Turbo Stream broadcasts happening before turbo_stream_from can establish websocket connection in System tests with Capybara

I recently added turbo_streams to a feature and I was trying to test the live updating. It 100% works in the browser while manually testing. That said, when I attempt to test it in a system test a weird behavior seems to be happening. Here’s the test

visit handwritten_design_path(design)

expect(page.find('#front_preview')['src']).to match(/loading/)
expect(page.find('#back_preview')['src']).to match(/loading/)

front_url = 'https://via.placeholder.com/150?text=front'
back_url = 'https://via.placeholder.com/150?text=back'

expect(page).not_to have_selector('img[data-async-image-url-value="https://via.placeholder.com/150?text=front"]')
expect(page).not_to have_selector('img[data-async-image-url-value="https://via.placeholder.com/150?text=back"]')

# adding sleep to allow turbo_stream the ability to load before the broadcast happens.
# without this, broadcasting happens before the  ws connection is established.
# sleep 0.25

design.update(front_image_url: front_url, back_image_url: back_url)

design.broadcast_replace_to(
  design,
  :previews,
  target: "design_#{design.id}_previews",
  partial: 'dashboard/handwritten_designs/previews'
)

expect(find('img[data-async-image-url-value="https://via.placeholder.com/150?text=front"]'))
expect(find('img[data-async-image-url-value="https://via.placeholder.com/150?text=back"]'))

I originally thought that this was a capybara waiting problem but apparently, this flow results in broadcast firing before turbo_stream_from can establish the connection. (notice the broadcast log happening before the connection registered)

Adding a sleep 0.25 does solve the problem consistently.

I’m a bit stumped on what to do next. Does anyone have any ideas? (Obviously I can add the sleep I just wonder if there are any better ideas)

This problem seems similar to these but slightly different from

1 Like

I’m also facing this issue.
Have you found a better solution than adding sleep ?

You could change it to test the original flow of the page by filling out the form and submitting to the controller that way it will have to load the page first and it will more accurately test the functionality

In my case changing the flow won’t fix it.

I’ll try to explain the kind of test I’m doing:
Let suppose there is an open ticket in the database (setup through fixtures)
In the test steps are:

  1. Visit the dashboard
  2. Close the ticket (via api, email, which triggers a broadcast)
  3. Check that new ticket state is broadcasted

Before starting step 2 I’d like to use a helper to wait for actionCable to connect.

You could do an assertion for the turbo-cable-stream-source element to appear on the page

I have yet to find a better solution :slightly_frowning_face: That said, as we’re continuing to add more and more Turbo streams in we are hitting more and more random test failures like this. So we’re going to have to come to a solution at some point. Can’t just continue to sleep the for all of our system specs

This has been the main solution we’ve decided good be the best solution at this point. That said, it feels so annoying to check for this in all of these tests. It goes back to that dirty feeling that I’m just writing code to make tests pass. Again it’s fine if that’s the only solution, but doesn’t feel great imo.