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.

1 Like

Hey there, has this been working for you to avoid flaky tests?

The solution I did was return turbo_frame in the response on the action and trigger broadcast. This seems to make things work a bit better since it doesn’t rely on the turbo stream to be setup.

I’ll likely ask if this is okay in another thread.

I posted my experience dealing with this problem here: Returning turbo_frame and broadcasting is a legit way to speed up performance? - #2 by crespire - I know it’s over a year now, but curious if you ended up coming up with another solution!

has this been working for you to avoid flaky tests?

All the solutions that I provided or tried above unfortunately did not work out well and were still prone to intermittent failures. Other than adding the sleep if I remember correctly (I’ve moved on from day-to-day on that project).

The solution I did was return turbo_frame in the response on the action and trigger broadcast. This seems to make things work a bit better since it doesn’t rely on the turbo stream to be setup.

Interesting, and you’d say that it consistently works? I read through your other post but I’ll be honest I think I’d need a bit more context with the code. For example, was that just a partial being rendered in another view or something?

Your comment prompted me to look deeper. It turns that it wasn’t that I added a process jobs to the test, it was that I added a turbo_stream_from on my component under test.

My context is that I am system testing a viewcomponent, and I had to render that view component in a template in order to do some stuff (it’s a bootstrap modal, so I had to programmatically display it after render). For me, it was the case that in a test context, there was no turbo stream connected in the view, so my controller broadcasts were being shot into space.

I was able to get the test working without using the jobs helper. Not sure if that is applicable for your situation. Unfortunately for us, we are using a few libraries on the frontend for this feature that aren’t Rails native so having those libraries support Turbostreams was more work than using broadcasts.