Softknobs

Softknobs

How to do "top-down" LiveView rendering tests of nested LiveComponents

Hi,

I cannot figure out how to test some LiveComponents simulating the actual events that would be sent by the user.

In my case, the LiveComponent is not visible when the parent LiveView is rendered. I guess this is where the problem lies but it is required. To test my Liveview, I do:

{:ok, view, html} = live(conn, "/myview")

Doing the action to show the LiveComponent works:

render_click(view, "show", %{"param1" => "value1", "param2" => "value2"})

but I can only test the initial state of the LiveComponent because render_click only renders the html.

For that mater, I can’t use find_child to get the LiveComponent view because it is always nil:

find_child(view, "component_id") # returns nil

So I cannot send other render_* events to the LiveComponent.

It other LiveComponents I created, I used send to change the state in the parent view. This did work fine but in this case the rendering of LiveComponent is not handled in the same way and it would feel wrong to add an handle_info only for testing.

The last thing I looked into is render_component. Again, render_component seems to only render a definite state of the LiveComponent and that is not what I am looking for. Sure, testing that all different component states render what is expected is fine but there is no guarantee that the user actions on the UI set the component in the right state. Moreover, such tests would be hard to maintain: I want to test what is rendered when the user makes some actions, no mater what the implementation is (ie. the assigns state).

There are also other solutions like rendering the LiveComponent and hidding it with css but I am not confortable doing such design decisions only to solve testing problems.

I may not be familiar with all the tools available for testing and maybe I am missing something, that’s why I am asking for help here… Thanks!

Marked As Solved

shamanime

shamanime

To make this more clear I’ve setup a repo here: GitHub - shamanime/phx_live_component_test: Sample app to show how to test live components. · GitHub

It is a fresh Phoenix app created with --live with one LiveView that conditionally displays/renders a component.

I’ve used the same snipped I posted here to test the component: Add LV with conditional component and test LV interaction · shamanime/phx_live_component_test@a3b600b · GitHub

Also Liked

shamanime

shamanime

What is your exact issue and what approach are you taking?

Recent LV updates have improved a lot the test tooling.

Here’s a snippet to test LV with components that aren’t available at initial rendering:

{:ok, view, html} = live(conn, "path/to/lv")

# Component is not there
refute html =~ "some component text"

# click something that displays the component
assert view
           |> element("#button-id")
           |> render_click() =~ "some component text"

# interact with the component inside the parent LV
view
|> form("form[phx-submit=save]")
|> render_submit(%{
  "resource" => %{
    "description" => "some fake params"
  }
})

# test what happens after save
assert_patch(view, "some/other/path)
shamanime

shamanime

1: Instead of using live to “mount” your LV in testing you can use live_isolated/3.

2: The snippet I wrote in the other post should work. After you mount your menu LV you can interact with it using the element and render_ helpers to reproduce the steps taken for the component to display and to interact with it. This will work as an integration test: you won’t update the LV assigns directly, instead you’ll mimic what the user does and assert that your LV/Component is updating correctly.

So basically after your router renders and mounts the LV you’ll end with this structure:

<html from layout>
  <html from some_view.html.eex>
    <html from your side_bar LV>
      <html from your component inside the LV that may not be here>
      </html from your component inside the LV that may not be here>
    </html from your side_bar LV>
  </html from some_view.html.eex>
</html from layout>

When you use live_isolated you’ll get only this chunk:

<html from your side_bar LV>
  <html from your component inside the LV that may not be here>
  </html from your component inside the LV that may not be here>
</html from your side_bar LV>

And then you can interact with it by passing CSS selectors to element and using the render_ functions to trigger events in your LV and components.

shamanime

shamanime

The render_ helpers returns HTML so they can’t be piped to element, you need to do it step by step like this:

# this will display your component
view
  |> element("#menu_opener")
  |> render_click()

# you interact with it with the same `view` you mounted earlier
assert
  view
  |> element("#filter-input")
  |> render_change(%{term: "o"}) =~ "oo"

Where Next?

Popular in Questions Top

nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New

Other popular topics Top

axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48475 226
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement