set Rails environment url for selenium remote - ruby-on-rails

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>

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.

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

How can I configure puma when running Capybara?

I'd like to adjust the puma configuration when running Capybara tests. Changing settings in .env, .env.test (I use dotenv), or config/puma.rb has no effect.
Where can I change the configuration?
Rails 5.1, poltergeist 1.15.0, capybara 2.14.0, puma 2.8.2
Generally with Capybara you configure the server in a register_server block. The :puma server definition Capybara provides is
Capybara.register_server :puma do |app, port, host|
require 'rack/handler/puma'
Rack::Handler::Puma.run(app, Host: host, Port: port, Threads: "0:4")
end
If you're using Rails 5.1 system testing it has added a layer of abstraction on top of that with server configuration being done in
ActionDispatch::SystemTesting::Server#register which is defined as
def register
Capybara.register_server :rails_puma do |app, port, host|
Rack::Handler::Puma.run(app, Port: port, Threads: "0:1")
end
end
Either way you should be able to either overwrite one of the current server registrations
Capybara.register_server :rails_puma do |app, port,host|
Rack::Handler::Puma.run(app, ...custom settings...)
end
or configure your own
Capybara.register_server :my_custom_puma do |app, port, host|
Rack::Handler::Puma.run(app, ...custom settings...)
end
Capybara.server = :my_custom_puma
In ApplicationSystemTestCase, you can configure by passing options to the default :puma server used by Rails in setup.
AFAIK, this will work for any options, but I've only used
Silent: true to silence the puma startup output
Thread: '1:1' to configure the puma process to only use one thread
Here's how I've setup up rails systems tests to run inside docker containers:
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400], options: { url: "http://selenium:4444/wd/hub" }
def setup
Capybara.server_host = '0.0.0.0' # bind to all interfaces
Capybara.server = :puma, { Silent: true, Threads: '1:1' }
host! "http://#{IPSocket.getaddress(Socket.gethostname)}"
super
end
end

Cucumber/Capybara tests returning Addressable::URI::InvalidURIError when I visit url

I am unable to visit the following url "http://localhost:7000/login/next="
In my page object i have set the following:
class LoginPage < SitePrism::Page
set_url "/login/next="
and I have set the following in my env.rb file
Capybara.app_host = "http://localhost:7000"
Capybara.server_host = "localhost"
Capybara.server_port = "7000"
and required the following:
require 'uri'
require 'net/http'
require 'open-uri'
When I run test it get the following
Cannot assemble URI string with ambiguous path: ':7000/login/next='(Addressable::URI::InvalidURIError)
Capybara doesn't like the port/host in the url. Any ideas who to get rounds this please?
You shouldn't set Capybara.app_host when you are running locally. From Capybara
Normally Capybara expects to be testing an in-process Rack
application, but you can also use it to talk to a web server running
anywhere on the internet, by setting app_host:
Capybara.current_driver = :selenium
Capybara.app_host = 'http://www.google.com'
...
visit('/')
You should be fine just setting the Capybara.server_port = 10000. Also I think the server_port expects a number not a string.

How to get Capybara/Selenium to play nicely with SSL in local environment

I'm using RSpec, Capybara and Selenium to test my local Rails application. Capybara by itself is working fine. My problem is that I use a forced SSL connection in most of my application.
The kind of workaround I have now is to configure Capybara for javascript testing like this:
Capybara.register_driver :selenium_profile do |app|
profile = Selenium::WebDriver::Firefox::Profile.new
profile.secure_ssl = false
profile.assume_untrusted_certificate_issuer = true
Capybara::Selenium::Driver.new(app, :browser => :firefox, :profile => profile)
end
Capybara.javascript_driver = :selenium_profile
Capybara.run_server = false
Capybara.server_port = 3001
Capybara.app_host = "https://localhost:%d" % Capybara.server_port
That only works if I start up a server independently on port 3001 with a valid local SSL certificate. In practice, this is annoying and is just generally a manual dependency that I don't like.
Does anyone know a cleaner workaround for this? Either:
1) How to configure Capybara's internal server to find and use my local SSL certificate?, or
2) How to disable forced SSL for javascript testing, or
3) How to automatically start that local server running before any javascript tests?
Any help would be greatly appreciated.
You can tell Capybara how to start a server, by passing a block accepting an app and a port to Capybara.server. The default just calls Rake::Handler::WEBrick.run:
# This is the default setting
Capybara.server = {|app, port| Capybara.run_default_server(app, port)}
def run_default_server(app, port)
require 'rack/handler/webrick'
Rack::Handler::WEBrick.run(app, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
end
As long as whatever you pass to server accepts an app and a port, you can define whatever server starting code you like.
Rack::Handler::WEBrick passes most of its options straight through to WEBrick::HTTPServer, so you can pass the options for SSL config (SSLEnable, SSLCertificate and SSLPrivateKey, taken from the docs) and start your server something like this:
def run_ssl_server(app, port)
require 'rack/handler/webrick'
require 'webrick/https'
certificate = OpenSSL::X509::Certificate.new File.read '/myapp/some_directory/certificate.pem'
key = OpenSSL::PKey::RSA.new File.read '/myapp/some_directory/key.pem'
opts = {
:Port => port,
:AccessLog => [],
:Logger => WEBrick::Log::new(nil, 0),
:SSLEnable => true,
:SSLCertificate => certificate,
:SSLPrivateKey => key
}
Rack::Handler::WEBrick.run(app, opts)
end
# Elsewhere in your test/spec helper
Capybara.server = {|app, port| run_ssl_server(app, port)}

Resources