Increase poltergeist timeout for a specific capybara click_button call - ruby-on-rails

I have a rails project, which I'm testing with rspec/capybara/poltergeist/phantomjs. I know I can increase the general poltergeist timeout with the general settings
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, timeout: 2.minutes)
end
But is there a way to increase the timeout for a specific request?
I have a page with a button (id=submit) which kicks off a longish (90-120 seconds) running process, before returning. I'm working on optimizing the back end to shorten the request time, but in the meanwhile, I want to increase the timeout for that specific request when testing, so something along the lines of
click_button 'submit', wait: 180

You can do
Capybara.using_wait_time(180) do
click_button 'submit'
end
Another thing you can do is
# capybara.rb
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, timeout: 30)
end
Capybara.register_driver :poltergeist_long do |app|
Capybara::Poltergeist::Driver.new(app, timeout: 180)
end
# wherever.rb
session = Capybara::Session.new(:poltergeist_long)
session.visit("http://thatlongwaittime.com")

Timeuouts for specific requests can be increased by increasing the value of default wait time which is generally configured in you env.rb file.
To understand this well lets take below mention code:
Cucumber file:
When Joe is on abc page
Then Joe clicks submit button
Step definition for clicking submit button:
Then(/^Then Joe clicks submit button$/) do
Capybara.default_wait_time = 120 // increasing the default wait time to 180 seconds
click_button('submit') // performing the action
Capybara.default_wait_time = DEFAULT_WAIT_TIME // reset the wait time to its default value after clicking submit button.
end
Note: The value of DEFAULT_WAIT_TIME can be configured in env.rb file
Hope this helps :)

Related

