diff --git a/.travis.yml b/.travis.yml index d15de4fd..4b86878b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: ruby sudo: required dist: trusty +services: + - docker rvm: - 2.5.0 - 2.3.6 @@ -30,6 +32,9 @@ cache: matrix: include: + - gemfile: Gemfile + rvm: 2.5.0 + env: CAPYBARA_CHROME_REMOTE=true - gemfile: gemfiles/Gemfile.rspec-34 rvm: 2.3.6 env: CAPYBARA_FF=true @@ -66,6 +71,7 @@ matrix: - gemfile: gemfiles/Gemfile.beta-versions - gemfile: gemfiles/Gemfile.edge-marionette - rvm: rbx-3 + - env: CAPYBARA_CHROME_REMOTE=true before_install: - gem update --system # - gem update bundler @@ -77,7 +83,7 @@ before_install: bundle config local.selenium-webdriver ../selenium/build/rb; fi before_script: - - if [[ -z $HEADLESS ]]; then + - if [[ -z $HEADLESS && -z $CAPYBARA_CHROME_REMOTE ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; sleep 1; @@ -85,4 +91,15 @@ before_script: echo "require('awful'); tags = {}; tags[1] = awful.tag({'name'})" > ~/.config/awesome/rc.lua; awesome & fi + - if [[ $CAPYBARA_CHROME_REMOTE = true ]]; then + docker-compose up -d selenium + + TIMEOUT=10 + + until wget --spider http://localhost:4444 > /dev/null 2>&1 || [ $TIMEOUT -eq 0 ]; do + echo "Waiting for selenium server, $((TIMEOUT--)) remaining attempts..."; + sleep 1; + done + fi + script: "bundle exec rake travis" diff --git a/Rakefile b/Rakefile index 4ac166e7..d7a900e8 100644 --- a/Rakefile +++ b/Rakefile @@ -7,20 +7,22 @@ require 'yard' desc "Run all examples with Firefox non-marionette" +rspec_opts = %w[--color] + RSpec::Core::RakeTask.new(:spec_marionette) do |t| - t.rspec_opts = %w[--color] + t.rspec_opts = rspec_opts t.pattern = './spec{,/*/**}/*{_spec.rb,_spec_marionette.rb}' end -%w[chrome ie edge].each do |driver| +%w[chrome ie edge chrome_remote].each do |driver| RSpec::Core::RakeTask.new(:"spec_#{driver}") do |t| - t.rspec_opts = %w[--color] + t.rspec_opts = rspec_opts t.pattern = "./spec/*{_spec_#{driver}.rb}" end end RSpec::Core::RakeTask.new(:spec_rack) do |t| - t.rspec_opts = %w[--color] + t.rspec_opts = rspec_opts t.pattern = './spec{,/*/**}/*{_spec.rb}' end @@ -42,6 +44,8 @@ task :travis do Rake::Task[:spec_ie].invoke elsif ENV['CAPYBARA_EDGE'] Rake::Task[:spec_edge].invoke + elsif ENV['CAPYBARA_CHROME_REMOTE'] + Rake::Task[:spec_chrome_remote].invoke else Rake::Task[:spec_chrome].invoke end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..735cd624 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +--- +version: "2.1" + +services: + selenium: + network_mode: "host" + image: "selenium/${SELENIUM_IMAGE:-standalone-chrome-debug}" + volumes: + - "/dev/shm:/dev/shm" + - "${PWD}:${PWD}" # For making attach_file specs work diff --git a/lib/capybara/selenium/driver.rb b/lib/capybara/selenium/driver.rb index f519f709..6ddb48d5 100644 --- a/lib/capybara/selenium/driver.rb +++ b/lib/capybara/selenium/driver.rb @@ -32,10 +32,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base def browser unless @browser - if firefox? - options[:desired_capabilities] ||= {} - options[:desired_capabilities][:unexpectedAlertBehaviour] = "ignore" - end + # if firefox? + # options[:desired_capabilities] ||= {} + # options[:desired_capabilities][:unexpectedAlertBehaviour] = "ignore" + # end @processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) } @browser = Selenium::WebDriver.for(options[:browser], @processed_options) @@ -305,30 +305,26 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base # @api private def firefox? - browser_name == "firefox" + browser.browser == :firefox end # @api private def chrome? - browser_name == "chrome" + browser.browser == :chrome end # @api private def edge? - browser_name == "edge" + browser.browser == :edge end # @api private def ie? - browser_name == "ie" + browser.browser == :ie end private - def browser_name - options[:browser].to_s - end - def clear_storage if options[:clear_session_storage] if @browser.respond_to? :session_storage diff --git a/lib/capybara/selenium/node.rb b/lib/capybara/selenium/node.rb index 6717c25d..2a5c9fc4 100644 --- a/lib/capybara/selenium/node.rb +++ b/lib/capybara/selenium/node.rb @@ -255,7 +255,7 @@ private def set_date(value) # rubocop:disable Naming/AccessorMethodName if value.respond_to?(:to_date) - set_text(value.to_date.strftime(SET_FORMATS[driver.options[:browser].to_sym][:date])) + set_text(value.to_date.strftime(SET_FORMATS[driver.browser.browser][:date])) else set_text(value) end @@ -263,7 +263,7 @@ private def set_time(value) # rubocop:disable Naming/AccessorMethodName if value.respond_to?(:to_time) - set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:time])) + set_text(value.to_time.strftime(SET_FORMATS[driver.browser.browser][:time])) else set_text(value) end @@ -271,7 +271,7 @@ private def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName if value.respond_to?(:to_time) - set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:datetime])) + set_text(value.to_time.strftime(SET_FORMATS[driver.browser.browser][:datetime])) else set_text(value) end diff --git a/spec/selenium_spec_chrome_remote.rb b/spec/selenium_spec_chrome_remote.rb new file mode 100644 index 00000000..81f294ee --- /dev/null +++ b/spec/selenium_spec_chrome_remote.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'selenium-webdriver' +require 'shared_selenium_session' +require 'rspec/shared_spec_matchers' + +def selenium_host + ENV.fetch('SELENIUM_HOST', '0.0.0.0') +end + +def selenium_port + ENV.fetch('SELENIUM_PORT', 4444) +end + +def ensure_selenium_running! + TCPSocket.open(selenium_host, selenium_port) +rescue + raise 'Selenium is not running. ' \ + "You can run a selenium server easily with: \n" \ + ' $ docker-compose up -d selenium' +end + +Capybara.register_driver :selenium_chrome_remote do |app| + ensure_selenium_running! + + url = "http://#{selenium_host}:#{selenium_port}/wd/hub" + caps = Selenium::WebDriver::Remote::Capabilities.chrome + + Capybara::Selenium::Driver.new app, + browser: :remote, + desired_capabilities: caps, + url: url +end + +CHROME_REMOTE_DRIVER = :selenium_chrome_remote + +module TestSessions + Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp) +end + +skipped_tests = %i[response_headers status_code trigger] +# skip window tests when headless for now - closing a window not supported by chromedriver/chrome +skipped_tests << :windows if ENV['TRAVIS'] && (ENV['SKIP_WINDOW'] || ENV['HEADLESS']) + +Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests + +RSpec.describe "Capybara::Session with chrome" do + include Capybara::SpecHelper + include_examples "Capybara::Session", TestSessions::Chrome, CHROME_REMOTE_DRIVER + include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_REMOTE_DRIVER + + it 'is considered to be chrome' do + expect(session.driver).to be_chrome + end +end diff --git a/spec/shared_selenium_session.rb b/spec/shared_selenium_session.rb index 12770c92..7d0f2bb7 100644 --- a/spec/shared_selenium_session.rb +++ b/spec/shared_selenium_session.rb @@ -43,13 +43,15 @@ RSpec.shared_examples "Capybara::Session" do |session, mode| end it "should have return code 1 when running selenium_driver_rspec_failure.rb" do - skip if ENV['HEADLESS'] + skip if headless_or_remote? + system(@env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL) expect($CHILD_STATUS.exitstatus).to eq(1) end it "should have return code 0 when running selenium_driver_rspec_success.rb" do - skip if ENV['HEADLESS'] + skip if headless_or_remote? + system(@env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL) expect($CHILD_STATUS.exitstatus).to eq(0) end @@ -226,4 +228,8 @@ RSpec.shared_examples "Capybara::Session" do |session, mode| end end end + + def headless_or_remote? + !ENV['HEADLESS'].nil? || session.driver.options[:browser] == :remote + end end