I have a select2 v4 that loads options through AJAX.
I am running a Cucumber test where I need to select 2 options of the list, but I can't seem to make the list open up and load (which normally gets populated when I type 2 or characters).
I have tried:
As suggested here:
#session.execute_script("$('#publish_to').select2('open')")
and
#session.first(".input.publish_to .select2-container").click
and
#session.first("#publish_to").find(".select2-choice").click
which do not give me an error, but I am not getting the options to select, so I am assuming that the click is not really working. Things I have tried to select the options:
# This one cannot find the css:
#session.find(".select2-results__options", text: client.email).click
# This one gives me a Timeout error
#session.evaluate_script "$('#publish_to').val(#{client.id}).trigger('change')"
# This one gives me a Timeout error
#session.evaluate_script "$('.select2-search__field').trigger('keydown').val('#{client.email}').trigger('keyup')";
sleep 10
#session.find('.select2-search__option', text: client.email).click
Anything with trigger gives me a Timeout error, so I tried waiting for jQuery.active but I never got a true even waiting for 2 minutes:
counter = 0
timeout_in_sec = 120
while counter < timeout_in_sec && #session.evaluate_script('jQuery.active').zero?
sleep 1.second
counter+=1
end
I tried using the gem capybara-select2 running:
#session.select2 client.email, css: '#publish_to', search: true
but I get the error undefined methodselect2' for #and I haveWorld(CapybaraSelect2)in myenv.rb`
I am using Cucumber v3.1.2 with ruby gem 'cucumber-rails'
The poltergeist driver is roughly equivalent to a 7 year old version of Safari which means it doesn't support a lot of current JS/CSS. This means your issue could simply be that select2 is no longer compatible with Poltergeist (without a lot of polyfilling). You're going to be much better off updating to using a real browser (stable - chrome via selenium, etc) or one of the direct to Chrome drivers (highly beta) that have spun off Poltergeist (Apparition is one of them). Those will allow you to run with a visible browser (useful for debugging) or headless.
The following code uses Chrome via selenium and interacts with the select2 demo site to select an entry that is loaded via Ajax.
require "selenium/webdriver"
require "capybara/dsl"
sess = Capybara::Session.new(:selenium_chrome)
sess.visit("https://select2.org/data-sources/ajax")
sess.first('.select2-container', minimum: 1).click
sess.find('.select2-dropdown input.select2-search__field').send_keys("capy")
sleep 5 # just to watch the browser search
sess.find('.select2-results__option', text: 'teamcapybara/capybara').click
sess.assert_selector(:css, '.select2-selection__rendered', text: 'teamcapybara/capybara')
sleep 5 # just to see the effect
Related
Specs: Rails 5.2, Ruby 2.5.1, Yarn 1.91.1, React-Rails 2.6.0, selenium-webdriver 3.14, chromedriver 77, capybara gem 3.29 , Ubuntu 18.04
Issue: I created a questionnaire using react components in a Rails app which works when run in development mode. I can click the yes or no button then a callback function renders the new question text. However my selenium integration tests don't pick this up. The page object continues to have the same text for the first question so I receive an error like this
Failure/Error: expect(question.text).to include('last question')
expected "first question" to include "last question"
The test itself looks like this in features/questionnaire.rb
RSpec.feature "Onboarding Questionnaire", type: :feature, js:true do
(...)
it...
question = find('h3.question')
expect(question.text).to include('first question')
yes = find('#yes')
yes.click
sleep 5
question = find('h3.question')
expect(question.text).to include('last question')
The problem arises after yes.click. I thought at first my animations were causing the issue, but I removed those and it is just using setState in my callback function.
Here is my react callback function
saveAnswer(e) {
questionnaire = this
++questionnaire.state.page
questionnaire.setState({page: questionnaire.state.page})
}
Here is my driver config
Capybara.javascript_driver = :selenium_chrome_headless
Now using sleep works as desired. Here are the changes I made.
1) I removed the deprecated gem chromedriver-helper and replaced it with webdrivers
2) Added require 'webdrivers' to the rails_helper.rb file.
3) Found error in my react code (which wasn't showing up in development or being logged in chrome webdriver) of questionnaire = this which I changed to var questionnaire = this.
Integration tests now pass fine using selenium_chrome_headless
I'm noticing a strange issue that I haven't been able to solve for a few days.
I have a Rails 5 API server with system tests using RSpec and Capybara + Selenium-webdriver driving headless Chrome.
I'm using Capybara.app_host = 'http://localhost:4200' to make the tests hit a separate development server which is running an Ember front-end. The Ember front-end looks at the user agent to know to then send requests to the Rails API test database.
All the tests run fine except for ones which use RSpec file fixtures.
Here's one spec that is failing:
describe 'the affiliate program', :vcr, type: :system do
fixtures :all
before do
Capybara.session_name = :affiliate
visit('/')
signup_and_verify_email(signup_intent: :seller)
visit_affiliate_settings
end
it 'can use the affiliate page' do
affiliate_token = page.text[/Your affiliate token is \b(.+?)\b/i, 1]
expect(affiliate_token).to be_present
# When a referral signs up.
Capybara.session_name = :referral
visit("?client=#{affiliate_token}")
signup_and_verify_email(signup_intent: :member)
refresh
# It can track the referral.
Capybara.session_name = :affiliate
refresh
expect(page).to have_selector('.referral-row', count: 1)
# When a referral makes a purchase.
Capybara.session_name = :referral
find('[href="/videos"]').click
find('.price-area .coin-usd-amount', match: :first).click
find('.cart-dropdown-body .checkout-button').click
find('.checkout-button').click
wait_for { find('.countdown-timer') }
order = Order.last
order.force_complete_payment!
Rake::Task['affiliate_referral:update_amounts_earned'].invoke
# It can track the earnings.
Capybara.session_name = :affiliate
refresh
amount = (order.price * AffiliateReferral::COMMISSION_PERCENTAGE).floor.to_f
amount_in_dom = find('.referral-amount-earned', match: :first).text.gsub(/[^\d\.]/, '').to_f * 100
expect(amount).to equal(amount_in_dom)
end
end
This will fail maybe 99% of the time. There is the odd case where it passes. I can get my test suite to eventually pass by running it on a loop for a day.
I ended up upgrading all versions to the latest (Node 10, latest Ember, latest Rails) but the issue persists.
I can post a sample repo that reproduces the issue later. I just wanted to get this posted in case anyone has encountered the issue.
Here's a typical stack trace when the timeout happens:
1.1) Failure/Error: page.evaluate_script('window.location.reload()')
Net::ReadTimeout:
Net::ReadTimeout
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:97:in `block in request'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:110:in `block in request'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:109:in `request'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:121:in `response_for'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:76:in `request'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/common.rb:62:in `call'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/bridge.rb:164:in `execute'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:584:in `execute'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:267:in `execute_script'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/common/driver.rb:211:in `execute_script'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:84:in `execute_script'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:88:in `evaluate_script'
# /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/session.rb:575:in `evaluate_script'
# ./spec/support/selenium.rb:48:in `refresh'
# ./spec/support/pages.rb:70:in `signup_and_verify_email'
# ./spec/system/payment_spec.rb:43:in `block (3 levels) in <top (required)>'
I should point out it doesn't always happen with page.evaluate_script('window.location.reload()'). It can happen with something benign like visit('/').
Edit: I tried disabling Ember FastBoot (server-side rendering) using the DISABLE_FASTBOOT env variable and suddenly all tests pass. I'm thinking that somehow the RSpec fixtures are causing Ember FastBoot to not finish rendering in some cases. This certainly lines up with dropped connections I've occasionally seen in production logs.
I've been experimenting with the client code and it may be due to my use of FastBoot's deferRendering call.
Edit: I'm using the following versions:
ember-cli: 3.1.3
ember-data: 3.0.2
rails: 5.2.1
rspec: 3.8.0
capybara: 3.8.2
selenium-webdriver: 3.14.0
google chrome: 69.0.3497.100 (Official Build) (64-bit)
Edit: I'm using this somewhat flaky Node/Express library fastboot-app-server to do server-side rendering. I've discovered that it sometimes strips important response headers (Content-Type and Content-Encoding). I'm wondering if this is contributing to the issue.
Edit: I added a strict Content Security Policy to make sure there are no external requests running during the test suite that could be causing the Net::ReadTimeout.
I inspect the Chrome network tab at the point when it locks up and it seems to be loading nothing. Manually refreshing the browser allows the tests to pick up and continue running. How strange.
I've spent a couple weeks on this now and it may be time to give up on Selenium tests.
I upgraded to Chrome 70 and chromedriver 2.43. It didn't seem to make a difference.
I tried using the rspec-retry gem to force a refresh when the timeout occurs but the gem seems to fail to catch the timeout exception.
I've inspected the raw request to chromedriver where things hang. It looks like it's always POST http://127.0.0.1/session/<session id>/refresh. I tried refreshing in an alternate way: visit(page.current_path) which seems to fix things!
I finally got my test suite to pass by switching page.driver.browser.navigate.refresh to visit(page.current_path).
I know it's an ugly hack but it's the only thing I could find to get things working (see my various attempts in the question edits).
I looked at the request to chromedriver that was causing the timeouts each time: POST http://127.0.0.1/session/<session id>/refresh. I can only guess that it's some kind of issue with chromedriver. Perhaps incidentally, it only hangs when multiple chromedriver instances are active (which happens when multiple Capybara sessions are being used).
Edit: I needed to account for query params as well:
def refresh
query = URI.parse(page.current_url).query
path = page.current_path
path += "?#{query}" if query.present?
visit(path)
end
I tried just doing visit(page.current_url) but that was giving timeouts as well.
I test following assumption And I click on the text "2018/2019" within ".year" with capybara/headless_chrome and constantly get the error
element not visible
(Session info: headless chrome=67.0.3396.87)
(Driver info: chromedriver=2.40.565386 (45a059dc425e08165f9a10324bd1380cc13ca363),platform=Mac OS X 10.13.5 x86_64) (Selenium::WebDriver::Error::ElementNotVisibleError)
I've already tried to adjust the window size, as suggested here. But it didn't work.
My step definition:
When(/^(?:|I )click on the text "([^"]*)"(?: within "([^"]*)")?$/) do |link, selector|
begin
page.find(:css, selector).click
end
end
The element is actually visible and found by Capybara
[1] pry(#<Cucumber::Rails::World>)> page.find(:css, ".year")
=> #<Capybara::Node::Element tag="a" path="/html/body/div[2]/div[2]/section/div[2]/div/div[1]/div[1]/div[2]/div/div[2]/ul/li/a">
But click fails
[2] pry(#<Cucumber::Rails::World>)> page.find(:css, ".year").click
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
Why doesn't click work here?
EDIT:
The HAML of the link is
%ul.facet_values
- unselected.each do |facet_value|
%li.filtered{data: {hayf: {text: facet_value.name.downcase}}}
= link_to facet_value.path, title: facet_value.name, class: 'year' do
=truncate("#{facet_value.name}", length: 24)
- if facet.has_counts?
%span.small="(#{facet_value.count})"
I tried headless testing with Poltergeist and got Poltergeist detected another element with CSS selector 'html body header div.container-fluid' at this position which was solved with .trigger('click') and the test passed.
Since this doesn't work in non-headless Chrome or Poltergeist, then the simplest answer is that the element isn't actually visible. Firstly remove the Capybara.ignore_hidden_elements = false setting since that makes no sense when testing an app, and will screw up all sorts of waiting behaviors. Secondly use save_and_open_screenshot to get a picture of what the page actually looks like before you are trying to click the link.
I'm guessing you'll see that the link isn't actually visible at that point and your test is missing a step a user would have to do to make the link visible, is it in a menu where you have to hover over a different element first? is it in a popup where you have to click on something else first?, does it need to be scrolled to? etc.
Finally, you'll generally have less issues if you stick with Chrome via Selenium. Poltergeist uses PhantomJS which hasn't been updated in quite some time and doesn't support modern JS or CSS which can lead to all sorts of strangeness.
[7] pry(#<RSpec::Core::ExampleGroup::Nested_1>)> page.execute_script "window.close()"
Selenium::WebDriver::Error::NoSuchWindowError: Script execution failed. Script: window.close();
The window could not be found
[8] pry(#<RSpec::Core::ExampleGroup::Nested_1>)> page.driver.browser.window_handles
=> ["f1-2"]
I had a browser open with two tabs, the above command does close one but the last tab never closes. It is open but when I try to run page.execute_script "window.close()" it gives the above error.
page.driver.browser.window_handles.each do |handle|
page.driver.browser.switch_to.window(handle)
page.execute_script "window.close()"
end
The above code was working for me sometime back but doesnt work anymore. It gives the same error.
UPDATE:
When I use,
page.driver.browser.window_handles.each do |handle|
page.driver.browser.switch_to.window(handle)
page.driver.browser.close
end
it gives the following error Selenium::WebDriver::Error::UnknownError: 'auto_id' does not refer to an open tab
Two ways you can do it
In line with your technique using JS. You would first need to switch back to your first browser window (window_handle) and then perform "window.close()". (Not Preferred) (Not sure why its not working now for you, did you upgrade server version or different browser?)
Simply use #driver.quit (Preferred)
Update
Just write this once. This will close all windows.
after(:each) do
#driver.quit
end
If you want to close only one browser tab/window/popup, switch to that window_handle and then perform
#driver.close();
page.driver.browser.close closes current tab towards the end and the last (second) tab closes itself after each example.
in case if you are using cucumber, you can the use the BEFORE/AFTER hooks .please refer similar question on stackoverflow
for some more on cucumber,please refer this Cucumber Hooks
I'm using rack mini profiler in rails just fine, but during some coding sessions especially where I'm working on a lot of different client side code, it gets in the way. (mainly in my client side debugging tools network graphs, etc.)
I'm trying to turn it off with a before filter, that also serves to see if the user is authorized to see the profile anyway, but "deauthorize" doesn't seem to do anything for me. Here's my code called as a before filter:
def miniprofiler
off = true
if off || !current_user
Rack::MiniProfiler.deauthorize_request
return
elsif current_user.role_symbols.include?(:view_page_profiles)
Rack::MiniProfiler.authorize_request
return
end
Rack::MiniProfiler.deauthorize_request
end
I also know there is a setting "Rack::MiniProfiler.config.authorization_mode" but I can't find docs on what the possible settings are, and not seeing it used in the code? Right now its telling me :allow_all, but :allow_none doesn't do anything either.
Even if I can just temporarily set a value in the dev environment file and restart the server, that would serve my purposes.
Get latest and type:
http://mysite.com?pp=disable
When you are done type
http://mysite.com?pp=enable
See ?pp=help for all the options:
Append the following to your query string:
pp=help : display this screen
pp=env : display the rack environment
pp=skip : skip mini profiler for this request
pp=no-backtrace : don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
pp=normal-backtrace (*) : collect stack traces from all the SQL executed and filter normally
pp=full-backtrace : enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
pp=sample : sample stack traces and return a report isolating heavy usage (experimental works best with the stacktrace gem)
pp=disable : disable profiling for this session
pp=enable : enable profiling for this session (if previously disabled)
pp=profile-gc: perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
pp=profile-gc-time: perform built-in gc profiling on this request (ruby 1.9.3 only)
You can also use Alt + p to toggle on Windows/Linux and Option + p on MacOS.
If you want the profiler to be disabled initially, and then activate on demand... add a pre-authorize callback in an initializer file like:
Rack::MiniProfiler.config.pre_authorize_cb = lambda {|env| ENV['RACK_MINI_PROFILER'] == 'on'}
then in your application controller, add a before_filter that looks for the pp param
before_filter :activate_profiler
def activate_profiler
ENV['RACK_MINI_PROFILER'] = 'on' if params['pp']
ENV['RACK_MINI_PROFILER'] = 'off' if params['pp'] == 'disabled'
end
your environment will not have RACK_MINI_PROFILER set initially, but if you want to turn it on, you can tack ?pp=enabled onto your url. Then you can disable again later (the pp=disabled will only turn it off for the current session, but setting the ENV variable to off will kill it entirely until you force it back on).