Why does an `ajax:success' event not fire in Rails system test?

I have a system test that fails because the proper ajax:success event is not being fired in the test environment. The event is correctly fired when testing manually in the browser.
Here's the link the user (and the test) clicks:
link_to 'Note', note_product_option_path(#product, option), remote: true, data: { note_link: true }
The JS (CoffeeScript) event, which takes the response and inserts into the body of the document:
NOTE_LINK = '[data-note-link="true"]'
$ ->
$('body').on 'ajax:success', NOTE_LINK, (e) ->
alert 'ajax:success'
$('.option-note').remove()
data = e.detail[2].response
$div = $('<div></div>').html(data)
.addClass('option-note')
$('body').append($div)
Here's the system testing setup:
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
include Devise::Test::IntegrationHelpers
Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('headless')
options.add_argument('window-size=1480x1680')
Capybara::Selenium::Driver.new(app, :browser => :chrome, :options => options)
end
end
In my system test:
visit product_path(option.product)
click_link 'Note'
assert_selector '.option-note', wait: 5
That assertion always fails, even though it always works when I test in the browser. The alert is never caught or fired by Capybara either, but always appears when manually checking. The test log shows that the proper JS is being rendered by the controller.
What am I missing here?
Check the browser console for any other JS errors and fix them. The big difference between the development and test environments is that the JS assets get concatenated into one file in the test environment which means an error in one JS can prevent JS from other files being run. In the development mode that doesn’t happen because each file is loaded separately so an error can only affect other code in the same file

capybara fill_in sometimes doesn't fill in all the characters

About 3 in 10 times, this spec will fail because fill_in fails to fill in all of the characters of the user[username] field.
uuid = SecureRandom.uuid
fill_in "user[username]", :with => uuid
Notes:
Sometimes it succeeds
Sometimes it drops a random number of characters, usually somewhere after index 29
never fails locally on OSX
failures happen remotely on Solano aka tddium
Does anyone know what is going on?
This is my configuration:
capybara (2.4.4)
selenium-webdriver (3.0.0)
ChromeDriver 2.25.426924 (649f9b868f6783ec9de71c123212b908bf3b232e) (very recent)
using Chrome, not headless:
And in spec_helper:
Capybara.register_driver :selenium do |app|
http_client = Selenium::WebDriver::Remote::Http::Default.new
http_client.timeout = 100
Capybara::Selenium::Driver.new(app, :browser => :chrome, :http_client => http_client)
end
#thomas-walpole probably is pointing out the root cause - a change in chromedriver that was not compensated for by a change in capybara as we had pegged that gems version number at 2.4.4.
I upgraded capybara to the latest version, 2.10.1, fixed all the necessary deprecations, and retested.
Somewhere in there, it seems to work now. At least, where as I used to get 3 failures in 10 tries, now it works consistently for at least 20 repetitions.

Capybara does not wait for icons to load

I am running a Ruby on Rails integration test in Capybara. It is currently failing because a panel is overlapping a button that I need to click on. So, I am trying to click on an icon with id #click-to-close which will close the panel, and then click on the button under it. However, the close icon is not appearing when I run the Capybara test.
Here is the button in question - it uses the font awesome double-angle (>>) icon.
<div class="fa fa-angle-double-right fa-lg right-sidebar-show close-button" id="click-to-close"></div>
Here is the part of the Capybara test I am working with:
page.accept_confirm do
sleep 5.seconds #wait for help dash to appear
page.save_screenshot "XPath.png"
page.find("#click-to-close").click
sleep 5.seconds #wait for help dash to disappear
click_on "Remove", wait: 25, match: :first
end
Here is a screenshot of the panel at the time the test is being executed.
Here is a screenshot of how the panel should look with the close icon.
I was running into this issue:
https://github.com/thoughtbot/capybara-webkit/issues/808
https://github.com/thoughtbot/capybara-webkit/issues/728
The optimal solution is to make the entire top bar of the dash clickable, and have the Capybara automated test search for the ID of the top bar and click it. Not enough information was given in the question, but let's say the ID was #top-bar. You would do:
page.accept_confirm do
sleep 5.seconds #wait for help dash to appear
page.find("#top-bar").click
click_on "Remove", wait: 25, match: :first
end

Poltergeist/Capybara test unable to find CSS intermittently

I'm using Capybara and Poltergeist and cannot for the life of me get all my tests to consistently pass. I have this one issue in particular with a date selector. It should be really simple - user clicks on input, out puts a selection of months (first image). A month is then clicked, and then a day selection appears (second image), on which a day of the month is selected.
Now, my code looks as follows:
all(:css, 'input.from_date').last.click
expect(page).to have_css(".datepicker-months")
within(:css, '.datepicker-months') { find('.month', :text => 'Jun', match: :first).click }
expect(page).not_to have_css(".datepicker-months")
expect(page).to have_css(".datepicker-days")
within(:css, '.datepicker-days') { find('.day', :text => work[:start_date].stamp('31').to_i.to_s, match: :first).click } #.to_i.to_s used to remove leading zeros
page.assert_no_selector('.datepicker-days')
Sometimes it passes, but most of the time it says:
expected to find css ".datepicker-days" but there were no matches
or
expected not to find css ".datepicker-months", found 1 match: "« 2015 » JanFebMarAprMayJunJulAugSepOctNovDec"
If I try and debug this by calling binding.pry, I can run the commands step by step in the console and it works perfectly. My timeout is set to more than enough (i think). Any ideas why this test fails intermittently?
My config:
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 60
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {
timeout: 60,
js_errors: false,
phantomjs_logger: File.open("log/phantomjs.log", "a")})
end
UPDATE:
When adding sleep(0.5) after every step in the process, it passes each and every time. This is bad practice and I'm supposed to be able to write tests without doing this. :/
Whenever I run into these inconsistencies, I try to find other things to assert against so that you don't have to count on sleep.
If there are any sort of animations that start/stop, you can try to assert against those.
You can also try to be more specific in your current expectation, since it seems like you had no trouble clicking on the object you found
expect(page).not_to have_css('.datepicker-months .month', :text => 'Jun')
If any of that gets you any farther let us know.

With Capybara, how do I switch to the new window for links with "_blank" targets?

Perhaps this isn't actually the issue I'm experiencing, but it seems that when I "click_link" a link with target="_blank", the session keeps the focus on the current window.
So I either want to be able to switch to the new window, or to ignore the _blank attribute - essentially, I just want it to actually go to the page indicated by the link so I can make sure it's the right page.
I use the webkit and selenium drivers.
I submitted my findings thus far below. A more thorough answer is much appreciated.
Also, this only works with selenium - the equivalent for the webkit driver (or pointing out where I could discover it myself) would be much appreciated.
Capybara >= 2.3 includes the new window management API. It can be used like:
new_window = window_opened_by { click_link 'Something' }
within_window new_window do
# code
end
This solution only works for the Selenium driver
All open windows are stores in Selenium's
response.driver.browser.window_handles
Which seems to be an array. The last item is always the window that was most recently opened, meaning you can do the following to switch to it.
Within a block:
new_window=page.driver.browser.window_handles.last
page.within_window new_window do
#code
end
Simply refocus for current session:
session.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
Referenced on the capybara issues page: https://github.com/jnicklas/capybara/issues/173
More details on Selenium's window switching capabilities: http://qastuffs.blogspot.com/2010/10/testing-pop-up-windows-using-selenium.html
This is now working with Poltergeist. Although window_handles is still not implemented (you need a window name, i.e. via a JavaScript popup):
within_window 'other_window' do
current_url.should match /example.com/
end
Edit: Per comment below, Poltergeist now implements window_handles since version 1.4.0.
Capybara provides some methods to ease finding and switching windows:
facebook_window = window_opened_by do
click_button 'Like'
end
within_window facebook_window do
find('#login_email').set('a#example.com')
find('#login_password').set('qwerty')
click_button 'Submit'
end
More details here: Capybara documentation
I know this is old post, but for what its worth in capybara 2.4.4
within_window(switch_to_window(windows.last)) do
# in my case assert redirected url from a prior click action
expect(current_url).to eq(redirect['url'])
end
Seems like it is not possible with capybara-webkit right now: https://github.com/thoughtbot/capybara-webkit/issues/271
:-(
At the same time https://github.com/thoughtbot/capybara-webkit/issues/129 claims it is possible to switch windows with within_window.
Also https://github.com/thoughtbot/capybara-webkit/issues/47 suggests that page.driver.browser.switch_to().window(page.driver.browser.window_handles.last) works. Ah well, on to code reading.
The code at https://github.com/thoughtbot/capybara-webkit/blob/master/lib/capybara/webkit/browser.rb at least has some references that suggest that the API that works for webdriver / firefox is also working for webkit.
Now within_window implemented for capybara-webkit http://github.com/thoughtbot/capybara-webkit/pull/314 and here you can see how to use it http://github.com/mhoran/capybara-webkit-demo
As of May 2014 the following code works on capybara-webkit
within_window(page.driver.browser.window_handles.last) do
expect(current_url).to eq('http://www.example.com/')
end
To explicitly change window, you use switch_to_window
def terms_of_use
terms_window = window_opened_by do
click_link(#terms_link)
end
switch_to_window(terms_window)
end
An after that method browser will work in the new page, instead of wrap everything in a within_window block
This works for me in capybara-webkit:
within_window(windows.last) do
# code here
end
(I'm using capybara 2.4.1 and capybara-webkit 1.3.0)
You can pass a name, url or title of the window also
(But now its dipricated)
let(:product) { create :product }
it 'tests' do
visit products_path
click_link(product.id)
within_window(product_path(product)) do
expect(page).to have_content(product.title)
end
end
You can pass a labda or a proc also
within_window(->{ page.title == 'Page title' }) do
click_button 'Submit'
end
wish it stretches the method usage to more clearly understaing
I had this issue when opening links in an gmail window: I fixed it like this:
Given /^(?:|I )click the "([^"]*)" link in email message$/ do |field|
# var alllinks = document.getElementsByTagName("a");
# for (alllinksi=0; alllinksi<alllinks.length; alllinksi++) {
# alllinks[alllinksi].removeAttribute("target");
# }
page.execute_script('var alllinks = document.getElementsByTagName("a"); for (alllinksi=0; alllinksi<alllinks.length; alllinksi++) { alllinks[alllinksi].removeAttribute("target"); }')
within(:css, "div.msg") do
click_link link_text
end
end
The best idea is to update capybara to the latests version (2.4.1) and just use
windows.last
because page.driver.browser.window_handles is deprecated.
The main implementation (window_opened_by) raises an error for me:
*** Capybara::WindowError Exception: block passed to #window_opened_by opened 0 windows instead of 1
So, I resolve it by this solution:
new_window = open_new_window
within_window new_window do
visit(click_link 'Something')
end
page.driver.browser.window_handles
# => ["CDwindow-F7EF6D3C12B68D6B6A3DFC69C2790718", "CDwindow-9A026DEC65C3C031AF7D2BA12F28ADC7"]
I personally like the following approach since it works correctly regardless of JS being enabled or not.
My Spec:
it "shows as expected" do
visit my_path
# ...test my non-JS stuff in the current tab
switch_to_new_tab
# ...test my non-JS stuff in the new tab
# ...keep switching to new tabs as much as necessary
end
# OR
it "shows as expected", js: true do
visit my_path
# ...test my non-JS stuff in the current tab
# ...also test my JS stuff in the current tab
switch_to_new_tab
# ...test my non-JS stuff in the new tab
# ...also test my JS stuff in the new tab
# ...keep switching to new tabs as much as necessary
end
Test helpers:
def switch_to_new_tab
current_browser = page.driver.browser
if js_enabled?
current_browser.switch_to.window(current_browser.window_handles.last)
else
visit current_browser.last_request.fullpath
end
end
def js_enabled?
Capybara.current_driver == Capybara.javascript_driver
end

Resources