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).
Related
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.
I'm writing a System test to confirm the entire sign up flow is working in a Rails 7 app (with the Clearance gem and an email confirmation SignInGuard).
The test is working fine right up until I "click" the confirm link in the email (after parsing it with Nokogiri). For some reason the URL in the email points to my dev server (port 3000) instead of pointing to the test server (port 49736, 49757, 49991, whatever).
I could look up the current port the test server is using (it changes every run) and replace the port portion of the URL but that seems quite hacky. Am I missing something obvious or doing something wrong?
URL in mailer: confirm_email_url(#user.email_confirmation_token)
Route from rails routes:
Prefix Verb URI Pattern Controller#Action
confirm_email GET /confirm_email/:token(.:format) email_confirmations#update
The system test so far:
require "application_system_test_case"
require "test_helper"
require "action_mailer/test_helper"
class UserSignUpFlowTest < ApplicationSystemTestCase
include ActionMailer::TestHelper
test "Sign up for a user account" do
time = Time.now
email = "test_user_#{time.to_s(:number)}#example.com"
password = "Password#{time.to_s(:number)}!"
# Sign up (sends confirmation email)
visit sign_up_url
fill_in "Email", with: email
fill_in "Password", with: password
assert_emails 1 do
click_on "Sign up"
sleep 1 # Not sure why this is required... Hotwire/Turbo glitch?
end
assert_selector "span", text: I18n.t("flashes.confirmation_pending")
# Confirm
last_email = ActionMailer::Base.deliveries.last
parsed_email = Nokogiri::HTML(last_email.body.decoded)
target_link = parsed_email.at("a:contains('#{I18n.t("clearance_mailer.confirm_email.link_text")}')")
visit target_link["href"]
# ^^^^^^^^^^^^^^^^^^^^^^^^^
# This is the bit that fails... The link in the email points to my dev server (port 3000) rather than
# the test server (port 49736, 49757, 49991, etc). I only figured this out when my dev server crashed
# and Selenium started choking on a "net::ERR_CONNECTION_REFUSED" error
assert_selector "span", text: I18n.t("flashes.email_confirmed")
end
end
Edit
For the time being I've worked around it by replacing visit target_link["href"] with visit hacky_way_to_fix_incorrect_port(target_link["href"]):
private
def hacky_way_to_fix_incorrect_port(url)
uri = URI(url)
return "#{root_url}#{uri.path}"
end
The URL used in mailers is specified by:
Rails.application.configure.action_mailer.default_url_options
In config/environments/test.rb I had set mine to port 3000 when I first installed Clearance:
config.action_mailer.default_url_options = {host: "localhost:3000"}
To fix it, I first tried specifying the port dynamically but the suggested method didn't actually work and it seems it isn't necessary. Removing the port number was enough to get my system test passing:
config.action_mailer.default_url_options = {host: "localhost"}
As mentioned by Thomas Walpole, the reason this works is that Capybara.always_include_port is set to true. With this setting, attempts to access http://localhost/confirm_email/<token> (with no port specified) are automatically rerouted to http://localhost:<port>/confirm_email/<token>.
The always_include_port setting defaults to false in the Capybara gem but it turns out Rails sets it to true when it starts up the System Test server.
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 got selenium remote configured and running. I can access my development server (deployed with 'rails s') on '192.168.1.23:3000' from my computer using test methods like 'visit root_path' on capybara with minitest.
However, when I close my development server, I can't access my test server while testing, I just get a chrome error with page not found
test file:
require "test_helper"
feature "dashboard" do
scenario "test1", :js => true do
#Rails.application.routes.default_url_options[:host]= 'http://192.168.1.23:3000'
visit root_path
end
Note: activating line 4 gives me nil when running visit root_path
test_helper.rb
Capybara.configure do |config|
config.default_host = 'http://192.168.1.23:3000'
config.app_host = 'http://192.168.1.23:3000'
end
I've also tried
test environment, test.rb
Rails.application.default_url_options = {:host => "http://192.168.1.23:3000" }
Rails.application.routes.default_url_options = {:host => "http://192.168.1.23:3000" }
Edit:
I have rails server configured to listen to external addresses:
boot.rb
#this lets listen to some interfaces,
#https://fullstacknotes.com/make-rails-4-2-listen-to-all-interface/
require 'rubygems'
require 'rails/commands/server'
module Rails
class Server
alias :default_options_bk :default_options
def default_options
default_options_bk.merge!(Host: '192.168.1.23')
end
end
end
By default Capybara runs the application on a random port, but you've set app_host to only connect on port 3000 (Which is the dev server default). Rather than setting a fixed port in app_host set
Capybara.always_include_port = true
Capybara.app_host = 'http://192.168.1.23`
Then if the random port assignment isn't working due to firewall issues between your selenium remote and local machine you can set
Capybara.server_port = <some fixed port number the test app will get run on>
Solution
This worked. The main essence is that I have to set the Capybara.server_port and Capybara.app_host and sign in manually in the sign in form. Capybara.app_host cannot be set with a dynamic subdomain unless its declared in a variable. All urls has to be hard coded.
require 'spec_helper'
feature 'customer' do
let(:user) {FactoryGirl.create(:user)}
let(:firm) {user.firm}
let(:customers) {"http://#{firm.subdomain}.lvh.me:31234/customers"}
let(:root_url) {"http://#{firm.subdomain}.lvh.me:31234/"}
before(:all) do
Capybara.server_port = 31234
sub = firm.subdomain
Capybara.app_host = root_url
end
def sign_in_on_js
visit root_url
fill_in "Email", :with => user.email
fill_in "Password", :with => "password"
click_button "Sign in"
page.should have_content("Signed in successfully.")
end
scenario "make new", js: true do
sign_in_on_js
visit customers
page.should have_content("Add new customer")
find("#dialog_customer").click
page.should have_content("Create new customer")
end
end
Original question
I am making a multitenant app in rails. There is going to be a lot of javascript. But, I cant get the testing to work.
When not running :js = true every thing works. The problem arises in specs like this one
let(:customers) {"http://#{firm.subdomain}.lvh.me:3003/customers"}
scenario "Statistics select", :js => true do
visit customers
page.should have_content("Add new customer")
end
The poltergeist web driver for capybara cannot find the url and returns a blank page
Failure/Error: page.should have_content("Add new customer")
expected there to be text "Add new customer" in ""
I have this in my spec_helper.rb
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, :debug => true)
end
Poltergeist and phantomjs try to deliver. I get this output
{"name"=>"set_debug", "args"=>[true]}
{"response"=>true}
{"name"=>"visit", "args"=>["http://subdomain2.lvh.me:3003/statistics"]}
poltergeist [1362522132943] state default -> loading
{"response"=>{"status"=>"fail"}}
Do I need to have a server running during testing to make this work?
I've tried selenium and capybara-webkit, but phantomjs has gotten closest to success.
I have also tried to change the hosts file in different ways( maybe not correct )
Any tips on setup are welcome!
Update
Starting to get desperate. I now start the rails server
rails s -e test -p 3001
and then run my tests.
Now I get redirected to the sign in page. I have this in the specs
before(:each) do
login_as(user, :scope => :user)
end
How can I sign in the test user on the rails test server without going trough the sign in process manually for every spec
Capybara already starts a server for you, to quote the docs:
Some Capybara drivers need to run against an actual HTTP server. Capybara takes care of this and starts one for you in the same process as your test, but on another thread. Selenium is one of those drivers, whereas RackTest is not.
Within your test you can use the visit method with a relative url, for example:
visit("/statistics")
Capybara will direct this request to the server it just started for this test.
When your want to use an absolute url within your test, you can, but you should also specify the port the server is running on. This port is being randomly chosen during the test. Some drivers have a method available to retrieve the port number.
For example when you use the Capybara-Webkit driver:
Capybara.current_session.driver.server_port
To visit an absolute url you can then use:
port_number = Capybara.current_session.driver.server_port
visit("http://127.0.0.1:#{port_number}/statistics")
Within the test specs probably a method login_as won't work. You have to log in with a few simple steps. For example:
before(:each) do
visit "/login"
fill_in "Email", :with => "my#email.com"
fill_in "Password", :with => "secret"
click_button "Login"
end
To test multiple subdomains you can set the Capybara.app_host. Take a look at this question for a detailed explanation.
UPDATE
Capybara 2 includes a nice feature called always_include_port which will automatically add the port number the server is running on.
Capybara.always_include_port = true
So instead of
visit("http://127.0.0.1:#{port_number}/statistics")
you can now use
visit("/statistics")
and it will automatically connect to http://127.0.0.1:#{port_number}/statistics.
If you want to test multiple subdomains with Capybara.app_host, you could use a domain name which always resolves to 127.0.0.1 for example lvh.me.
For example, if you specify Capybara.app_host = "http://example.lvh.me" it will run the tests using the example subdomain.