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 .

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)