fjordllc / bootcamp

プログラマー向けEラーニングシステム
https://bootcamp.fjord.jp
MIT License
281 stars 71 forks source link

chromedriverを更新すると、テスト実行時に`NoMethodError: undefined method 'execute_cdp'`が発生する #7168

Closed Kassy0220 closed 7 months ago

Kassy0220 commented 8 months ago

概要

brew upgradeを実行して、chromedriverのformulaeを次のバージョンに更新しました。

% brew info chromedriver                                                                                                     +[feature/debug-flaky-tests-on-CI]
==> chromedriver: 120.0.6099.109
https://chromedriver.chromium.org/
/opt/homebrew/Caskroom/chromedriver/120.0.6099.109 (2 files, 15MB)

そして、test/system/comments_test:238を実行すると、「execute_cdpというメソッドはありません」というエラーが発生し、テストが失敗しました。

% PARALLEL_WORKERS=1 bin/rails test --seed 65250 test/system/attachments_test.rb test/system/comments_test.rb                 [feature/debug-flaky-tests-on-CI]
Running via Spring preloader in process 3791
Run options: --seed 65250

# Running:

...........[Screenshot Image]: /Users/kashiyamashintarou/Working/ScrumDevelopment/bootcamp/tmp/screenshots/failures_test_comment_url_is_copied_when_click_its_updated_time.png
E

Error:
CommentsTest#test_comment_url_is_copied_when_click_its_updated_time:
NoMethodError: undefined method `execute_cdp' for #<Selenium::WebDriver::Chrome::Driver:0x713e6f0760da2e8a browser=:"chrome-headless-shell">

    page.driver.browser.execute_cdp('Browser.setPermission', **cdp_permission)
                       ^^^^^^^^^^^^
Did you mean?  execute_script
    test/system/comments_test.rb:238:in `block in <class:CommentsTest>'

rails test test/system/comments_test.rb:228

.....................
[Minitest::CI] Generating test report in JUnit XML format...

Finished in 57.025516s, 0.5787 runs/s, 1.2626 assertions/s.
33 runs, 72 assertions, 0 failures, 1 errors, 0 skips

該当のテストは次の様になっています。

  test 'comment url is copied when click its updated_time' do
    visit_with_auth "/reports/#{reports(:report1).id}", 'komagata'
    find('#comments.loaded', wait: 10)
    first(:css, '.thread-comment__created-at').click
    # 参考:https://gist.github.com/ParamagicDev/5fe937ee60695ff1d227f18fe4b1d5c4
    cdp_permission = {
      origin: page.server_url,
      permission: { name: 'clipboard-read' },
      setting: 'granted'
    }
    page.driver.browser.execute_cdp('Browser.setPermission', **cdp_permission)
    clip_text = page.evaluate_async_script('navigator.clipboard.readText().then(arguments[0])')
    assert_equal current_url + "#comment_#{comments(:comment1).id}", clip_text
  end

再現手順

  1. chromedriverのバージョンをstableの120.0.6099.109[^1]に更新する(https://googlechromelabs.github.io/chrome-for-testing/)
  2. mainブランチに移動する
  3. bin/rails test test/system/comments_test.rb:238を実行し、NoMethodError: undefined method 'execute_cdp' for ...のエラーが発生することを確認する

期待される振る舞い

エラーが発生せずテストが通る。

環境

[^1]:2023/12/29時点でのstableですChrome_for_Testing_availability

Kassy0220 commented 8 months ago

この問題に関するIssueを発見しました。 [🐛 Bug]: undefined method `execute_cdp' on latest ChromeDriver 120.0.6099.71 · Issue #13319 · SeleniumHQ/selenium

Ok thought so. Change to --headless=new and it will work. Google changed the name of the old headless since it isn't actually chrome. Support for old headless will be in 4.17

Googleは古いヘッドレスの名前を変更したためこの問題が発生しているようで、解決するためにはオプションで—headless=newを指定すれば良いとあります。

次のようにオプションを追加してテストを実行すると、無事エラーは発生しませんでした。 (test/application_system_test_case.rb)

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  include LoginHelper
  include TestAuthHelper
  include StripeHelper
  include NotificationHelper
  include ReportHelper
  include CommentHelper
  include TagHelper
  include MockEnvHelper

  if ENV['HEADED']
    driven_by :selenium, using: :chrome
  else
    driven_by(:selenium, using: :headless_chrome) do |driver_option|
      driver_option.add_argument('--headless=new') # 追加
      driver_option.add_argument('--no-sandbox')
      driver_option.add_argument('--disable-dev-shm-usage')
    end
  end
% PARALLEL_WORKERS=1 bin/rails test --seed 65250 test/system/attachments_test.rb test/system/comments_test.rb                +[feature/debug-flaky-tests-on-CI]
Running via Spring preloader in process 7042
Run options: --seed 65250

# Running:

.................................
[Minitest::CI] Generating test report in JUnit XML format...

Finished in 62.280985s, 0.5299 runs/s, 1.1721 assertions/s.
33 runs, 73 assertions, 0 failures, 0 errors, 0 skips
Kassy0220 commented 8 months ago

ヘッドレスモードについて調査したので、記録として残しておきます。

2つのヘッドレスモード

ヘッドレスモードはChrome59(2017)から利用可能になったのですが、今年新しいヘッドレスモードが実装されました。 それぞれ主に次の特徴がある様です。

既存のヘッドレス

新しいヘッドレス

Chrome のヘッドレス モードがアップグレード: --headless=new  |  Chromium  |  Chrome for Developers

headless=newについて

Seleniumでは以下のオプションを指定することで、新しいヘッドレスモードを使うか既存のヘッドレスモードを使うか指定することができる様です。

Headless is Going Away! | Selenium

Kassy0220 commented 7 months ago

[ 記録1 ] 新しいヘッドレスChromeをテストで使用するように設定を変更したところ、CI上でいくつかのテストが落ちるようになってしまいました。

テストが落ちる原因について調査したところ、visit_with_authでユーザーを切り替える際にブラウザにセッション情報が残ったままになっていて、APIからのアクセスは1つ前のユーザーのセッションで動いていることが分かりました。 Flakyなテストの調査について

Kassy0220 commented 7 months ago

[ 記録2 ] テストが落ちないように、伊藤さんから教えていただいた修正案をpushしCIを実行したところ、テストの実行時間がとても長くなってしまう問題が発生してしましました。

この問題を解決するため、visit_with_authを修正するのではなくそのままにして、落ちるテストのみログアウト処理を実行することになりました。 Q&A: テスト時のログイン処理を修正したことで、CI上でテストの実行時間が長くなってしまう問題を解決したい | FBC

Kassy0220 commented 7 months ago

雑談・質問タイムで駒形さんに相談したところ、一旦この問題は置いておくことになりました。 https://bootcamp.fjord.jp/questions/1848#answer_7006

Issueは開いたままで良いとのことなので、そのままにしておきます。

komagata commented 7 months ago

:memo: --headless=oldを追加することで古いheadlessで動かすことができるPRがtrunkにマージされている。これを指定すれば新しいchromedriverでもこれまでのコードでテストが動く(あと速い)。 selenium-webdriverの最新(4.16.0)では入ってないがtrunkには入っている。 trunkを今すぐ入れてもいいがそこまで急いで無いので最新がリリースされたら入れる。

本来的には今後サポートされないCDPを使わないのが一番だが、推奨されているWebDriver BiDi APIも仕様はdraftで該当の機能は実装されていない。クリップボードのパーミッションの仕様はブラウザごとにバラバラで統一APIとしてはできそうにない。

長期に安定するテストにするならばそもそもクリップボードの中身をテストするのを辞めるのが良いかも。

komagata commented 7 months ago

Closed from #7347