ブラウザ確認でボタンをクリックしようとすると、RSpec Capybara が Selenium エラーをスローします

概要

Rails アプリケーションのエンドツーエンドの Capybara 仕様でエラーが発生しました。

私の見解の関連部分は次のとおりです。

#show.html.erb
...
<%= button_to 'Cancel',
  cancel_subscription_path,
  class: 'button button--danger',
  data: {
    turbo_confirm: 'Are you sure you want to cancel?',
    turbo_method: 'post'
  }
%>
<div id="cancel_message"></div>
...
# cancel_subscription_spec.rb

require 'rails_helper'

RSpec.describe 'CancelSubscriptions', js: true do
  it 'cancels an active subscription' do
    email = sign_up_and_subscribe

    visit subscription_path

    accept_confirm do
      click_button 'Cancel'
    end

    subscription = User.find_by(email:).current_subscription

    expect(subscription.cancelled?).to be(true)
  end
end

Sign_up_and_subscribe は、アプリケーションにサインアップし、Stripe フォームに記入してサブスクリプションを作成する単純なヘルパー メソッドです。これは他のテストでも機能し、Stripe ダッシュボードでサブスクリプションが作成されたことも確認できます。これは問題ではありません。

仕様が visit subscription_path に到達し、キャンセル ボタンをクリックしようとすると、次のエラーが表示されます。

Selenium::WebDriver::Error::UnexpectedAlertOpenError:  
  unexpected alert open: {Alert text : }
    (Session info: chrome-headless-shell=123.0.6312.87)

accept_confirm メソッドの使用法を誤っているのではないかと思いましたが、ドキュメントで見つけたところによると、これは正しい使用法です。

私が奇妙に思ったのは、仕様の accept_confirm とビューの Turbo_confirm をコメントアウトしても、実際にはエラー メッセージが変わらないことです。 Capybara はテスト実行の間にページをキャッシュしていますか?

この時点で仕様を通過させるために動作を変更することを検討します。ユーザー エクスペリエンスに小さな変更が生じる場合でも、十分にテストされたユーザー フローを使用することを好みますが、それすらできず、問題は残ります。同じ。

役立つ場合に備えて、rails_helper.rb の関連部分は次のとおりです。

Capybara.register_driver :chrome do |app|
  Capybara::Selenium::Driver.new app, browser: :chrome,
                                      options: Selenium::WebDriver::Chrome::Options.new(args: %w[headless disable-gpu])
end

Capybara.javascript_driver = :chrome

役立つ可能性がある場合は残りを投稿することもできますが、私が見る限り、ほとんど無関係です。

コントローラーでのキャンセルアクションは次のようになります。

def cancel
  @subscription = current_user.current_subscription
  SubscriptionCanceller.new(subscription_id: @subscription.provider_id).call
rescue SubscriptionCancellationError => _e
  render :cancellation_error
end

ここでは、cancel_error と cancel 自体の Turbo_stream ビューがあります。手動でテストすると、どちらもブラウザーで機能します。

影響を受ける場合に備えて、cancel.turbo_stream.erb を次に示します。

<%= turbo_stream.replace 'cancel_message' do %>
  <div>We are cancelling your subscription.</div>
<% end %>

さらに詳細やコードが必要な場合は、お知らせください。投稿させていただきます。

解決策

問題はわかりました。問題の仕様の問題ではありませんでしたが、動作は予期していませんでした。

テスト対象のページが依存する基礎となるデータが存在しませんでした。これは、他の仕様で同じ Stripe フォーム入力機能をテストしましたが、それらのテストで行われたアサーションにより、アプリケーションがそれらが true になるまで待機するようになったためです。ただし、このテストでは、Stripe API からの結果が保存される前にページが visit subscription_path でリダイレクトされたため、エラーが発生しました。

テストで発生したエラーは、開いているはずのないダイアログ ボックスが開いていると信じ込ませたので、私をイライラさせました。一方、実際の問題は、開いているはずのダイアログ ボックスが開いていないことでした。サブスクリプション ページが空で表示されていたため、キャンセル ボタンがページに表示されませんでした。

テストは次のようになります。

RSpec.describe 'CancelSubscriptions', js: true do
  it 'cancels an active subscription' do
    email = sign_up_and_subscribe

    expect(page).to have_current_path('/subscription/success', ignore_query: true) # making sure the Stripe return_url was hit and so the data is saved in my DB

    click_on 'dropdown-toggle'
    click_on 'Subscription'
    expect(page).to have_content 'Your subscription'

    accept_confirm do
      click_button 'Cancel'
    end
    expect(page).to have_content 'We are cancelling your subscription.'

    subscription = User.find_by(email:).current_subscription
    expect(subscription.cancelled?).to be(true)
  end
end

要点: Capybara/Selenium で確認またはプロンプトダイアログボックスを受け入れるかどうかをアサーションする前に、ページが期待どおりにレンダリングされていることを確認してください。そうしないと、予期しないエラーが発生する可能性があります。