ruby-on-rails - Selenium webdriver 不适用于 docker-compose
问题描述
我有一个如下所示的 docker-compose.yml,其中使用selenium/standalone-chrome-debug
图像为硒定义了服务。
# docker-compose.yml
version: '3'
services:
webapp:
tty: true
stdin_open: true
container_name: webapp
depends_on:
- postgres
- elasticsearch
- redis
- selenium
build: .
volumes:
- .:/webapp
ports:
- "3000:3000"
entrypoint: sh /webapp/setup.sh
environment:
- REDISTOGO_URL=redis://redis:6379
- ELASTICSEARCH_URL=http://elasticsearch:9200
- SELENIUM_HOST=selenium
- SELENIUM_PORT=4444
postgres:
container_name: postgres
image: postgres:9.5.17
ports:
- "5432:5432"
volumes:
- ./postgres:/var/lib/postgresql
environment:
- POSTGRES_PASSWORD=test
- POSTGRES_USER=test
- POSTGRES_DB=test
redis:
container_name: redis
image: redis:5.0.5-alpine
command: redis-server
hostname: redis
ports:
- "6379:6379"
volumes:
- redis:/data
sidekiq:
build: .
command: bundle exec sidekiq
volumes:
- .:/webapp
depends_on:
- postgres
- redis
environment:
- REDISTOGO_URL=redis://redis:6379
elasticsearch:
image: elasticsearch:6.8.0
container_name: elasticsearch
ports:
- "9200:9200"
depends_on:
- postgres
volumes:
- esdata:/usr/share/elasticsearch/data
selenium:
image: selenium/standalone-chrome-debug
ports:
- "4444:4444"
volumes:
redis:
postgres:
esdata:
和 rails_helper.rb
# rails_helper.rb
require 'database_cleaner'
require 'simplecov'
SimpleCov.start('rails') do
coverage_dir 'coverage'
add_group 'Modules', 'app/modules'
add_filter "lib/api_constraints.rb"
add_filter "app/uploaders/"
add_filter "app/models/redactor_rails/"
add_filter "app/controllers/application_controller.rb"
add_filter "app/models/application_record.rb"
add_filter "app/workers/"
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
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'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
# Add additional requires below this line. Rails is not loaded until this point!
require 'capybara/rspec'
require 'net_http_ssl_fix'
require 'selenium-webdriver'
require 'webdrivers/chromedriver'
require 'spree/testing_support/capybara_ext'
require 'rack_session_access/capybara'
require 'capybara-screenshot/rspec'
require 'rspec/retry'
# Add rake task example group
require 'support/tasks'
# Force lock local timezone for test environment
ENV['TZ'] = 'UTC'
Webdrivers.cache_time = 86_400
selenium_host = "http://127.0.0.1:4444/wd/hub"
unless ENV['SELENIUM_HOST'].nil?
selenium_host = "http://#{ ENV["SELENIUM_HOST"] }:4444/wd/hub"
end
Capybara.register_driver :selenium_chrome do |app|
caps = Selenium::WebDriver::Remote::Capabilities.chrome(
browserName: 'chrome',
"chromeOptions" => {
args: ['headless','no-sandbox','disable-gpu','window-size=1920x1080']
}
)
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
url: selenium_host,
desired_capabilities: caps
)
end
Capybara.server = :puma, { Silent: true }
Capybara.javascript_driver = :selenium_chrome
Capybara.save_path = "#{ Rails.root }/tmp/screenshots/"
Capybara.raise_server_errors = false
Capybara.default_max_wait_time = 10
Capybara.asset_host = 'http://localhost:3000'
Capybara.configure do |config|
config.match = :prefer_exact
config.ignore_hidden_elements = false
config.visible_text_only = true
# accept clicking of associated label for checkboxes/radio buttons (css psuedo elements)
config.automatic_label_click = true
end
Capybara.always_include_port = true
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# 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!
RSpec.configure do |config|
config.expect_with :rspec do |c|
# enable both should and expect
c.syntax = [:should, :expect]
end
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
config.append_after(:each) do
Capybara.reset_sessions!
end
config.include Capybara::DSL
config.order = "random"
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
# compile front end
WebpackerHelper.compile_once
# disable searchkick callbacks, now enabled by using search hook
Searchkick.disable_callbacks
end
# hook for enabling searchkick callbacks
config.around(:each, search: true) do |example|
Searchkick.callbacks(true) do
example.run
end
end
config.before(:each) do
DatabaseCleaner.strategy = Capybara.current_driver == :rack_test ? :transaction : :truncation
DatabaseCleaner.clean
DatabaseCleaner.start
DownloadHelper.clear_downloads
Factory.seed_data
end
config.after(:each) do
Capybara.app_host = nil # don't change me, explicitly set host in each spec appropriately
DatabaseCleaner.clean
Timecop.return
end
config.include DeviseHelpers
config.include Devise::Test::ControllerHelpers, type: :controller
config.include CommonHelper
config.include ImageHelper
config.include CommonSpecHelper
config.include ReactComponentHelper
config.include BraintreeHelper
config.include BookingSpecHelper
config.include CapybaraRspecExt
config.include DownloadHelper
config.include ActionView::Helpers::NumberHelper
config.include ActionView::Helpers::DateHelper
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
# Suppress Braintree noise
null_logger = Logger.new("/dev/null")
null_logger.level = Logger::INFO
Braintree::Configuration.logger = null_logger
config.after(:example, :on_fail => :screenshot) do |example|
full_screenshot if example.exception
end
config.after(:example, :on_fail => :open_page) do |example|
save_and_open_page if example.exception
end
# set parallel env for searchkick
Searchkick.index_suffix = ENV['TEST_ENV_NUMBER']
# show retry status in spec process
config.verbose_retry = true
# default number of retries
config.default_retry_count = 0
# sleep for 1 seconds before retry
config.default_sleep_interval = 1
# Retry failing specs (conditions to retry are set in config.retry_count_condition)
config.around :each do |ex|
ex.run_with_retry
end
# Retry failing JS specs or failing specs with specific exception
config.retry_count_condition = proc do |ex|
if (ex.metadata[:js] || [Net::ReadTimeout].include?(ex.exception.class)) && !ex.metadata[:on_fail]
nil # will fallback to config.default_retry_count
else
0 # no retries if conditions not matched
end
end
# callback to be run between retries
config.retry_callback = proc do |ex|
Capybara.reset!
end
end
当我使用 js: true 为功能规范运行 docker-compose exec webapp rspec spec/feature/test_spec.rb 时,它会失败并显示以下堆栈跟踪:
Failure/Error: ex.run_with_retry
Selenium::WebDriver::Error::WebDriverError:
<unknown>: Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document.
(Session info: chrome=75.0.3770.100)
# #0 0x5651e686d7a9 <unknown>
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/response.rb:72:in `assert_ok'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/http/common.rb:88:in `new'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/http/common.rb:88:in `create_response'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/http/default.rb:114:in `request'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/http/common.rb:64:in `call'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/bridge.rb:167:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/w3c/bridge.rb:567:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/w3c/bridge.rb:305:in `execute_script'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/remote/w3c/bridge.rb:277:in `clear_session_storage'
# /usr/local/bundle/gems/selenium-webdriver-3.142.3/lib/selenium/webdriver/common/html5/session_storage.rb:40:in `clear'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver.rb:325:in `clear_session_storage'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver.rb:317:in `clear_storage'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver_specializations/chrome_driver.rb:45:in `clear_storage'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver.rb:291:in `clear_browser_state'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver.rb:446:in `reset_browser_state'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver.rb:125:in `reset!'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/selenium/driver_specializations/chrome_driver.rb:36:in `reset!'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/session.rb:128:in `reset!'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara.rb:315:in `block in reset_sessions!'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara.rb:315:in `reverse_each'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara.rb:315:in `reset_sessions!'
# /usr/local/bundle/gems/capybara-3.20.0/lib/capybara/rspec.rb:18:in `block (2 levels) in <top (required)>'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:123:in `block in run'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `loop'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `run'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec_ext/rspec_ext.rb:12:in `run_with_retry'
# ./spec/rails_helper.rb:224:in `block (2 levels) in <top (required)>'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:123:in `block in run'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `loop'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `run'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec_ext/rspec_ext.rb:12:in `run_with_retry'
# /usr/local/bundle/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:37:in `block (2 levels) in setup'
有什么我想念的吗?使用 docker-compose 设置 selenium 的正确方法是什么?
解决方案
由于您使用的是 selenium 独立服务器设置,因此您需要配置 Capybara selenium 驱动程序以供远程使用。
Capybara.register_driver :selenium_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new(args: %w[
headless no-sandbox disable-gpu window-size=1920x1080
])
Capybara::Selenium::Driver.new(
app,
browser: :remote,
desired_capabilities: :chrome,
options: options
url: selenium_host,
)
end
其他注意事项:
如果您使用的是 Rails 5.1+,您可能可以删除所有数据库清理程序并启用事务测试
在编写测试时设置
ignore_hidden_elements = false
是一个糟糕的主意,因为您通常只想处理用户实际可以看到的元素:smart 通常是 Capybara.match 更好的默认值(而不是 :prefer_exact),如果您关心确保您的测试引用您期望的元素。
您不应该在每个 RSpec 测试类型中包含 Capybara::DSL
推荐阅读
- flutter - 如何在 Flutter 中的 TabBarView 中从最后一个选项卡滑动到第一个选项卡
- javascript - 如何在 webpack 中定义全局变量?
- c++ - 获取无符号字符的长度*
- php - 如何在 ionic 4 中的 inAppBroser.create() 中处理来自 php Web 应用程序的回调
- php - ON DUPLICATE KEY UPDATE 的正确语法是什么?
- java - 为什么我不能在 Android Studio 中使用 ImageButton 上传图像文件?
- android - adb shell om clear com.android.vending 错误
- mysql - SQL Join left, group by 得到 Where ID 只等于一个特定的
- javascript - 如何获取动态 iframe 的 xpath?
- html - 媒体查询未更改字体大小