Softknobs

Softknobs

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!

Showing Posts 1 to 10

phasnox

phasnox

I have a similar problem where I want to test a nested live_view that does not mount in router.(with mount :not_mounted_in_router)

I guess those are the caveats of the bleeding edge (sigh)

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

phasnox

Hey @shamanime !

Here is what I have:

A “normal” get view like this:

routex.ex

get "/someview", SomeController, :index

In some_view.html.eex template

<div>@inner_content</div>
<div>
  <%= live_render(@conn, SidebarViewLive, session: %{}) %>
</div>

sidebar_view_live.ex

defmodule SidebarViewLive do
  use MyApp, :live_view
  def mount(:not_mounted_at_router, params, socket), do: stuff
end

So 2 questions:

  1. How do I test SidebarViewLive?
  2. How would I test events in any LiveComponent inside that live view?

Thanks for taking the time man!

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.

phasnox

phasnox

@shamanime What about multi-step actions?

Lets say I want to test something like this:

     html = view
            |> element("#menu_opener")
            |> render_click()
            |> element("#filter-input")
            |> render_change(%{term: "o"})
     assert html =~ "oo"

The problem is I have the live component hidden, and only shown after the user clicks the menu opener.

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"
Softknobs

Softknobs OP

Thanks @shamanime for your input. I haven’t had the occasion to write tests in LV 0.13 yet but it looks the original problem remains.

As @phasnox mentionned the requirement here is to test a nested LiveComponent.

Your last example pinpoints my original problem. The problem is that #filter-input does not exist in the view because it is only rendered when #menu_opener is clicked. @phasnox last example shows exactly what we are trying to test but that can’t be done because, as you mention it, render_ returns HTML and not a view that could be used to do additionnal render_click calls.

There is no way to get a view struct that would be the result of the render_click on #menu_opener. Here view only contains original code of the LiveView.

Since live/2 only works for LiveViews, as does live_isolated/3, they cannot be used to test LiveComponents. And there seems to be no way to test UI events on LiveComponents outside of a LiveView.

At this point it looks that it is not possible to test LiveComponents by simulating the user actions on LiveComponents that are not available when the LiveView is created.

shamanime

shamanime

Hello!

There are no issues testing live components. My snippet shows how to test them.
I work with an app that has this exact issue you’re describing and I am able to test the component.

Have you tried adding tests as I mentioned?

You need to get them to show by interacting with your LiveView and from there you interact with your component.

Let me expand it so you are sure I understand what you’re saying:

# mount the LiveView, this is from a Route but you can also use live_isolated.
{:ok, view, _html} = live(conn, /path/to/main/lv)

# make sure the **component** is not displayed
# at this point your LV is mounted but the component is not present
refute view |> element("#filter-input") |> has_element?

# click a button in your LV that will display the component
view
  |> element("#menu_opener")
  |> render_click()

# make sure your **component** is displayed after the click
assert view |> element("#filter-input") |> has_element?

# interact with it with your **component**
assert
  view
  |> element("#filter-input")
  |> render_change(%{term: "o"}) =~ "oo"
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

Softknobs

Softknobs OP

Thanks for the detailed answer @shamanime. To be honest, I did not try the example you gave because it was equivalent to what I was doing, without success, before posting this two months ago.

I don’t have the exact code that caused the initial problem anymore and I upgraded my LV version several times since then. Anyway, it would make no sense trying to reproduce the problem now when your solution does work with current LiveView version.

Thanks for taking the time to answer.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews