Poltergeist/Capybara test unable to find CSS intermittently - ruby-on-rails

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.

Related

RSpec record created seemingly before(:suite) how do I track this record down?

Our test suite has a problem.
Whenever the whole suite starts up, a stray record is created.
Whenever I run a single file or context of specs, this stray record does not get created.
I wrote config.before(:each) { puts "COUNT = #{Model.count}\n" } in spec_helper.rb and no matter which order the tests ran, this record persists somehow. Seemingly before the examples even started.
We seed our database, so I tried a clean setup.
RAILS_ENV=test rake db:setup
RAILS_ENV=test rake db:seed
echo "Model.count" | rails c test
=> 0
Then whenever I run all tests (checked the order and checked for before(:all)).
rspec
COUNT = 1
.
COUNT = 1
.
COUNT = 1
.
etc
I've meticulously examined spec_helper.rb (RSpec2, pre rails_helper.rb) and commented out every support file with no luck.
I'm suspecting the code base at this point, maybe some weird call to something where someone left behind a find_or_create, a weird callback or something, I have no idea.
What I would like to know is:
How do I tackle breaking down the suites startup?
Can I get a backtrace of my test suite just starting up?
Anyone successfully chased down this kind of persistent record in RSpec?
EDIT
I added config.before(:suite) { debugger } and the record is still created before this!
How can I break down this code even further?
[23, 32] in /Users/yourname/.rvm/gems/ruby-1.9.3-p327#projectname/gems/rspec-core-2.14.6/lib/rspec/core/command_line.rb
23 #world.announce_filters
24
25 #configuration.reporter.report(#world.example_count, #configuration.randomize? ? #configuration.seed : nil) do |reporter|
26 begin
27 #configuration.run_hook(:before, :suite)
=> 28 #world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : #configuration.failure_exit_code
29 ensure
30 #configuration.run_hook(:after, :suite)
31 end
32 end
(rdb:1) ModelName.count
1
For the record of how I found the persistent record:
By using before(:suite) { debugger }, I removed the record and ran the entire test suite and notice a test was failing.
Within this test, was something similar to the following:
context "blah" do
model = FactoryGirl.create(:model)
end
I simply put the code within a before(:each) block, and the test was passing and now so were mine.
TIL: Whenever RSpec loads the suite, it evaluates all the code that is not within a transaction. Which is why I could not try to pin point a problematic file by looking at when the record was being created.

Increase poltergeist timeout for a specific capybara click_button call

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 :)

How to Close a Browser with capybara?

In my code i am opening a browser as follow :-
Capybara.current_driver = :selenium
include Capybara::DSL
describe 'Auro' do
specify "OMX Manual Order" do
visit 'https://omx.ordermotion.com/en/console.asp'
end
How can i close this browser?
Have Tried following ,but no luck:-
Capybara.current_session.driver.reset!
page.execute_script "window.close();"
If you're only using selenium, then the following should work:
page.driver.quit
However, if you ever want to switch between different webdrivers, then you might want to add a condition or two. This is what I use:
page.driver.quit unless (Capybara.current_driver == :webkit || Capybara.current_driver == :sauce)
:webkit refers to the headless capybara-webkit and :sauce refers to Sauce Labs, but you can use that code for whichever webdrivers you want to use.
Hope that helps!
Try some of these
page.driver.browser.close
or
window = page.current_window
window.close
Keep in mind that if you have no other windows to switch to, an error will be raised
Try giving this a shot:
page.execute_script "window.close();"

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

How do I confirm a javascript popup with Capybara?

I've tried several examples found online, but with no luck. I am looking to confirm the confirm message of a delete link. The last attempt was the code below, but that resulted in an Capybara::NotSupportedByDriverError error.
def confirm_dialog
page.evaluate_script('window.confirm = function() { return true; }')
end
Adding an answer for those hitting this in 2016 and beyond. You can now use Capybara directly to accept a confirmation box. You do this by wrapping the code that causes the confirmation box to appear in the accept_confirm function.
accept_confirm do
click_link 'Destroy'
end
First of all switch to using Selenium as the driver by putting an #javascript tag in front of your scenario.
The following code in your cucumber step will then confirm the dialogue:
page.driver.browser.switch_to.alert.accept
# or
page.driver.browser.switch_to.alert.dismiss
# or
page.driver.browser.switch_to.alert.text
As #NobbZ said, this question has been asked and answered before here: How to test a confirm dialog with Cucumber?.
More selenium documentation available here too: http://code.google.com/p/selenium/wiki/RubyBindings#JavaScript_dialogs
for capybara-webkit:
page.driver.browser.accept_js_confirms
page.driver.browser.reject_js_confirms
which is still working, but the documentation says also:
page.driver.accept_js_confirms!
page.driver.accept_js_confirms!
See https://github.com/thoughtbot/capybara-webkit , search "accept_js_confirms"
I've had timing issues with browser dialogs in a CI environment so I'm polling for a dialog before accepting it:
def accept_browser_dialog
wait = Selenium::WebDriver::Wait.new(:timeout => 30)
wait.until {
begin
page.driver.browser.switch_to.alert
true
rescue Selenium::WebDriver::Error::NoAlertPresentError
false
end
}
page.driver.browser.switch_to.alert.accept
end
I had to use a sleep in the webkit test since it would fail everynow and then otherwise.
Here is what I came up with after reading everyones posts:
if page.driver.class == Capybara::Selenium::Driver
page.driver.browser.switch_to.alert.accept
elsif page.driver.class == Capybara::Webkit::Driver
sleep 1 # prevent test from failing by waiting for popup
page.driver.browser.accept_js_confirms
else
raise "Unsupported driver"
end
try to add :js => true to your test.
RSpec’s metadata feature can be used to switch to a different driver.
Use :js => true to switch to the javascript driver, or provide a
:driver option to switch to one specific driver. For example:
it 'will use the default js driver' :js => true do
...
end
In Capybara its very simple to accept the model window. Even we can do the same in selenium but its little tough for people who are not aware about selenium.
page.accept_modal #This will accept the modal window
page.dismiss_modal #This will Reject/Dismiss the modal window
I would guess that you have to add selenium to your gem-file and configure it and capybara that capybara uses selenium as the driver.
I think also that How to test a confirm dialog with Cucumber? is very similar to your question, especially the accepted answer.

Resources