17

I seem to be having trouble testing the slick javascript things I do with jQuery when using Capybara and Selenium. The expected behavior is for a form to be dynamically generated when a user clicks on the link "add resource". Capybara will be able to click the link, but fails to recognize the new form elements (i.e. "resource[name]").

Is there a way to reload the DOM for Capybara, or is there some element of this gem that I just haven't learned of yet?

Thanks in advance!

==Edit==

Currently trying my luck with selenium's:

wait_for_element 

method.

==Edit==

I keep getting an "undefined method 'wait_for_element` for nill class" when attempting to do the following:

@selenium.wait_for_element

It appears that that specific method, or perhaps wait_for with a huge selector accessing the DOM element I expect is the correct course of action, but now trying to get the selenium session is starting to be a huge headache.

kelly.dunn
  • 1,546
  • 3
  • 16
  • 23

3 Answers3

17

I use the Webdriver based driver for Capybara in RSpec, which I configure and use like this and it will definitely handle JS and doesn't need a reload of the dom. The key is using a wait_until and a condition that will be true when your AJAX response has finished.

before(:each) do
  select_driver(example)  
  logout
  login('databanks') 
end

 def select_driver(example)
   if example.metadata[:js]
    Capybara.current_driver = :selenium
  else
    Capybara.use_default_driver
  end
end

it "should let me delete a scenario", :js=>true do
    select("Mysite Search", :from=>'scenario_id')  
    wait_until{ page.has_content?('mysite_searchterms')}    
    click_on "delete"     
    wait_until{ !page.has_content?('mysite_searchterms')}   
    visit '/databanks'
    page.should_not have_content('Mysite Search')
  end

I also figured out a hack to slow down webdriver last night, like this, if you want to watch things in slo-mo:

   #set a command delay
   require 'selenium-webdriver'

   module ::Selenium::WebDriver::Remote
     class Bridge
       def execute(*args)
         res = raw_execute(*args)['value']
         sleep 0.5
         res
       end
     end
   end  

As someone else mentioned, if you are getting a timeout waiting for the element, you could look at upping this:

Capybara.default_wait_time = 10
ebeland
  • 1,543
  • 10
  • 21
  • thanks @ebeland. now after many months of using something similar in our tests at work, the `wait_until` method is without a doubt the correct way to go :) – kelly.dunn Jun 25 '11 at 01:17
  • 4
    `wait_until` has been removed from Capybara 2.0.0. [See this blog post](http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara) – David Tuite Nov 20 '13 at 12:06
7

From the Capybara docs:

When working with asynchronous JavaScript, you might come across situations where you are attempting to interact with an element which is not yet present on the page. Capybara automatically deals with this by waiting for elements to appear on the page.

You might have some luck increasing the wait time:

Capybara.default_wait_time = 10

If that doesn't help then I would encorage you to contact somebody from the project on GitHub, write to the mailing list or submit an issue report.

thomasfedb
  • 5,990
  • 2
  • 37
  • 65
  • 3
    +1 here. Capybara uses this default wait time for method calls such as `has_selector?` `has_css?` and so on. So you can use these methods to wait for the element to appear, using something like: `has_css?("a#myinput", :visible => true)` which will wait until the element is on the screen before proceeding. – bergyman Mar 18 '11 at 18:20
  • where should I put `Capybara.default_wait_time = 10`? – Incerteza Mar 03 '15 at 13:06
  • 1
    @AlexanderSupertramp in your `spec/rails_helper.rb` if you have one, otherwise your `spec/spec_helper.rb`. – thomasfedb Mar 03 '15 at 13:51
  • What does Cucumber have to do with the directory of RSpec which is /spec? – Incerteza Mar 03 '15 at 14:26
  • @AlexanderSupertramp Nothing! This question is about using Capybara with RSpec, not about Cucumber. – thomasfedb Mar 04 '15 at 14:48
0

Even wait_until deleted from Capybara 2.0. Still that's useful and grab code from below:

 def wait_until(delay = 1)   
 seconds_waited = 0   
 while ! yield && seconds_waited < Capybara.default_wait_time     
    sleep delay     
    seconds_waited += 1   
  end   
  raise "Waited for #{Capybara.default_wait_time} seconds but condition did not become true" unless yield 
end
ASM
  • 709
  • 2
  • 8
  • 27