RSpec Capybara throwing Selenium error when trying to click a button with browser confirm

30 Views Asked by At

I'm running into an error with an end-to-end Capybara spec in my Rails application.

Here is the relevant part of my view:

#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 is just a simple helper method that signs up to the application and fills in the Stripe form to create a subscription - it works in other tests and I also see the subscription created in the Stripe dashboard, this isn't the issue.

When the spec reaches visit subscription_path and attempts to click the cancel button, I get the following error:

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

I figured I was misusing the accept_confirm method, but from what I found in the documentation, this is the correct usage.

What I found strange was that commenting out the accept_confirm in the spec and the turbo_confirm in the view didn't actually change the error message. Is Capybara caching the page in between test runs?

I would consider changing the behaviour to make the spec pass at this point, I prefer to have a well-tested user flow even if that means a small change in the user experience, but I can't even do that, the issue remains the same.

In case it's helpful, the relevant part of the rails_helper.rb is:

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

I can post the rest if it might be useful, but it's mostly unrelated as far as I can see.

My cancel action in the controller looks like this:

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

where I have turbo_stream views for cancellation_error and cancel itself, both of which work in the browser when I test them manually.

Just in case that affects it, here is the cancel.turbo_stream.erb

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

If there is any further detail/code required, let me know and I'll post it.

1

There are 1 best solutions below

0
Michael Evans On

I've figured out the issue, and it wasn't an issue with the spec in question, although the behaviour was unexpected.

The underlying data that the page under tests relies on was not present. That's because in other specs I tested the same Stripe form-filling functionality and the assertions made in those tests made the application wait until they were true. In this test, however, the page was redirected with visit subscription_path before the result from the Stripe API were saved, thus causing an error.

The error raised in the test threw me off because it led me to believe that there was a dialogue box open that should not have been, whereas the actual issue was that no dialogue box was open even though it was expecting one to be. The cancel button was not present on the page because the subscription page was showing empty.

The test now looks like:

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

Takeaway: make sure the page is rendered as expected before making assertions on accepting confirmation or prompt dialogue boxes with Capybara/Selenium. Not doing can cause unexpected errors.