Testing click-away event in LiveView

Hi all

I am working on a table component with an inline editing feature. When you click on a cell, the content is replaced with an input to edit the value. To cancel the editing, I use phx-click-away to fire the respective event.

I want to unit test this feature but found no obvious way to fire the phx-click-away.

I tried render_click/2 with a random element of the live view but this does not work, when the target does not have a phx-click binding.
There is render_click/3 but that targets the liev view and not the component.

Is there a way to emit a phx-click-away in testing?

1 Like

If the phx-click-away is being handled by a LiveView (not a LiveComponent), I have been able to test it with the render_hook API.

render_hook(view, "date_selector_blur", params_for_event)

Params are most likely %{} for a click-away.

This is not ideal in that it doesn’t interact with the element throwing the event; it simulates it with the live view directly and re-renders the view accordingly. But this is how we have been having to trigger the validate event on forms as well.

Having the same challenge…

Here’s an isolated example:

defmodule TestLiveView do
  use Phoenix.LiveView

  def mount(_, _, socket) do
    {:ok, socket |> assign(form: to_form(%{}, as: "search"))}
  end

  def render(assigns) do
    ~H"""
    <div>
      <button id="outside" type="button" phx-click="noop">Outside</button>
      <div id="search-form-container" phx-click-away="clear_search_form">
        <.form for={@form} id="search-form">
            <.input field={@form[:query]} type="text" phx-change="search_form_update" />
        </.form>
      </div>
    </div>
    """
  end

  def handle_event("clear_search_form", _params, socket) do
    {:noreply, socket |> assign(form: to_form(%{}, as: "search"))}
  end

  def handle_event("search_form_update", params, socket) do
    updated_search_form =
      params["search"]
      |> to_form(as: "search")

    {:noreply, socket |> assign(form: updated_search_form)}
  end

  def handle_event("noop", _, socket) do
    {:noreply, socket}
  end
end

defmodule FooWeb.SearchTest do
  @moduledoc false
  use FooWeb.ConnCase, async: true

  import Phoenix.LiveViewTest

  test "clears the search term on click away", %{conn: conn} do
    {:ok, lv, _html} = live_isolated(conn, TestLiveView)

    render_change(element(lv, "#search-form input"), %{"search" => %{"query" => "asdf"}})

    html = render_click(element(lv, "#outside"))

    refute html =~ "asdf"
  end
end

When we run the test, we see the live view receives the “noop” event (from clicking the “outside” button)…
… but the live view does not receive the “clear_search_form” event.

Also, tried to find a way to send a click-away event instead of a click event, but LiveViewTest doesn’t seem to expose a function to do so.

I was able to figure out how to trigger these click events. I mentioned previously they could be triggered directly against the view with render_hook (provided the event handler exists on the live_view).

I was encountering a problem where moving logic to a shared component broke the existing tests. I was able to get past this with the with_target function, where you can pass an ID for the primary div in a LiveComponent, and trigger an event with a specific target component. This allows me to trigger a click away.

Hope this helps, it got me unblocked on this issue.

    view
    |> with_target("#live-component-with-event-handler")
    |> render_hook("given-click-away-name", %{})
1 Like