Rspec and Chrome/headless flag - ruby-on-rails

I've got headless Chrome and Chrome working for my Rspec tests. I want a flag to switch between the two so I can see the tests happen when I want and hide them when I don't. How can I implement something like:
rspec --headless
Right now I just have this secret tied to a .env var:
Capybara.javascript_driver = Rails.application.secrets.headless ? :headless_chrome : :chrome

in your rails_helper.rb you should create a statement like that:
RSpec.configure do |config|
config.before(:each) do
if ENV['HEADLESS'] == 'true'
Capybara.current_driver = :selenium_chrome_headless
else
Capybara.current_driver = :selenium_chrome
end
end
end
then send a variable when running specs
$ HEADLESS=true rspec ./spec/features

Well, overriding the env var works so that's something.
HEADLESS=true rspec

Hmm, I personally use this code to register driver:
Capybara.register_driver :chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
args: [
('headless' if ENV.fetch('HEADLESS', '1') == '1')
].compact
}
)
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities
)
end
then in .env you can set the variable to be HEADLESS or not by default and then if you want to overwrite it just type HEADLESS=0 rspec

Related

Dockerized Selenium with rails tests

I'm trying to run rspec tests with Selenium chrome in docker but caught dozens of error. Finally i connected capybara to remote capybara, but now i got these errors:
Got 0 failures and 2 other errors:
1.1) Failure/Error: visit new_user_session_path
Selenium::WebDriver::Error::WebDriverError:
unexpected response, code=404, content-type="text/html"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Action Controller: Exception caught</title>
....................
Failure/Error: raise Error::WebDriverError, msg
Selenium::WebDriver::Error::WebDriverError:
unexpected response, code=404, content-type="text/html"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Action Controller: Exception caught</title>
<style>
body {
background-color: #FAFAFA;
...............
So here is my rails_helper.rb. It's really messy cause I tried dozen times with different configs
require 'simplecov'
SimpleCov.start
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'turnip/capybara'
require "selenium/webdriver"
require 'capybara-screenshot/rspec'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
# Checks for pending migration and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
Capybara::Screenshot.register_driver(:headless_chrome) do |driver, path|
driver.browser.manage.window.resize_to(1600, 1200)
driver.browser.save_screenshot("tmp/capybara/chrom_#{Time.now}.png")
end
url = 'http://test.prs.com:3001/'
Capybara.javascript_driver = :remote_browser
Capybara.register_driver :headless_chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOqptions: { args: %w(headless disable-gpu no-sandbox) }
)
end
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOqptions: { args: %w(headless disable-gpu no-sandbox) }
)
Capybara.default_driver = :remote_browser
Capybara.register_driver :remote_browser do |app|
Capybara::Selenium::Driver.new(app, :browser => :remote, url: url,
desired_capabilities: capabilities)
end
# Capybara::Selenium::Driver.new app,
# browser: :chrome,
# desired_capabilities: capabilities
# end
Capybara.app_host = "http://#{ENV['APP_HOST']}:#{3001}"
Capybara.run_server = false
Capybara.configure do |config|
config.always_include_port = true
end
Chromedriver.set_version '2.32'
# Capybara.javascript_driver = :headless_chrome
# Capybara.server_host= '0.0.0.0'
# Capybara.default_host = "http://test.prs.com"
# Capybara.app_host = "#{Capybara.default_host}:#{Capybara.server_port}"
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.include RequestSpecHelper
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.before(:each) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
end
config.before(:all, type: :request) do
host! 'test.prs.com'
end
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
And here is my docker-compose.yml:
version: '3'
services:
db:
image: postgres
web:
build: .
command: bundle exec rails s -p 3001 -b '0.0.0.0'
volumes:
- .:/prs
ports: ['3000:3000', '3001:3001']
# - "3001:3001"
depends_on:
- db
- selenium
extra_hosts:
- "test.prs.com:127.0.0.1"
environment:
- APP_HOST=test.prs.com
links:
- selenium
selenium:
image: selenium/standalone-chrome-debug:3.6.0-bromine
# Debug version enables VNC ability
ports: ['4444:4444', '5900:5900']
# Bind selenium port & VNC port
volumes:
- /dev/shm:/dev/shm
shm_size: 1024m
privileged: true
container_name: selenium
I'm new to all this so any help will be appreciated.
From the comments you have clarified that you are trying to run the tests in the web docker instance while using the selenium driven browser in the selenium docker instance. Additionally, since your tests are working locally I assume you are using Rails 5.1+ so transactional testing for feature tests will work. Based on those parameters there are a few things needed to make everything work properly.
Capybara needs to start its own copy of the app to run the tests against. This is needed for transactional testing to work and for request completion detection. You enable that with
Capybara.run_server = true # You currently have this set to false for some reason
Capybara needs to run its copy of the app on an interface which can be reached from the selenium docker instance. Easiest way to do that is to specify to bind to 0.0.0.0
Capybara.server_host = `0.0.0.0`
Capybara.server_port = 3001 # I'm assuming this is what you want, change to another port and make sure it's reachable from `selenium` instance if desired
The driver capybara is using needs to be configured to use the selenium instance
Capybara.register_driver :remote_browser do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w(headless disable-gpu no-sandbox) }
)
Capybara::Selenium::Driver.new(app,
:browser => :remote,
url: "http://selenium:4444",
desired_capabilities: capabilities
)
end
Capybara.javascript_driver = :remote_browser
# Capybara.default_driver = :remote_browser # only needed if you want all tests to use selenium rather than just JS tagged tests.
Configure Capybara to use the correct host when visiting relative URLs
Capybara.app_host = "http://web:3001" # a URL that represents the `web` docker instance from the perspective of the `selenium` docker instance
Notes: If you were expecting your tests to run on port 3001 as I guessed, then you want to stop the docker instance from launching rails s on that port, since you want the tests run against an app instance that Capybara itself launches # command: bundle exec rails s -p 3001 -b '0.0.0.0'. If the instance you currently have running on 3001 is for something else then you'll need a different port for the tests.
Additionally, if you're not running Rails 5.1+ then you'll need to set config.use_transactional_fixtures = false and fully configure database_cleaner - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example . If you are using Rails 5.1+ then you can probably remove all the database_cleaner stuff.
My problem was completely unrelated to Docker, Selenium, Capybara, Chromedriver or any of that. They were all red herrings because on my container only tests related to feature specs were failing.
It turns out they were all failing because feature specs are the only part of the app that looks at IP addresses.
I am using the ip_anonymizer gem and failed to set the IP_HASH_SECRET for the container. Hopefully, anyone else using this gem with Capybara and CI finds this useful.

