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 (shiny is 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 div with id 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 error to the div containing the graph. Then my above scenario tries to check that the div with id graph containing the class error does 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 error is assigned on the element

    <div id="graph">
    

    only after the user clicks the button.

    I have a workaround using sleep:

    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 sleep?



  • 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 3 as 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 // rather than .// - see https://github.com/teamcapybara/capybara#beware-the-xpath--trap



Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2