Capybara/Selenium 在 location.reload() 上随机得到一个 Net::ReadTimeout

Capybara/Selenium gets a Net::ReadTimeout randomly on location.reload()

我正在使用 Capybara、selenium-webdriver gem 和 chromedriver 来驱动我的 javascript 启用测试。

问题是我们大约 50% 的构建由于 Net::ReadTimeout 错误而失败。起初这表现为 'could not find element' 错误,但在我将 Capybara 的默认最长等待时间提高到 30 秒后,我开始看到超时。

我检查了超时发生时的屏幕截图,它停留在我们在使用 Javascript 函数 location.reload() 重新加载页面之前简要显示的 'Successfully logged in' 模态。

我在本地 运行 进行了测试,有时可以重现,也 运行domly。有时它会通过此模式压缩并重新加载如此之快你几乎看不到它,而其他时候它会永远挂起。

我觉得这不是资产编译问题,因为此时网站已经加载,以便用户访问登录表单。

想知道是否有人以前见过此问题并知道解决方案。

具体代码:

    visit login_path

    page.within '#sign-in-pane__body' do
      fill_in 'Email', with: user.email
      click_button 'Submit'
    end

    expect(page).to have_content 'Enter Password'

    page.within '#sign-in-pane__body' do
      fill_in 'Password', with: user.password
      click_button 'Submit'
    end

    expect(page).to have_text 'Home page landing text'

挂断发生在 click_button 'Submit' 和等待主页文本之间。

导致超时的逻辑流程是用户提交登录表单,我们等待服务器呈现一个 .js.erb 模板,该模板在成功登录后触发 JS 事件。当触发发生时,我们会显示一个模式,说明登录成功,然后执行 location.reload().

事实证明,这并不是在 JS 中执行 location.reload() 所独有的。有时只是访问一个页面。

我的解决方案是为 selenium 驱动程序创建一个 HTTP 客户端并指定更长的超时时间:

Capybara.register_driver :chrome do |app|
  client = Selenium::WebDriver::Remote::Http::Default.new
  client.read_timeout = 120

  Capybara::Selenium::Driver.new(app, {browser: :chrome, http_client: client})
end

使用我自己的访问方法版本解决了类似问题:

 def safe_visit(url)
  max_retries = 3
  times_retried = 0
  begin
    visit url
  rescue Net::ReadTimeout => error
    if times_retried < max_retries
      times_retried += 1
      puts "Failed to visit #{url}, retry #{times_retried}/#{max_retries}"
      retry
    else
      puts error.message
      puts error.backtrace.inspect
      exit(1)
    end
  end
end

如果你需要为 headless 配置它,你需要执行以下操作 chrome

Capybara.register_driver :headless_chrome do |app|
  client = Selenium::WebDriver::Remote::Http::Default.new
  client.timeout = 120 # instead of the default 60
  options = Selenium::WebDriver::Chrome::Options.new
  options.headless!

  Capybara::Selenium::Driver.new(app, {
    browser: :chrome,
    http_client: client,
    options: options
  })
end

Capybara.default_driver = :headless_chrome
Capybara.javascript_driver = :headless_chrome

在功能中传递无头参数对我不起作用。

capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
   chromeOptions: { args: %w[headless disable-gpu] }
)

这里是more details为什么 headless in capabilities 不起作用的原因。