Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: live_select LiveView component testing #37

Open
lockhinator opened this issue Sep 7, 2023 · 5 comments
Open

Discussion: live_select LiveView component testing #37

lockhinator opened this issue Sep 7, 2023 · 5 comments

Comments

@lockhinator
Copy link

First off I would like to say great job on the library, it is extremally useful.

I had a question around testing when being used within a LiveView component. While the directive of phx-target={@myself} works on the actual <.live_select /> element when being used in the live environment, when running this in a test environment the event messages are sent to the parent LiveView.

I in no way think this is a short coming of the library in anyway and understand this is something that still needs to be fleshed out in LiveView testing methodologies but was wondering if anyone has come up with a clever solution for this or if at the moment most are just not testing the live_select component in their views and are instead direct testing their components.

as an example:

Component:

defmodule MyAppWeb.PageLive.FormComponent do
  use MyAppWeb, :live_component

  def render(assigns) do
    ~H"""
      <.simple_form for={@form} id="page-form" phx-target={@myself} phx-change="validate" phx-submit="save">
        <.input field={@form[:name]} type="text" label="Name" />
        <.live_select field={@form[:cities]} phx-target={@myself} placeholder="Search Cities..." mode={:tags} />
        <:actions>
          <.button phx-disable-with="Saving...">Save Cities</.button>
        </:actions>
      <./simple_form>
    """
  end

  ...

  @impl true
  def handle_event("live_select_change", %{"field" => "cities_tags", "text" => text, "id" => live_select_id}, socket) do
    ...
    send_update(LiveSelect.Component, id: live_select_id, options: result)
    {:noreply, socket}
  end
end

Test:

  ... include the helper (with a few minor modifications to selectors) from this lib since it makes life much easier

  test "can save cities", %{conn: conn} do
    {:ok, new_live, _html} = live(conn, ~p"/cities/new")

    type(new_live, "ala",
        component: "#cities_live_select_component",
        field: "cities_tags"
      )

    select_nth_option(new_live, 1,
        method: :key,
        component: "#cities_live_select_component"
      )

    assert new_live
             |> form("#page-form",
               cause: %{
                 "name" => "A Fake Title Name"
               }
             )
             |> render_submit()
  end

This results in the following error which you won't get in when using this from the browser:
Assume MyAppWeb.PageLive is the LiveView which is hosting this in modal or something similar.

  ** (UndefinedFunctionError) function MyAppWeb.PageLive.handle_event/3 is undefined or private

If anyone has any ideas on how to test in the scenario or any pointers it would be very much appreciated.

@lockhinator lockhinator changed the title Discussion: live_select LiveView Component Testing Discussion: live_select LiveView component testing Sep 7, 2023
@maxmarcon
Copy link
Owner

I in no way think this is a short coming of the library in anyway and understand this is something that still needs to be fleshed out in LiveView testing methodologies but was wondering if anyone has come up with a clever solution for this

LiveView test functions (such as render_click) honor the phx-change attribute. This is not the problem here.

Are the type and select_nth_option functions I see in your code the same functions I use in my test suite? If so, you probably see the error when calling type. type needs to know which component it should pass the live_select_change event to.

Try to pass the parent to type:

type(new_live, "ala",
        component: "#cities_live_select_component",
        parent: "#page-form",
        field: "cities_tags"
      )

But beware that you need a phx-hook (name doesn't matter) set in #page-form for this to work, because type uses a render_hook internally.

More importantly, these functions are not a public API and you're not supposed to be using them unless you're working on the LiveSelect code itself.

Which leads me to the most important question: what are you testing here? It seems to me that you're testing live_select component itself, which you shouldn't and don't need to (it's already tested).

A minor point:

 assert new_live
             |> form("#page-form",
               cause: %{
                 "name" => "A Fake Title Name"
               }
             )
             |> render_submit()

This assertion is meaningless and will always pass

@pau-riosa
Copy link

pau-riosa commented Jan 18, 2024

It seems I encounter this one. I have a question

When live-select is inside the component, how can I test the I have selected the option/s that I've search?

@Munksgaard
Copy link
Contributor

Which leads me to the most important question: what are you testing here? It seems to me that you're testing live_select component itself, which you shouldn't and don't need to (it's already tested).

Not the guy you're asking, but I've been trying to figure out how to test that my form using live_select works. For instance, I have this code:

      <.simple_form
        for={@form}
        id="foo-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >
        <.live_select
          field={@form[:id]}
          label={gettext("Foo")}
          phx-target={@myself}
        />
        <:actions>
          <.button phx-disable-with={gettext("Saving...")}><%= gettext("Save") %></.button>
        </:actions>
      </.simple_form>

And a handle_event that looks like this:

  def handle_event("live_select_change", %{"id" => live_select_id, "text" => text}, socket) do
    foos =
      text |> Foos.search() |> Enum.map(&value_mapper/1)

    send_update(LiveSelect.Component, id: live_select_id, options: foos)

    {:noreply, socket}
  end

I would like to add a test to my test suite that I can render the form, that the user can input something, that the correct search results show up and that I can add the one that I want and click save. Here's what I've tried to do:

  test "add new foo", %{conn: conn} do
    foo = foo_fixture(%{name: "my foo"})

    {:ok, lv, html} = live(conn, ~p"/foos/add")

    assert lv
           |> form("#foo-form",
             foo: %{id: "#{foo.id}"}
           )
           |> render_change() =~ "my foo"
  end

But that just gives me the following error:

** (ArgumentError) value for hidden "foo[id]" must be one of [""], got: "1523"

How do you suggest I properly test my user flows that contain live_select?

@maxmarcon
Copy link
Owner

maxmarcon commented Jun 8, 2024

** (ArgumentError) value for hidden "foo[id]" must be one of [""], got: "1523"

LiveSelect uses a hidden input for the value in the form. You cannot send hidden inputs using form/3 because that function mimics what a real user can do.

Instead, send the value for id in render_submit/2 or render_change/2, as suggested in the docs

I would like to add a test to my test suite that I can render the form, that the user can input something, that the correct search results show up and that I can add the one that I want and click save. Here's what I've tried to do:

This is a good test to have, but I don't know if LiveView tests are the right place for it. LiveSelect relies heavily on JS hooks to work, and LiveView tests don't execute Javascript. So you'll end up with something that doesn't really give you the confidence that everything is really working end to end.

For such a test I would use an e2e testing framework like Cypress. There are many others, and I think some integrate well with LiveView, for example I just googled and I found this one that integrates Cypress with LV.

I've been thinking of adding an e2e test myself to the LiveSelect test suite, but I didn't find the time yet. That test would give me the ultimate confidence that everything is working.

@Munksgaard
Copy link
Contributor

Thank you @maxmarcon for the detailed answers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants