I have a LiveView that tracks how many users are viewing another page. The other page is using Presence to track when users enter and leave. Everything works fine during manual testing.
For my tests, I need to simulate that a user has left the page (closed tab, clicked a link, etc.) so that they no longer appear in the list of users being tracked. I am unable to simulate such an event in my Elixir test code.
In my Elixir test code, how can I simulate that a LiveView user has exited the page?
EDIT: I can use render_click/2 to click a link that happens to be in the view and that does the trick for now, but hopefully there’s something a little less hacky that I can use.
It’s a really long test, so there’s a lot of cruft. I tried making a barebones version and it’s not crashing the whole test anymore. I’ll poke around for a bit and see if I can find out what is causing the issue.
Thanks for the hint. I have to make dinner so I’ll be back with more info in a bit.
It looks like killing the LiveView process causes the test to crash pretty much immediately. This example will crash:
test "test crash example", %{conn: conn, quiz: quiz} do
{:ok, view, html} = conn |> live(~p"/contact-us")
Process.exit(view.pid(), :kill)
Process.sleep(1)
end
I’m in over my head on this one. Until I can figure out how to gracefully end the LiveView processes, I’m just gonna stick to my simulated link-clicking hack.
Here’s a brief version of the test I’ve been working with (it’s for a toy project where multiple users can take a quiz while a teacher supervises the results):
test "renders expected presence data as the user progresses through the quiz", %{
conn: conn,
quiz: quiz
} do
{:ok, quiz_stats_view, html} = conn |> live(_get_quiz_stats_url(quiz.id))
# quiz_stats: template contains expected content before user joins the quiz lobby
assert html =~ "No users are preparing to take this quiz."
## quiz_take: unauthenticated user joins the quiz lobby
{:ok, quiz_take_view, _html} = build_conn() |> live(_get_quiz_take_url(quiz.id))
# quiz_stats: template no longer contains initial content after receiving Presence data broadcast
Process.sleep(5)
user_joined_html = render(quiz_stats_view)
refute user_joined_html =~ "No users are preparing to take this quiz."
# quiz_stats: template contains placeholder text for unauthenticated user with no name
assert html_element_has_content(
user_joined_html,
~s|[data-test-id="users-not-yet-started"]|,
"No name yet"
)
## quiz_take: user leaves the page (hacky, but it works)
# quiz_take_view |> element(~s|a|, "Exit this quiz") |> render_click()
## quiz_take: user leaves the page (not hacky, but crashes the test)
Process.exit(quiz_take_view.pid(), :normal)
# quiz_stats: template no longer contains information about this user
Process.sleep(5)
user_exited_html = render(quiz_stats_view)
refute user_exited_html =~ "No name yet"
end
To recap: Process.exit(quiz_take_view.pid(), :kill) crashes the entire test, and Process.exit(quiz_take_view.pid(), :normal) appears to have no effect whatsoever (even after a lengthy Process.sleep(30_000)). Simulating a link being clicked seems to shut down the view gracefully (thus the test can complete successfully), but I have no idea what’s going on behind the scenes (I’ve looked, but there’s a lot going on that goes above my head).
The issue is that two things are started. A LV client and a channel for communication. Both seem to be linked to the test process. Most of that stuff is private though. stop_supervised(view.pid) seems to work, though with a warning. I’d argue that LV should provide an API to close a view.