水豚 + Selenium-webdriver + RSpec 文件装置 + SSR 给予 Net::ReadTimeout

Capybara + Selenium-webdriver + RSpec file fixtures + SSR giving Net::ReadTimeout

我注意到一个奇怪的问题,几天来我一直无法解决。

我有一个 Rails 5 API 服务器,系统测试使用 RSpec 和 Capybara + Selenium-webdriver 无头驾驶 Chrome。

我正在使用 Capybara.app_host = 'http://localhost:4200' 使测试命中一个单独的开发服务器,该服务器 运行 正在 Ember front-end。 Ember front-end 查看用户代理知道然后将请求发送到 Rails API 测试数据库。

所有测试 运行 都很好,除了使用 RSpec file fixtures.

的测试

这是一项失败的规范:

describe 'the affiliate program', :vcr, type: :system do
  fixtures :all

  before do
    Capybara.session_name = :affiliate
    visit('/')
    signup_and_verify_email(signup_intent: :seller)
    visit_affiliate_settings
  end

  it 'can use the affiliate page' do
    affiliate_token = page.text[/Your affiliate token is \b(.+?)\b/i, 1]
    expect(affiliate_token).to be_present

    # When a referral signs up.
    Capybara.session_name = :referral
    visit("?client=#{affiliate_token}")
    signup_and_verify_email(signup_intent: :member)

    refresh

    # It can track the referral.
    Capybara.session_name = :affiliate
    refresh
    expect(page).to have_selector('.referral-row', count: 1)

    # When a referral makes a purchase.
    Capybara.session_name = :referral
    find('[href="/videos"]').click
    find('.price-area .coin-usd-amount', match: :first).click
    find('.cart-dropdown-body .checkout-button').click
    find('.checkout-button').click
    wait_for { find('.countdown-timer') }
    order = Order.last
    order.force_complete_payment!
    Rake::Task['affiliate_referral:update_amounts_earned'].invoke

    # It can track the earnings.
    Capybara.session_name = :affiliate
    refresh
    amount = (order.price * AffiliateReferral::COMMISSION_PERCENTAGE).floor.to_f
    amount_in_dom = find('.referral-amount-earned', match: :first).text.gsub(/[^\d\.]/, '').to_f * 100
    expect(amount).to equal(amount_in_dom)
  end
end

这可能会在 99% 的情况下失败。它通过的情况很奇怪。我可以让我的测试套件最终通过 运行循环一天。

我最终将所有版本升级到最新版本(Node 10,最新 Ember,最新 Rails),但问题仍然存在。

我可以 post 稍后重现该问题的示例存储库。我只是想得到这个 posted 以防有人遇到这个问题。

这是超时发生时的典型堆栈跟踪:

 1.1) Failure/Error: page.evaluate_script('window.location.reload()')

      Net::ReadTimeout:
        Net::ReadTimeout



      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:97:in `block in request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:110:in `block in request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:109:in `request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:121:in `response_for'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:76:in `request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/common.rb:62:in `call'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/bridge.rb:164:in `execute'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:584:in `execute'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:267:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/common/driver.rb:211:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:84:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:88:in `evaluate_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/session.rb:575:in `evaluate_script'
      # ./spec/support/selenium.rb:48:in `refresh'
      # ./spec/support/pages.rb:70:in `signup_and_verify_email'
      # ./spec/system/payment_spec.rb:43:in `block (3 levels) in <top (required)>'

我应该指出 page.evaluate_script('window.location.reload()') 并不总是这样。它可能发生在像 visit('/').

这样的良性事物上

Edit:我尝试使用 DISABLE_FASTBOOT env 变量禁用 Ember FastBoot(server-side 渲染),突然所有测试都通过了。我在想 RSpec 固定装置以某种方式导致 Ember FastBoot 在某些情况下无法完成渲染。这肯定与我偶尔在生产日志中看到的断开连接一致。

我一直在试验客户端代码,这可能是因为我使用了 FastBoot's deferRendering call


编辑:我使用的是以下版本:


编辑:我正在使用这个有点不稳定的 Node/Express 库 fastboot-app-server 来进行 server-side 渲染。我发现它有时会删除重要的响应 headers(Content-Type 和 Content-Encoding)。我想知道这是否导致了这个问题。


编辑:我添加了严格的内容安全策略以确保在测试套件期间没有外部请求 运行ning 可能导致 Net::ReadTimeout.

我在 Chrome 网络选项卡锁定时检查它,它似乎没有加载任何内容。手动刷新浏览器允许测试开始并继续 运行ning。真奇怪。

我已经在这上面花了几个星期了,可能是时候放弃 Selenium 测试了。

我升级到 Chrome 70 和 chromedriver 2.43。好像没什么区别。

我尝试使用 rspec-retry gem 在超时发生时强制刷新,但 gem 似乎无法捕获超时异常。

我已经检查了对 chromedriver 的原始请求,那里有问题。看起来总是 POST http://127.0.0.1/session/<session id>/refresh。我尝试以另一种方式刷新:visit(page.current_path) 这似乎可以解决问题![​​=26=]

通过将 page.driver.browser.navigate.refresh 切换为 visit(page.current_path),我终于让我的测试套件通过了。

我知道这是一个丑陋的技巧,但这是我能找到的唯一让事情正常进行的方法(请参阅我在问题编辑中的各种尝试)。

我查看了每次导致超时的 chromedriver 请求:POST http://127.0.0.1/session/<session id>/refresh。我只能猜测这是 chromedriver 的某种问题。也许顺便说一下,它仅在多个 chromedriver 实例处于活动状态时挂起(这在使用多个 Capybara 会话时发生)。

编辑:我还需要考虑查询参数:

def refresh   
  query = URI.parse(page.current_url).query
  path = page.current_path
  path += "?#{query}" if query.present?

  visit(path)
end

我试过只做 visit(page.current_url) 但那也会超时。