Check that an element does not exist *after* a button is clicked
With ruby, I'm doing some cucumber tests with capybara to test a Shiny application (
shinyis a R package to do web applications). But I'm a newbie with ruby and cucumber (and this is my first post here).
I have a scenario like this:
Given a user uses the application When he sets the x-axis limits "10,20,30" And he clicks on the changeaxis button Then the graph has no error
Currently, I expect that this test should fail, i.e. there should be an error on the graph.
The application displays a graph in a
graph. When the user sets the axis limits "10,20,30" and clicks on the "changeaxis" button, there is an error in the application: Shiny assigns the class
divcontaining the graph. Then my above scenario tries to check that the
graphcontaining the class
errordoes not exist:
Then (/^the graph has no error$/) do @screen.graph_page.check_graph_has_no_error end def check_graph_has_no_error check_element_is_not_present GRAPH_ERROR end GRAPH_ERROR = "//div[(@id='graph') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' error '))]" def check_element_is_not_present xpath should have_no_selector(:xpath, xpath) end
The test does not fail. That's because things go too fast: the class
erroris assigned on the element
only after the user clicks the button.
I have a workaround using
Given a user uses the application When he sets the x-axis limits "10,20,30" And he clicks on the changeaxis button And he waits for 5 seconds Then the graph has no error And (/^he waits for (\d+) seconds?/) do |n| sleep(n.to_i) end
Here the test fails, as expected.
But is there a way without using
This is the problem with negative assertions since Capybara has no way to know how long any actions are going to take on the page, so really can't know that even though the assertion is passing now you really want it to wait longer to make sure it keeps passing. What you really need is a positive assertion before the negative assertion to assure the page is in the correct state. What that would be depends on exactly what happens when you click the button, but would have to be something that definitely will change on the page and also indicates the error class would have been set on the graph by then if it's going to be set. If there is nothing like that available then you have run into the one place it actually makes sense to use Capybaras predicate methods while testing. You can do whatever the equivalent in your page object model is of
expect(page.has_css?('#div.error', wait: 3)).to eq false
This works because it will wait for the entire 3 seconds, if necessary, looking for the div with class 'error' before returning. Adjust the
3as necessary for certainty that your action will have completed but you're not wasting too much extra time.
Note: You should be preferring CSS over XPath when possible. It's faster, easier to read, easier to write correctly, and easier to maintain. In your use of XPath you are breaking scoping of elements (probably/usually unintentionally) by starting your XPath with
.//- see https://github.com/teamcapybara/capybara#beware-the-xpath--trap