RSpec matcher to compare two HTML fragments

Updated . Posted . Visible to the public.

The RSpec matcher tests if two HTML fragments are equivalent. Equivalency means:

  • Whitespace is ignored
  • Types of attribute quotes are irrelevant
  • Attribute order is irrelevant
  • Comments are ignored

You use it like this:

html = ...
expect(html).to match_html(<<~HTML)
  <p>
    Expected content
  </p>  
HTML

You may override options from CompareXML Show archive.org snapshot by passing keyword arguments after the HTML string:

html = ...
expect(html).to match_html(<<~HTML, ignore_text_nodes: true)
  <p>
    Expected content
  </p>  
HTML

Installing the matcher

The matcher requires these two gems in your Gemfile:

gem 'nokogiri'
gem 'compare-xml'

Here is the matcher:

require 'nokogiri'
require 'compare-xml'

RSpec::Matchers.define :match_html do |expected_html, **options|
  match do |actual_html|
    expected_doc = Nokogiri::HTML5.fragment(expected_html)
    actual_doc = Nokogiri::HTML5.fragment(actual_html)

    # Options documented here: https://github.com/vkononov/compare-xml
    default_options = {
      collapse_whitespace: true,
      ignore_attr_order: true,
      ignore_comments: true,
    }

    options = default_options.merge(options).merge(verbose: true)

    diff = CompareXML.equivalent?(expected_doc, actual_doc, **options)
    diff.blank?
  end

end

Alternatives

To use Capybara matchers like have_css on an HTML fragment, use Capybara.string(html) Show archive.org snapshot .

Profile picture of Henning Koch
Henning Koch
Last edit
Florian Leinsinger
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2021-09-29 08:53)