Is it possible to test the order of elements via RSpec/Capybara?
Solution 1
I resolved this issue by testing for a regex match against the body content of the page. A bit kludgy, but it works.
page.body.should =~ /ITEM1.*ITEM2.*ITEM3/
Solution 2
I found a more canonical way of testing this behaviour with CSS. You could user :first-child
, :last-child
and :nth-child(n)
selectors in whichever assert you like.
In your example I'd try these assertions:
page.should have_tag("ul:last-child", :text => "ITEM #1")
pseuo_add_new_li
page.should have_tag("ul:nth-last-child(2)", :text => "ITEM #1")
page.should have_tag("ul:last-child", :text => "ITEM #2")
I hope this helps someone. Read more about this.
Solution 3
this article lists several ways to test sort order in RSpec, the best of which seems to be this matcher:
RSpec::Matchers.define :appear_before do |later_content|
match do |earlier_content|
page.body.index(earlier_content) < page.body.index(later_content)
end
end
Solution 4
I have had the same issue recently and found this neat & ideal solution: http://launchware.com/articles/acceptance-testing-asserting-sort-order
It's even packaged as a tiny gem.
Solution 5
You can use the all finder method to select multiple elements and then use collect to pull out the text into an array:
assert_equal page.all('#navigation ul li').collect(&:text), ['Item 1', 'Item 2', 'Item 3']
If your list isn't visible on the page such as a popup navigation menu, you need to pass visible: false
into the all
method.
Related videos on Youtube
John
Updated on June 07, 2022Comments
-
John almost 2 years
I'm using RSpec/Capybara as my test suite. I have some javascript that dynamically appends
<li>
to the end of a<ul>
. I want to write a request spec to ensure that this is happening.I tried using the
has_css
Capybara method and advanced CSS selectors to test for the ordering of the<li>
elements, but Capybara doesn't support the+
CSS selector.Example:
page.should have_css('li:contains("ITEM #1")') pseuo_add_new_li page.should have_css('li:contains("ITEM #1")+li:contains("ITEM #2")')
Does anyone know of another way to test for ordering?
-
dgilperez over 11 years
nth-last-child
selects the nth child from the bottom. quirksmode.org/css/nthlastchild.html -
JoaoHornburg about 11 yearsUsing Webrat I get "undefined local variable or method `page'"
-
Finn MacCool about 11 years@JoaoHornburg sorry, i can't help there. i've only used capybara so far.
-
JoaoHornburg about 11 yearsI've made it work with webrat. Already forked the project on github, will release my code when I have some time to change the docs and publish the gem
-
Daniel Wright almost 11 yearsI don't know why you got a downvote, but you're perfectly correct about Capybara (well, Nokogiri) supporting the
+
selector. My suspicion is the newli
was not being added adjacent toITEM #1
, and he should have used the~
selector instead. -
Drew Verlee almost 10 yearsHow is cucumber better at testing html?
-
Marnen Laibow-Koser almost 10 years@DrewV Cucumber is better at doing assertions about user-facing content in user-facing terms. RSpec is at the wrong level of abstraction for testing UI.
-
user664833 over 8 yearsAnd with plain Minitest:
assert page.body =~ /ITEM1.*ITEM2.*ITEM3/
-
lightsaber almost 7 yearsI'm using this, and it works. I want to check order of items within a particular div, what should do?
-
samjewell about 6 yearsThanks - that article really helped me (we're using Minitest, not RSpec)
-
dani24 almost 6 yearsYou could also check for the end of a tag:
expect(page) =~ >ITEM1<.*>ITEM2<.*>ITEM3<
-
soupdog about 4 yearsIn the OP's case I think this is a flaky test waiting to happen. Mapping the specific elements if at least one already exists on the page prior to the javascript that adds a new one will immediately return only those elements that are currently on the page, which may not include the new one if the javascript takes too long. Better to be explicit and use positional pseudo-class selectors as other answers have suggested.
-
Ben almost 4 yearsThank you, very elegant solution. I ended up using
expect(response.body).to match(/ITEM1.*ITEM2.*ITEM3/m)
with RSpec 4 and multi-line match -
alexventuraio over 3 yearsThe article is really good for further information.
-
Duderino9000 over 2 yearsCan someone explain what
ITEM
represents in these examples? Are those class names? -
John over 2 years@Barryman9000 It was the actual text in each of the
<li>