arcanemachine

arcanemachine

Testing LiveView and Presence: How to simulate a user has left (e.g. closed tab)

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.

Marked As Solved

arcanemachine

arcanemachine

Finally got it! GenServer.stop(view.pid()) did the trick.

Also Liked

LostKobrakai

LostKobrakai

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.

adw632

adw632

To simulate a user being unauthenticated and disconnected in all liveviews and channels you can do:

MyAppWeb.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})

This covered in the documentation here Disconnecting all instances of a live user.

sodapopcan

sodapopcan

You could always just kill the process:

{:ok, lv, _html} = live(conn, ~p"/the-page")

Process.exit(lv.pid, :kill)

If there is a cleaner way I am not sure.

arcanemachine

arcanemachine

Using :normal does not appear to have any effect.

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).

Tested on Elixir 1.15.5-otp-26 on Linux.

Where Next?

Popular in Questions Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New

We're in Beta

About us Mission Statement