Turbo Stream redirects fail in headless mode when multiple modal tests run together
Environment
- Apparition: 0.6.0
- Capybara: 3.37.1
- Rails: 7.0.4
- Turbo Rails: 1.3.2
- Ruby: 3.0.6
- Platform: macOS (ARM64 Darwin 24.6.0)
Description
When running multiple RSpec feature tests that test modals with Turbo Stream form submissions, the tests pass individually but fail when run together only in headless mode. With headless: false, all tests pass even when run together.
The modals themselves open correctly in both tests. However, the second test's form submission and Turbo Stream redirect fail to work properly in headless mode. The issue appears to be related to JavaScript/Turbo event handler state pollution between tests in headless Chrome. The first test leaves residual browser state that prevents the second modal's form submission from triggering the Turbo Stream redirect.
Expected Behavior
Tests using Turbo Stream redirects in modals should work consistently in both headless and non-headless modes, with proper test isolation between consecutive test runs.
Actual Behavior
In headless mode (headless: true):
- First modal test: ✅ Passes (modal opens, form submits, redirect works)
- Second modal test: ❌ Fails
- Modal opens correctly ✅
- Form submission doesn't trigger properly ❌
- Turbo Stream redirect doesn't occur ❌
- Test stays on the current page instead of redirecting
In non-headless mode (headless: false):
- First test: ✅ Passes
- Second test: ✅ Passes
Running individually:
- Both tests pass in both modes
Reproduction Steps
-
Create two similar feature tests that:
- Open a modal with a unique ID (e.g.,
#modal_a, #modal_b)
- Submit a form inside the modal using Turbo
- Expect a Turbo Stream redirect response from the controller
-
Configure Apparition with headless: true
-
Run both tests together: bundle exec rspec spec/features/first_modal_spec.rb spec/features/second_modal_spec.rb
Result: Second test fails with redirect not happening
- Change to
headless: false and run again
Result: Both tests pass
Minimal Test Code
# spec/features/first_modal_spec.rb
RSpec.feature 'First Modal Test', type: :feature do
let(:resource_a) { create(:resource_a) }
before do
visit resource_a_path(resource_a)
end
describe 'modal with turbo stream redirect', js: true do
scenario 'submits form and redirects' do
# Open modal
find('#open-modal-a').click
# Wait for modal to appear
expect(page).to have_css('#modal_a.show', wait: 5)
within '#modal_a' do
click_button 'Submit'
end
# Should redirect via Turbo Stream
expect(page).to have_current_path(index_path, wait: 10)
end
end
end
# spec/features/second_modal_spec.rb
RSpec.feature 'Second Modal Test', type: :feature do
let(:resource_b) { create(:resource_b) }
before do
visit resource_b_path(resource_b)
end
describe 'modal with turbo stream redirect', js: true do
scenario 'submits form and redirects' do
# Open modal
find('#open-modal-b').click
# Wait for modal to appear
expect(page).to have_css('#modal_b.show', wait: 5)
within '#modal_b' do
click_button 'Submit'
end
# FAILS HERE in headless mode when run after first test
expect(page).to have_current_path(index_path, wait: 10)
# Error: expected "/resource_b/123" to equal "/index"
end
end
end
Controller Code (Turbo Stream Response)
def destroy
@resource.destroy
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.action(:redirect, index_path)
end
end
end
Capybara Configuration
Capybara.register_driver :apparition do |app|
Capybara::Apparition::Driver.new(app, {
headless: true, # Tests fail when true, pass when false
window_size: [1400, 1400],
browser_options: {
'no-sandbox' => nil,
'disable-gpu' => nil,
'disable-dev-shm-usage' => nil
},
inspector: true,
js_errors: true
})
end
Capybara.javascript_driver = :apparition
Capybara.default_max_wait_time = 5
What We've Tried
- ✅ Adding explicit waits for modals and redirects
- ✅ Using unique modal IDs to prevent conflicts
- ✅ Adding
window.location.reload(true) between tests
- ✅ Using
requestSubmit() directly on forms
- ✅ Adding cleanup
after blocks
- ✅ Capturing camera/subscription IDs before deletion
None of these workarounds fixed the issue in headless mode.
Current Workaround
We're currently using headless: false as a workaround, but this is not ideal for CI/CD environments where headless execution is required.
headless: ENV.fetch('HEADLESS', 'false') == 'true'
Additional Context
- Important: The modals open correctly in both tests. The issue is specifically with the form submission/redirect behavior in the second test.
- The issue only occurs with Turbo Stream form submissions that trigger redirects
- It appears to be Turbo event handler state pollution specific to headless Chrome
- Regular form submissions (non-Turbo) work fine
- The modals work perfectly in development (manual testing)
- Both tests pass when run with
--order defined or individually
- When examining the second test in headless mode, clicking the submit button appears to do nothing (no form submission is triggered)
Questions
- Is there a known issue with Turbo/Stimulus event handlers being cached between page visits in headless mode?
- Does Apparition need special cleanup for Turbo-enabled applications?
- Is there a way to force a complete browser state reset between tests in headless mode?
Any guidance would be greatly appreciated! Happy to provide more details or a full reproduction repository if needed.
Turbo Stream redirects fail in headless mode when multiple modal tests run together
Environment
Description
When running multiple RSpec feature tests that test modals with Turbo Stream form submissions, the tests pass individually but fail when run together only in headless mode. With
headless: false, all tests pass even when run together.The modals themselves open correctly in both tests. However, the second test's form submission and Turbo Stream redirect fail to work properly in headless mode. The issue appears to be related to JavaScript/Turbo event handler state pollution between tests in headless Chrome. The first test leaves residual browser state that prevents the second modal's form submission from triggering the Turbo Stream redirect.
Expected Behavior
Tests using Turbo Stream redirects in modals should work consistently in both headless and non-headless modes, with proper test isolation between consecutive test runs.
Actual Behavior
In headless mode (
headless: true):In non-headless mode (
headless: false):Running individually:
Reproduction Steps
Create two similar feature tests that:
#modal_a,#modal_b)Configure Apparition with
headless: trueRun both tests together:
bundle exec rspec spec/features/first_modal_spec.rb spec/features/second_modal_spec.rbResult: Second test fails with redirect not happening
headless: falseand run againResult: Both tests pass
Minimal Test Code
Controller Code (Turbo Stream Response)
Capybara Configuration
What We've Tried
window.location.reload(true)between testsrequestSubmit()directly on formsafterblocksNone of these workarounds fixed the issue in headless mode.
Current Workaround
We're currently using
headless: falseas a workaround, but this is not ideal for CI/CD environments where headless execution is required.Additional Context
--order definedor individuallyQuestions
Any guidance would be greatly appreciated! Happy to provide more details or a full reproduction repository if needed.