test/support/live_view_helpers.ex

defmodule ExshomeTest.LiveViewHelpers do
  @moduledoc """
  API to help testing live views.
  """

  @endpoint ExshomeWeb.Endpoint
  import Phoenix.ConnTest
  import Phoenix.LiveViewTest

  @doc """
  Renders a live app page and returns a view. Raises when something bad happens.
  """
  @spec live_with_dependencies(Plug.Conn.t(), module(), atom()) :: Phoenix.LiveViewTest.View
  def live_with_dependencies(%Plug.Conn{} = conn, app_module, action)
      when is_atom(app_module) and is_atom(action) do
    start_dependencies(app_module, action)

    {:ok, view, _html} = live(conn, app_module.path(conn, action))

    view
  end

  @doc """
  Renders a preview for app page and returns a view. Raises when something bad happens.
  """
  @spec live_preview(Plug.Conn.t(), module()) :: Phoenix.LiveViewTest.View
  def live_preview(%Plug.Conn{} = conn, app_module) when is_atom(app_module) do
    module = app_page(app_module, :preview)
    {:ok, view, _html} = live_isolated(conn, module, [])

    view
  end

  @doc """
  Starts dependencies and renders a preview for app page.
  Returns a view. Raises when something bad happens.
  """
  @spec live_preview_with_dependencies(Plug.Conn.t(), module()) :: Phoenix.LiveViewTest.View
  def live_preview_with_dependencies(%Plug.Conn{} = conn, app_module)
      when is_atom(app_module) do
    start_dependencies(app_module, :preview)
    live_preview(conn, app_module)
  end

  @doc """
  Extracts value from input element by selector.
  """
  def get_value(view, selector) do
    [value] = view |> render() |> Floki.attribute(selector, "value")
    value
  end

  @spec start_dependencies(module(), atom()) :: :ok
  def start_dependencies(app_module, action)
      when is_atom(app_module) and is_atom(action) do
    supervised_dependencies =
      app_module
      |> app_page(action)
      |> then(& &1.dependencies())
      |> Keyword.keys()
      |> Enum.into(MapSet.new())
      |> Enum.filter(&function_exported?(&1, :child_spec, 1))

    for dependency <- supervised_dependencies do
      ExshomeTest.TestRegistry.start_dependency(dependency)
    end

    :ok
  end

  defp app_page(app_module, :preview), do: app_module.preview()

  defp app_page(app_module, action) do
    app_module.pages()
    |> Enum.filter(&(&1.action() == action))
    |> List.first()
  end
end