How to make Capybara find an element that appears and disappears quickly (loading spinner)

Viewed 369

I have an application for which you can select a checkbox, click a submit button and then have a value update. In the real world the click triggers an API call and values are updated with the response. Between the time the API is called and a value returned, I also set the value-to-be-updated-area to be populated with a spinner (a loading spinner). This works great in real life usage. But, in test, I want to make sure that spinner shows up, disappears and then the new value is in place. In the test environment (using rspec) I have setup a sinatra fake service to respond to all API calls via webmock's stub_request.

So the problem is that sinatra is really quickly giving the value, so quick that capybara never sees the spinner. (I think that's the problem)

I tried artificially slowing down the sinatra response for these kinds of feature specs, but it didn't work. And besides I'd rather not because I want the suite to be fast. Here's a sample spec:

    it 'lets the user change the pickup library of a hold', js: true do
      visit holds_path
      page.check 'hold_list__3911148'
      page.select 'A terrific value', from: 'some_dropdown'
      page.click_button 'Update Selected Things'
      expect(page).to have_css 'spinner-border' # this will _always_ fail to be detected
      expect(page).to have_css '#hold3911148 .pickup_at', text: 'York'
    end
1 Answers

Tests and the app run asynchronously so if you really need to validate the spinner appears then your only option is to slow down the response for that test enough to let the spinner actually appear and be seen. Depending on which driver you're using and how the browser driver (chromedriver, geckodriver, etc) author have chosen to implement button clicks the amount of time you have to wait may be longer than you expect. This is because driver authors may opt to have button clicks wait for a bit to see if they trigger page loads, and Capybara can't start looking for the spinner until click_button returns.

I know you said that slowing down the response "didn't work" but without any details on exactly what you did there it's tough to diagnose that. This also assumes that all the browser calls are going to your app and your app is then making calls to the external service (which you're faking via your sinatra app) before returning (webmock can't be used to fake external API calls made directly from the browser)

Related