A weird Capybara error in `within` block

At Nebulab we are encouraged in spending Fridays on open source projects, and I simply love it.
Solidus eCommerce ecosystem and its extensions is where I usually hang out, you never get bored with so many different projects and things to do.
A few Fridays ago I started working on a flaky spec that failed often on Solidus CI builds. This is pretty annoying as flaky failures mean the test suite must be run again with the usual consequences of uncertainty of results, wasted time, longer queues, wasted CPU time, CO2 buildup, seven years of famine and hordes of locusts (not necessarily in this order).
Most of the times flaky specs happen on Selenium tests, where JS interactions
may take a bit more time to complete and some parts of the HTML may not be yet
present or visible (want another example from Solidus codebase?
Here you are). But this time it was different: the spec
failed with Rack::Test
, which is usually very deterministic by nature, no
javascript interaction was involved, so I was quite puzzled about it.
The error
This is the relevant code of the tentative spec:
it "can sort desc" do
within_table(table_id) do
click_link sort_link, exact: false
expect(page).to have_selector '.sort_link.desc'
end
end
It basically clicks on the sorting triangle icon visible in this screenshot:
and checks that the triangle sorting direction has changed (this happens with a page refresh, no JS involved):
Quite surprisingly, this spec fails with the current version of Capybara (3.13.2):
expected to find css ".sort_link.desc" within #<Capybara::Node::Element tag="table" path="/html/body/div[5]/div/div/fieldset/table"> but there were no matches
If you change Capybara driver to Firefox or Chrome, for example by changing the first line to:
it "can sort desc”, :js do
then the spec passes without errors, so it must be an issue with Rack::Test
.
After digging a bit inside Capybara source code I confirmed that what happens
here is that the Capybara node that represents the HTML of the selected table
is not refreshed after the page reload, so changes that happen within that
block are not visible.
The fix
There’s an easy workaround that solves this issue: you just need to close the
within
block after clicking the link and reopening it before checking the
page, actively forcing Capybara to reload the HTML block from the table:
it "can sort desc" do
within_table(table_id) do
click_link sort_link, exact: false
end
within_table(table_id) do
expect(page).to have_selector '.sort_link.desc'
end
end
OSS Collaboration
I won’t claim to be the first man on earth that encountered this issue, but I think it’s quite rare as nobody here at Nebulab had seen this before, and also a few internet searches did not bring any results to me. This bug is barely worth noticing, the fix is very simple and makes perfect sense: if you need the node to be updated, just close and reopen the block.
Still, I decided it was worth digging a bit deeper and notify the Capybara team about the issue. After a few days they fixed it.
I think this is a good example of OSS cooperation between different projects, this kind of interactions are good for the community as they lead to improvements that will benefit everybody.
Years ago I was amazed by the simple truth of this quote from (or maybe not?) George Bernard Shaw:
If you have an apple and I have an apple and we exchange these apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
I think that software is more like ideas than apples, as it’s much easier to do good to others while still pursuing your (selfish) interest: if I share my apple with you, we both have only half an apple. But if I write some software and share it with you, then we both can benefit from the code.
Happy coding!