Capybara.current_driver = :chrome versus :selenium_chrome

what are the advantages/pros/cons of using
Capybara.current_driver = :chrome
versus using
Capybara.current_driver = :selenium_chrome
To give a bit more context, I'm using capybara in a standalone ruby script.
They are just names that can be used to identify specific driver configurations so it all depends on what is registered as :chrome and :selenium_chrome. The latest Capybara (2.15.1) release provides a default registration for :selenium_chrome which is defined as
# Configure selenium-webdriver to use chrome as the browser
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
but that can be redefined in your code. There is no gem, that I know of, that provides a default registration of :chrome so if that's currently valid it would most likely be defined in your code (search for register_driver).

capybara chrome headless confirm dialog

I'm trying to use headless mode of chrome with capybara/selenium in rspec Ruby on Rails and getting a error when try to click on confirm dialog
Capybara.register_driver(:headless_chrome) do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w[headless disable-gpu test-type window-size=1920x1080] }
)
driver = Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities
)
driver
end
Capybara.server = :puma
Capybara.javascript_driver = :headless_chrome
scenario 'delete movie' do
login_as user
visit edit_public_movie_path(movie)
expect(page).to have_selector('span[ng-click="ctrl.deleteMovie()"]')
find('span[ng-click="ctrl.deleteMovie()"]').click
page.driver.browser.switch_to.alert.accept
wait_for_ajax(wait_after: 1)
expect(page).to have_content('Click here to upload movie (Max: 500 MB)')
end
And got this error
Event movie success delete movie
Failure/Error: page.driver.browser.switch_to.alert.accept
Selenium::WebDriver::Error::NoSuchAlertError:
no alert open
(Session info: headless chrome=59.0.3071.115)
(Driver info: chromedriver=2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b),platform=Mac OS X 10.12.5 x86_64)
With common mode it works without errors. Seems the selenium don't support headless mode or need to other approach to do it. Any ideas?
Update(resolved)
So, I spent a lot of time and find working environments.
You need to last build of chromium because preview versions have a bug. You can get it by these scripts linux or Mac
Here is my capybara config
Capybara.register_driver(:headless_chrome) do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
args: %w[headless disable-gpu disable-popup-blocking no-sandbox window-size=1920x1920],
binary: "#{Dir.home}/chromium-latest-#{platrofm}/latest/#{chrome_file}"
})
driver = Capybara::Selenium::Driver.new(app,
browser: :chrome,
desired_capabilities: capabilities)
driver
end
Capybara.server = :puma
Capybara.javascript_driver = :headless_chrome
Capybara.default_max_wait_time = 20
Capybara.server_port = 55305
def platform
if /linux/ =~ RUBY_PLATFORM
"linux"
else
"macosx"
end
end
def platform_linux?
platform == "linux"
end
def chrome_file
if platform_linux?
"chrome"
else
"Chromium.app/Contents/MacOS/Chromium"
end
end
And you should use page.driver.browser.switch_to.alert.accept instead of page.accept_alert
Had the same problem. Please use the disable-popup-blocking in chromeOptions.

How to use selenium and poltergeist together in RSpec?

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.

How to set the ignore_ssl_errors option for Capybara Webkit in spec_helper.rb

In my spec_helper file I have:
Capybara.javascript_driver = :webkit
capybara_webkit now has a ignore_ssl_errors option that I want to use. How do I specify that in my spec_helper?
Here's how to register the :webkit driver with the :ignore_ssl_errors option.
Capybara.register_driver :webkit do |app|
Capybara::Driver::Webkit.new(app, :ignore_ssl_errors => true)
end
As of writing (capybara-webkit 1.7.1), the configuration seems to have been simplified:
Capybara::Webkit.configure do |config|
config.ignore_ssl_errors
end
(source)
Somehow the above register_driver examples don't work with Capybara 1.1.4. The example below is taken from the capybara browser_spec.rb.
Capybara.register_driver :webkit_ignore_ssl do |app|
browser = Capybara::Webkit::Browser.new(Capybara::Webkit::Connection.new).tap do |browser|
browser.ignore_ssl_errors
end
Capybara::Webkit::Driver.new(app, :browser => browser)
end
Capybara.javascript_driver = :webkit_ignore_ssl
As #hjblok says, the interface has changed in recent versions of capybara-webkit. You can simplify the solution slightly:
Capybara.register_driver :webkit_ignore_ssl do |app|
Capybara::Webkit::Driver.new(app).tap {|d| d.browser.ignore_ssl_errors }
end
Capybara.javascript_driver = :webkit_ignore_ssl
When createing a new webkit Object you can use this to ignore the ssl errors
Capybara::Driver::Webkit.new({ :ignore_ssl_errors => true})

Resources