lib/hexp/nokogiri/equality.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Hexp
  module Nokogiri
    # Used in test to see if two Nokogiri objects have the same content,
    # i.e. are equivalent as far as we are concerned
    #
    class Equality
      CLASSES = [
        ::Nokogiri::HTML::Document,
        ::Nokogiri::HTML::DocumentFragment,
        ::Nokogiri::XML::Document,
        ::Nokogiri::XML::Node,
        ::Nokogiri::XML::Text,
        ::Nokogiri::XML::Element,
        ::Nokogiri::XML::DocumentFragment,
        ::Nokogiri::XML::DTD,
      ]

      # Create a new equality tester for two Nokogiri objects
      #
      # (see Hexp::Nokogiri::Equality::CLASSES for all possible objects that can
      # be passed in)
      #
      # @example
      #   doc = Nokogiri::HTML::Document.new
      #   this_node = Nokogiri::XML::Element.new("div", doc)
      #   that_node = Nokogiri::XML::Element.new("span", doc)
      #   Hexp::Nokogiri::Equality.new(this_node, that_node).call #=> false
      #
      # @param this [Object] The first object to compare
      # @param that [Object] The second object to compare
      #
      # @api public
      #
      def initialize(this, that)
        @this, @that = this, that
        [this, that].each do |input|
          raise "#{input.class} is not a Nokogiri element." unless CLASSES.include?(input.class)
        end
      end

      # Perform the comparison
      #
      # @return [Boolean]
      #
      # @api public
      #
      def call
        [ equal_class?,
          equal_name?,
          equal_children?,
          equal_attributes?,
          equal_text? ].all?
      end

      # Are the two elements instances of the same class
      #
      # @return [Boolean]
      #
      # @api public
      #
      def equal_class?
        @this.class == @that.class
      end

      # Do both elements have the same tag name
      #
      # @return [Boolean]
      #
      # @api public
      #
      def equal_name?
        @this.name == @that.name
      end

      # Do the elements under comparison have the same child elements
      #
      # @return [Boolean]
      #
      # @api public
      #
      def equal_children?
        return true unless @this.respond_to? :children
        @this.children.count == @that.children.count &&
          compare_children.all?
      end

      # Compare the child elements, assuming both elements respond_to? :children
      #
      # @return [Boolean]
      #
      # @api public
      #
      def compare_children
        @this.children.map.with_index do |child, idx|
          self.class.new(child, @that.children[idx]).call
        end
      end

      # Do the elements under comparison have the same attributes
      #
      # @return [Boolean]
      #
      # @api public
      #
      def equal_attributes?
        return true unless @this.respond_to? :attributes
        @this.attributes.keys.all? do |key|
          @this[key] == @that[key]
        end
      end

      # Compare the text of text elements
      #
      # If the elements are not of type Nokogiri::XML::Text, return true
      #
      # @return [Boolean]
      #
      # @api public
      #
      def equal_text?
        return true unless @this.instance_of?(::Nokogiri::XML::Text)
        @this.text == @that.text
      end
    end
  end
end