Problem
visit uses browsingContext.navigate with wait: "complete" which waits for the HTML load event. But LiveSocket connects after load via a WebSocket handshake initiated by JavaScript.
After visit returns, data-phx-link links and phx-click bindings aren't active yet. Clicks that rely on LiveSocket (e.g. <.link navigate=...>) fall through to plain HTML behavior.
Impact
session |> visit("/auth/callback") # → redirects to /student
# LiveSocket not yet connected
session |> click(Query.link("check-in-link")) # <.link navigate=...>
# Falls through to plain <a href> click — full page nav, not LiveView nav
session |> assert_has(Query.css("span", text: "Start Session"))
# Fails — page didn't navigate as expected
Suggested fix
After browsingContext.navigate completes, call await_liveview_connected if the page has a LiveSocket:
def visit(session, url) do
context = browsing_context(session)
{method, params} = Commands.navigate(context, url)
case send_bidi(session, method, params) do
{:ok, _} ->
# Wait for LiveSocket to connect (if present)
await_liveview_connected(session)
:ok
error -> error
end
end
The await_liveview_connected JS already handles the case where there's no LiveSocket (times out after 5s and returns false). But for the common case (LiveView page), it would wait ~100ms for the connection, eliminating the race.
Problem
visitusesbrowsingContext.navigatewithwait: "complete"which waits for the HTMLloadevent. But LiveSocket connects after load via a WebSocket handshake initiated by JavaScript.After
visitreturns,data-phx-linklinks andphx-clickbindings aren't active yet. Clicks that rely on LiveSocket (e.g.<.link navigate=...>) fall through to plain HTML behavior.Impact
Suggested fix
After
browsingContext.navigatecompletes, callawait_liveview_connectedif the page has a LiveSocket:The
await_liveview_connectedJS already handles the case where there's no LiveSocket (times out after 5s and returns false). But for the common case (LiveView page), it would wait ~100ms for the connection, eliminating the race.