Project

rspec-html

0.01
Low commit activity in last 3 years
A long-lived project that still receives updates
HTML document abstraction and matchers for RSpec
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 1.10
~> 3.0
 Project Readme

RSpec::HTML

RSpec::HTML provides a simple object interface to HTML responses from RSpec Rails request specs.

Installation

Add the gem to your Gemfile:

gem 'rspec-html', '~> 0.3.0'

And rebuild your bundle:

$ bundle install

Usage

Require the gem in your spec_helper.rb:

# spec/spec_helper.rb
require 'rspec/html'

Several matchers are provided to identify text and HTML elements within the DOM. These matchers can only be used with the provided object interface.

Browser Inspection

To open the current document in your operating system's default browser, call document.open. Use this to inspect HTML content while debugging.

it 'has complex HTML' do
  get '/my/path'
  document.open
end

Alternatively document.html_path writes the current document to a temporary file and returns its path.

Object Interface

The top-level object document is available in all tests which reflects the current response body (e.g. in request specs).

If you need to parse HTML manually you can use the provided parse_html helper and then access document as normal:

let(:document) { parse_html('<html><body>hello</body></html>') }

it 'says hello' do
  expect(document.body).to match_text 'hello'
end

This method can also be used for ActionMailer emails:

let(:document) { parse_html(ActionMailer::Base.deliveries.last.body.decoded) }

Changed in 0.3.0: parse_html no longer assigns document automatically, you must use a let block to assign it yourself.

To navigate the DOM by a sequence of tag names use chained method calls on the document object:

Tag Traversal

expect(document.body.div.span).to match_text 'some text'

Attribute Matching

To select an element matching certain attributes pass a hash to any of the chained methods:

expect(document.body.div(id: 'my-div').span(align: 'left')).to match_text 'some text'

Special attributes like checked can be found using the #attributes method:

expect(document.input(type: 'checkbox', name: 'my-name')).attributes).to include 'checked'

Class Matching

CSS classes are treated as a special case: to select an element matching a set of classes pass the class parameter:

expect(document.body.div(id: 'my-div').span(class: 'my-class')).to match_text 'some text'
expect(document.body.div(id: 'my-div').span(class: 'my-class my-other-class')).to match_text 'some text'

Classes can be provided in any order, i.e. 'my-class my-other-class' is equivalent to 'my-other-class my-class'.

Simple CSS Matching

To use a simple CSS selector when no other attributes are needed, pass a string to the tag method:

expect(document.body.div('#my-id.my-class1.my-class2')).to match_text 'some text'

This is effectively shorthand for:

expect(document.body.div(id: 'my-id', class: 'my-class1 my-class2')).to match_text 'some text'

Counting Matchers

Use once, twice, times, at_least, and at_most to match element counts:

expect(document.div('.my-class')).to match_text('some text').once
expect(document.div('.my-class')).to match_text('some other text').twice
expect(document.div('.my-class')).to match_text('some').times(3)
expect(document.div('.my-class')).to match_text('some').at_least(:twice)
expect(document.div('.my-class')).to match_text('some').at_least.times(3)
expect(document.div('.my-class')).to match_text('some text').at_most.once
expect(document.div('.my-class')).to match_text('some other text').at_most.twice
expect(document.div('.my-class')).to match_text('text').at_most.times(3)

Text Matching

To select an element that includes a given text string (i.e. excluding mark-up) use the text option:

expect(document.body.div(text: 'some text').input[:value]).to eql 'some-value'

Attribute Retrieval

To select an attribute from an element use the hash-style interface:

expect(document.body.div.span[:class]).to match_text 'my-class'
expect(document.body.div.span['data-content']).to match_text 'my content'

Retrieve all matching elements

To select all matching elements as an array, use #all:

expect(document.span.all.map(&:text)).to eql ['foo', 'bar', 'baz']

Indexing a Matching Set

To select an index from a set of matched elements use the array-style interface (the first matching element is 1, not 0):

expect(document.body.div[1].span[1][:class]).to match_text 'my-class'

Alternatively, use #first, #last or, when using ActiveSupport, #second, #third, etc. are also available:

expect(document.body.div.first.span.last[:class]).to match_text 'my-class'

Element Existence

To test if a matching element was found use the exist matcher:

expect(document.body.div[1]).to exist
expect(document.body.div[4]).to_not exist

Length of matched attributes

To test the length of matched elements use the #size or #length method:

expect(document.body.div.size).to eql 3
expect(document.body.div.length).to eql 3

XPath / CSS Selectors

If you need something more specific you can always use the Nokogiri #xpath and #css methods on any element:

expect(document.body.xpath('//span[@class="my-class"]')).to match_text 'some text'
expect(document.body.css('span.my-class')).to match_text 'some text'

To simply check that an XPath or CSS selector exists use have_xpath and have_css:

expect(document.body).to have_css 'html body div.myclass'
expect(document.body).to have_xpath '//html/body/div[@class="myclass"]'

Checkboxes

Use be_checked to test if a checkbox is checked:

expect(document.input(type: 'checkbox')).to be_checked

Custom Matchers

Changed in 0.3.0: The contain_text matcher has been removed. Use match_text instead.

match_text

Use the match_text matcher to locate text within a DOM element. All mark-up elements are stripped when using this matcher.

This matcher receives either a string or a regular expression.

expect(document.body.form).to match_text 'Please enter your password'
expect(document.body.form).to match_text /Please enter your [a-z]+/

contain_tag

Use the contain_tag matcher to locate DOM elements within any given element. This matcher accepts two arguments:

  • The tag name of the element you want to match (e.g. :div);
  • (Optional) A hash of options. All options supported by the object interface can be used here.

Without options:

expect(document.div(class: 'my-class')).to contain_tag :span

With options:

expect(document.form(class: 'my-form')).to contain_tag :input, name: 'email', class: 'email-input'

Contributing

Feel free to make a pull request.

License

MIT License