Tibor Nagy
Tibor Nagy

Reputation: 1236

Capybara doesn't wait to complete the submit action on click_button "Save"

I have the following rspec fragment:

   describe "Save should create a ClassificationScheme" do
      subject { lambda { click_button "Save"; sleep 1 } }
      it { should change(ClassificationScheme, :count).by(1) 
   end

Without the "sleep 1" capybara doesn't wait for the action fired by the save button and the spec fails. With the sleep 1 is OK, but is there any better solution?

Note, that this test is running in Firefox using selenium webdriver.

My versions:

Upvotes: 1

Views: 2377

Answers (2)

Thomas Walpole
Thomas Walpole

Reputation: 49870

When you click something using Capybara there is no guarantee any actions triggered by that click have completed when the method returns. This is because Capybara knows nothing about what the browser is doing other than that it clicked an element on the screen. Instead of sleeping you need to check for something that visually changes on the page to indicate the action triggered by clicking the button has completed. That may be a message stating the save happened successfully or an element disappearing, etc. Something along the lines of

describe "Save should create a ClassificationScheme" do
  subject { lambda { click_button "Save"; page.should have_text('Classification Saved' } }
  it { should change(ClassificationScheme, :count).by(1) 
end

Note: you should also update Capybara - 2.4.4 was released in October of 2014, there have been a lot of improvements since then.

Upvotes: 2

Jim Stewart
Jim Stewart

Reputation: 17323

You didn't include code for your submit action, but if there's anything asynchronous going on, like an Ajax request, the submit action itself will finish quickly, while the async task is still processing the request. If that's the case, you can use a helper like this:

# spec/support/wait_for_ajax.rb
module WaitForAjax
  def wait_for_ajax
    Timeout.timeout(Capybara.default_max_wait_time) do
      loop until finished_all_ajax_requests?
    end
  end

  def finished_all_ajax_requests?
    page.evaluate_script('jQuery.active').zero?
  end
end

RSpec.configure do |config|
  config.include WaitForAjax, type: :feature
end

Code courtesy Thoughtbot.

Note that this only includes the helper in feature specs; so either tag your specs with type: :feature, or change the config.include line above to include it in whatever spec type you're using.

To use it:

describe "Save should create a ClassificationScheme" do
    subject { lambda { click_button "Save"; wait_for_ajax } }
    it { should change(ClassificationScheme, :count).by(1) 
end

Upvotes: 0

Related Questions