whenever I tell capybara to click on a link it immediately terminates the session I am in and brings me to the login screen. Obviously when browsing normal everything works fine.
this is my rspec:
require 'rails_helper'
require 'webdrivers'
RSpec.describe "Searches", driver: :selenium_firefox, js: true, type: :system do
before do
driven_by(:rack_test)
user = FactoryBot.create(:user)
login_as(user, :scope => :user)
driven_by :selenium_firefox
end
it "populate movie form" do
visit search_path
find('#home_path').click
sleep(3)
end
end
in spec_helper.rb I have set this:
Capybara.app_host = "http://localhost:3002"
Capybara.server_host = "localhost"
Capybara.server_port = "3002"
and in rails_helper. rb: config.use_transactional_fixtures = false
I have also gotten the href through capybara which is correct http://localhost:3002/search
Specifying driven_by twice in your before block makes no sense. Each driver uses its own session so it's no surprise you're having session issues. Get rid of both driven_by calls in the before block and see what happens.
There's also no need to set app_host if you're letting Capybara run the application under test, which it appears you are since you're setting server_host and server_port.
Related
A Rails app that uses remote testing to test a CakePHP app running on a Vagrant Ubuntu VM.
My OS is macOS High Sierra.
'rspec/rails'
'capybara/rails'
'capybara/mechanize'
'capybara/poltergeist'
'phantomjs'
If I run rspec ./spec/features/my_tests_folder/,
the first 2 tests in the folder always pass and the rest always end up with Capybara::Poltergeist::TimeoutError:.
If I run any of the tests in that folder individually, they ALL pass ALWAYS.
There are 7 test files total. They each have 1 feature with 1 scenario. All are js: true.
I have tried increasing :timeout in Capybara.register_driver and increasing default_max_wait_time in Capybara.configure. Neither changed the outcome.
I've also played around with Capybara.reset! after and before each test. It didn't seem to matter, either.
When I ran this with config.order = :random, sometimes 5 out of 7 had the erros, sometimes only 2 out of 7. But, there were always some errors and some passing. Also, every test was in the errors group at least once.
I'm running out of ideas. What could cause something like this?
UPDATE (to include Capybara and Poltergeist configs and example of a failing test):
Configs in rails_helper.rb:
Capybara.register_driver :poltergeist do |app|
options = {
:timeout => 90, # default is 30
:js_errors => false,
:phantomjs => Phantomjs.path,
# :debug => true
}
Capybara::Poltergeist::Driver.new(app, options)
end
Capybara.register_driver :mechanize do |app|
driver = Capybara::Mechanize::Driver.new(app)
driver.configure do |agent|
agent.user_agent_alias = 'Mac Safari'
end
driver
end
Capybara.configure do |config|
config.run_server = false
config.app_host = "http://my_vm_domain.com"
config.javascript_driver = :poltergeist
config.default_driver = :mechanize
config.default_max_wait_time = 10 # default is 2
end
Example of failing test (not failing, but getting Capybara::Poltergeist::TimeoutError:):
require 'rails_helper'
feature 'agente visualiza estoques de um passeio', js: true do
scenario 'com sucesso' do
agente_log_in
visit '/roteiro/show/723'
find(:partial_href, 'new_passeio/723').click
select 'Passeio Teste', from: 'passeio_produto'
fill_in 'passeio_data', with: '11/11/2017'
within('button#estoque_controls_0') do
within('div#estoque_0_hora') do
expect(page).to have_content('07:30')
end
within('span#estoque_0_vagas') do
expect(page).to have_content('3')
end
end
within('button#estoque_controls_1') do
within('div#estoque_1_hora') do
expect(page).to have_content('10:00')
end
within('span#estoque_1_vagas') do
expect(page).to have_content('5')
end
end
end
end
Code from agente_log_in.rb in support folder:
def agente_log_in
Capybara.app_host = "http://my_vm_domain.com"
visit '/usuario/logout'
visit '/'
fill_in 'data[AdmUsuario][usuario]', with: 'agente'
fill_in 'data[AdmUsuario][senha]', with: 'pa$$w0rd'
click_on 'Entrar'
end
Code for that :partial_href find:
module Selectors
Capybara.add_selector(:partial_href) do
xpath {|href| XPath.descendant[XPath.attr(:href).contains(href)] }
end
end
Everything is fine with the other tests that are in the app's other folders. They're also fine if I run the tests in this folder individually. The problem only seems to happen when I run THIS specific folder as a whole.
After including the extra information requested by Thomas Walpole, I continued searching and studying possibilities.
I eventually came across poltergeist's issue #781 on GitHub, which describes a situation very similar to mine, and eventually presents a wait_for_ajax solution.
Before implementing it in my project, I read more about waiting for Ajax and found this post very helpful, too.
In the end, I chose jasonfb's code from GitHub because it seemed more thorough and informative.
It worked like a charm! My tests now pass consistently. I was even able to remove customization for :timeout and default_max_wait_time.
The CakePHP app in question is very js heavy and the specific part that this folder tests is particularly full of Ajax requests.
I'm attempting to set up system tests with Capybara and Selenium on an existing Rails 5.1 (Upgraded from Rails 4) app that already had capybara based feature tests. Here's what I've done so far.
In the gem file under group :development, :test:
gem 'chromedriver-helper'
gem 'selenium-webdriver'
gem 'rack_session_access'
In the environments/development.rb and environments/test.rb:
config.action_mailer.default_url_options = { host: 'localhost:3000' }
In the spec\rails_helper.rb:
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, browser: :chrome)
end
Capybara.configure do |config|
config.default_max_wait_time = 10 # seconds
config.default_driver = :selenium
config.app_host = 'http://localhost:3000'
config.server_host = 'localhost'
end
The issues I'm having are both with new systems tests and old feature tests.
With the system tests it appears that Capybara isn't creating a page object as I get undefined local variable or method 'page' Additionally when I duplicate the same test under the feature test directory I don't have this issue.
With the old Capybara feature tests, working with the rackTest driver, a Chrome window opens but I get No route matches [GET] "/rack_session/edit"
config.middleware.use RackSessionAccess::Middleware is already present in the environments/test.rb
Example system test:
require 'rails_helper'
describe User do
let(:user) { create :user }
let(:membership) { create :membership, admin: true}
let(:admin) { create :user, memberships: [membership] }
context 'viewing the index' do
it 'directs you to the appropriate page' do
set_current_user(admin)
visit root_url
click_button 'Manage Users'
expect(page.current_url).to end_with users_path
expect(page).to have_selector 'h1', text: 'Users'
end
end
end
Example feature test:
require 'rails_helper'
describe 'edit an assignment' do
let(:roster) { create :roster }
let(:user) { create :user, rosters: [roster] }
before :each do
Timecop.freeze Date.new(2018, 1, 10)
set_current_user(user)
end
after :each do
Timecop.return
end
context 'returns the user to the appropriate index page' do
let(:date_today) { Date.new(2017, 4, 4) }
let(:start_date) { Date.new(2017, 3, 31) }
let(:month_date) { date_today.beginning_of_month }
it 'redirects to the correct URL' do
visit roster_assignments_url(roster, date: date_today)
visit new_roster_assignment_url(roster, date: start_date)
click_button 'Create'
expect(current_url)
.to eq roster_assignments_url(roster,
date: month_date)
end
In the spec_helper:
def set_current_user(user)
page.set_rack_session user_id: user.id
end
You need to have the gem puma installed. New rails 5 projects have it installed by default, but your app was made in Rails 4, which is why it didn't.
Why is this? Well, if you were to do a bundle update (and I'll admit I can't explain why) you'd get this error when trying to run the specs, which is a lot more explanatory:
System test integration requires Rails >= 5.1 and has a hard dependency on a webserver and `capybara`, please add capybara to your Gemfile and configure a webserver (e.g. `Capybara.server = :webrick`) before attempting to use system tests.
Googling this error led me to this page, which explains Capybara needs a server.
After adding puma, I'm able to run system tests on your application.
A number of issues here.
Why are you setting - config.app_host = 'http://localhost:3000' ??? That would run the tests against your dev instance rather than the test instance Capybara starts. app_host really should only ever need to be set if you are doing subdomain based testing. This could be the reason for the no route error (normally rack_session_access would only be included in the test environment), or that could be caused by having not actually included the middleware as specified in the rack_session_access gem readme.
NEVER do expectations against current_path/current_url directly, instead use the provided matchers or you'll have flaky tests
expect(page).to have_current_path(users_path)
page is just an alias for Capybara.current_session and is a member of the module Capybara::DSL. If it's not available in the scope of your tests that it most likely means Capybara::DSL isn't included. That would normally be done by rspec-rails - https://github.com/rspec/rspec-rails/blob/master/lib/rspec/rails/vendor/capybara.rb#L21 - so it's possible you haven't actually set the test type to 'system'. If it's that it's not available in your spec_helper methods, just using Capybara.current_session instead is usually easier.
Whenever I visit some url from my application then after visiting twitter goes to "twitter:3000"
Now I want to remove :3000 so that in my test case I can visit external link successfully. I have tried several options but its not working. I am using Capybara in Rails
Here is test case
test 'z' do
visit companies_url
login_twitter
end
def login_twitter
Capybara.run_server = false
Capybara.server_port = ''
Capybara.server_host = 'https://twitter.com'
visit 'https://twitter.com' #here it goes to **https://twitter.com:3000**
fill_in 'signin-email', with: 'email#email.com'
fill_in 'signin-password', 'pass'
page.find('a', :text => /\ALog in\z/).click
end
application_system_test_case.rb
Capybara.server_port = 3000
Capybara.default_max_wait_time = 10
Selenium::WebDriver::Chrome.driver_path = '/home/chromedriver'
driven_by :selenium
It looks like you're using Rails 5.1 system tests, where they've chosen to enable Capybara.always_include_port = true which tells Capybara to insert the port it's running the app on in every url visited (unless a non-default port is explicitly specified). To work around this and directly visit an external site you could do
def login_twitter
Capybara.always_include_port = false
visit 'https://twitter.com' #here it goes to **https://twitter.com:3000**
fill_in 'signin-email', with: 'email#email.com'
fill_in 'signin-password', 'pass'
page.find('a', :text => /\ALog in\z/).click
Capybara.always_include_port = true
end
Additionally I'm not sure why you're setting Capybara.server_port = 3000, that's generally a bad idea because it prevents you from having your dev instance running while running tests. It's generally better not to specify a specific server_port (let Capybara use a random port) unless you have a genuine need for it (firewall/routing issues).
I have a rich frontend in my application. Some of my tests not works well with poltergeist, because of animations and AJAX requests, but works fine with selenium.
How can i use them together in one project and in one test session?
If you're using the standard RSpec configuration with Capybara (require 'capybara/rspec') then you can override the normal driver that would be used for a given test with :driver metadata
it "should do something", driver: :selenium do
# will use the selenium driver for this test
end
it "should do something else", driver: :poltergeist do
# will use the poltergeist driver for this test
end
that could also be specified on the enclosing feature if you want the whole feature to use a specific driver
feature "blah balh", driver: :selenium do
# all scenarios here would use the selenium driver unless overridden with their own :driver metadata
I found solution.
Created macros in spec/support/selenium_macros.rb:
module SeleniumMacros
def use_selenium_webdriver
before(:all) do
Capybara.javascript_driver = :selenium
Capybara.current_driver = :selenium
end
after(:all) do
Capybara.current_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
end
end
end
spec/rails_helper.rb
RSpec.configure do |config|
config.extend SeleniumMacros, type: :feature # add macros for acceptance tests
using example
spec/features/example_feature_spec.rb
feature 'Add files to question' do
use_selenium_webdriver
this feature will be work with selenium, after it will be executed it activates poltergeist webdriver.
P.S. Sorry for my english.
My setup is using poltergeist as the Capybara driver for all my tests, both JS and non-JS.
# spec/rails_helper.rb
require "capybara/poltergeist"
# ...
# ...
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: true)
end
Capybara.configure do |config|
config.ignore_hidden_elements = true
Capybara.default_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
end
I have some tests where I confirm that certain features on my app are still working even with javascript disabled. For those tests, I of course disable javascript with js: false.
describe "accessibility" do
describe "JavaScript disabled", js: false do
before(:each) { visit root_path }
it "user can still log in" do
# ...
end
end
end
However, I'm noticing that these js:false tests still use JavaScript. I can confirm this by printing debug statements to the console log in JavaScript.
Is there a way to disable JavaScript when using poltergeist? Or is it always enabled? Is it even valid to use poltergeist as a non-JS driver?
Thanks!
No, there doesn't seem to be a way to use poltergeist without Javascript (unless you modify poltergeist yourself). According to this Github issue it would require support in phantomjs, which is available in a patch but not in master.