Testing javascript on subdomain with capybara, phantomjs and rails - ruby-on-rails

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.

Related

How do I set the port correctly in email links during a system test?

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.

Spree integration test login fail

I am trying to create test to admin panel. But it fails while the program try to log_in.
Failures:
1) Visit products login works correctly
Failure/Error: expect(page).to have_content("Logged in successfully")
expected to find text "Logged in successfully" in "Login\nAll departments\nHome\nCart: (Empty)\n \nInvalid email or
password.\nLogin as Existing Customer\nRemember me\nor Create a new
account | Forgot Password?"
# ./spec/features/home_spec.rb:14:in `block (2 levels) in '
The password, and the email are correct for admin. I found solutions in other posts, like adding configuration to capybara, but it still fails.
spec_helper
require 'capybara/rspec'
require 'rails_helper'
require 'spree/testing_support/controller_requests'
require 'capybara/rails'
Capybara.app_host = "http://localhost:3000"
Capybara.server_host = "localhost"
Capybara.server_port = "3000"
_spec.rb
require "spec_helper"
RSpec.describe 'Visit products' do
it 'login works correctly' do
visit spree.admin_path
fill_in "spree_user[email]", with: "piotr.wydrzycki#yahoo.com"
fill_in "spree_user[password]", with: "password"
click_button Spree.t(:login)
expect(page).to have_content("Logged in successfully")
end
end
Since the page is showing "Invalid email or password" either the email or password aren't correct, or the user for the test isn't being created correctly. Since you don't show the creation of any test users in your test it's most likely there aren't any. When running in test mode the app doesn't use your development database, it has its own database and you need to create all the objects (like users) you expect to exist for the test. You can do this by using fixtures or something like factory_bot to create users before each test.
Additionally, there should be no need to set server_host,server_port, or app_host in your situation.

Cannot remove port from Capybara while visiting external link

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

Can't make sense of capybara behaviour

I have this integration test for my Rails App:
require 'test_helper'
class StudyCapybaraTest < ActionDispatch::IntegrationTest
def setup
#user = users(:archer)
#vocabs = #user.vocabs
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
Capybara.current_driver = :selenium_chrome
#Capybara.default_wait_time = 5
visit login_path
fill_in "session_email", with: #user.email
fill_in "session_password", with: 'password'
click_button "session_commit"
end
test "full study process" do
assert_title "Home | Word Up"
visit study_user_path(#user)
....
end
end
Weirdly, when I remove the first line of the first test "full study process"
assert_title "Home | Word Up"
the test fails because the test user doesn't seem to be logged in. The same problem occurs when I move
visit study_user_path(#user)
into the setup function (as it was before).
But that doesn't change anything about the sequence and logic, right?
The only thing I can think of, is that the assertion comes to early, and the
app doesn't have time to execute the instructions needed to meet the assertions.
Is this a timing issue, and if so, how can I prevent them from happening in the future? Thx!
First, your intuition on it being a timing issue is correct. click_button does just that - it clicks the button. It doesn't wait for a form to post, it doesn't wait for any ajax to occur, etc. So without the assert_title your test is clicking the button, and immediately changing the url in the browser. The changing the url in the browser would have the effect of canceling any form submission or behavior that was triggered by the click_button call. You need to wait after the click_button for something that changes on the page as a result of clicking the button, along the lines of
assert_text('You are now logged in')
Secondly, the setup method is run before each test so you really don't want to be registering the driver in there, since it only needs to be done once.

Why does an rspec feature spec with javascript not do test teardown?

I have several non-javascript specs that use the ui to create and edit records.
When I run these specs the test database records are automatically removed for me by the rspec teardown for each test.
However the test below which is the first one to have :js => true for some ajax stuff isn't doing tear down of the records afterwards and then tests start to break because the database is no longer empty correctly when they start. The link and the group rows still exist in the test database.
# spec/features/verifying_link_spec.rb
require 'spec_helper'
describe "verification", :js => true, :type => :feature do
before :all do
User.create(:username => 'r#google.com', :password => 'esceptio')
end
before :each do
visit '/ladmin/login'
fill_in 'username', :with => 'r#google.com'
fill_in 'password', :with => 'esceptio'
find('input[value="Login"]').click
end
it "lets me verify a link" do
find('div#side div a', text: 'New Group').click
fill_in 'group[group_name]', with: 'Group Add'
click_button 'Save'
find('div#side div a', text: 'New Link').click
fill_in 'link[url_address]', with: 'http://www.a.com/newtest9876link'
fill_in 'link[alt_text]', with: 'abcd9876'
click_button 'Save'
this_year=Time.now.strftime('%Y')
l=Link.first
l.update_attribute(:verified_date, nil)
expect(Link.count).to eq 1
visit links_path
find('a', text: "verify")
click_link("verify", match: :first)
sleep(3)
expect(page).to have_content(this_year)
end
end
Right now I am using a workaround solution of using the ui to delete the records (below) but this should not be necessary
# added at bottom of spec
click_link('Details')
click_link('Delete')
page.driver.browser.switch_to.alert.accept
click_link('Groups')
click_link('Delete')
page.driver.browser.switch_to.alert.accept
None of my other unit tests or feature tests (except this one with js) have this problem. They all create records that get removed automatically.
I highly recommend using the database_cleaner gem to clean your database out between tests. Rspec wraps everything in a transaction and rolling it back once the example is finished. However, when you start using javascript you might be saving the data outside of the rspec transaction and then the database never reverts to its original state.
Truncation is slower than the transaction strategy. However you may only need the truncation strategy with JS tests. You could follow this guide to setup the database cleaner gem in that manner: http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/
If you find your tests taking eons, you might want to investigate some time looking at a gem that preloads your environment. I highly recommend zeus

Resources