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
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
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
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
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"
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









