phoenix_live_view v0.1.0 Phoenix.LiveViewTest View Source

Conveniences for testing Phoenix live views.

In LiveView tests, we interact with views via process communication in substitution of a browser. Like a browser, our test process receives messages about the rendered updates from the view which can be asserted against to test the life-cycle and behavior of live views and their children.

LiveView Testing

The life-cycle of a live view as outlined in the Phoenix.LiveView docs details how a view starts as a stateless HTML render in a disconnected socket state. Once the browser receives the HTML, it connects to the server and a new LiveView process is started, remounted in a connected socket state, and the view continues statefully. The LiveView test functions support testing both disconnected and connected mounts separately, for example:

use Phoenix.ConnTest
import Phoenix.LiveViewTest
@endpoint MyEndpoint

test "disconnected and connected mount", %{conn: conn} do
  conn = get(conn, "/my-path")
  assert html_response(conn, 200) =~ "<h1>My Disconnected View</h1>"

  {:ok, view, html} = live(conn)
end

test "redirected mount", %{conn: conn} do
  assert {:error, %{redirect: %{to: "/somewhere"}}} = live(conn, "my-path")
end

Here, we start by using the familiar Phoenix.ConnTest function, get/2 to test the regular HTTP get request which invokes mount with a disconnect socket. Next, live/1 is called with our sent connection to mount the view in a connected state, which starts our stateful LiveView process.

In general, it's often more convenient to test the mounting of a view in a single step, provided you don't need the result of the stateless HTTP render. This is done with a single call to live/2, which performs the get step for us:

test "connected mount", %{conn: conn} do
  {:ok, view, html} = live(conn, "/my-path")
  assert html =~ "<h1>My Connected View</h1>"
end

Testing Events

The browser can send a variety of events to a live view via phx- bindings, which are sent to the handle_event/3 callback. To test events sent by the browser and assert on the rendered side-effect of the event, use the render_* functions:

  • render_click/3 - sends a phx-click event and value and returns the rendered result of the handle_event/3 callback.

  • render_submit/3 - sends a form phx-submit event and value and returns the rendered result of the handle_event/3 callback.

  • render_change/3 - sends a form phx-change event and value and returns the rendered result of the handle_event/3 callback.

  • render_keydown/3 - sends a form phx-keydown event and value and returns the rendered result of the handle_event/3 callback.

  • render_keyup/3 - sends a form phx-keyup event and value and returns the rendered result of the handle_event/3 callback.

For example:

{:ok, view, _html} = live(conn, "/thermo")

assert render_click(view, :inc) =~ "The temperature is: 31℉"

assert render_click(view, :set_temp, 35) =~ "The temperature is: 35℉"

assert render_submit(view, :save, %{deg: 30}) =~ "The temperature is: 30℉"

assert render_change(view, :validate, %{deg: -30}) =~ "invalid temperature"

assert render_keydown(view, :key, :ArrowUp) =~ "The temperature is: 31℉"

assert render_keydown(view, :key, :ArrowDown) =~ "The temperature is: 30℉"

Testing regular messages

Live views are GenServer's under the hood, and can send and receive messages just like any other server. To test the side effects of sending or receiving messages, simply message the view and use the render function to test the result:

send(view.pid, {:set_temp: 50})
assert render(view) =~ "The temperature is: 50℉"

Testing shutdowns and stopping views

Like all processes, views can shutdown normally or abnormally, and this can be tested with assert_remove/3. For example:

send(view.pid, :boom)
assert_remove view, {:shutdown, %RuntimeError{}}

stop(view)
assert_remove view, {:shutdown, :stop}

Nested views can be removed by a parent at any time based on conditional rendering. In these cases, the removal of the view is detected by the browser, or our test client, and the child is shutdown gracefully. This can be tested in the same way as above:

assert render(parent) =~ "some content in child"

[child] = children(parent)
send(parent.pid, :msg_that_removes_child)

assert_remove child, _
refute render(parent) =~ "some content in child"

Link to this section Summary

Functions

Asserts a redirect was peformed after execution of the provided function.

Asserts a view was removed by a parent or shutdown itself.

Returns the current list of children of the parent live view.

Spawns a connected LiveView process.

Returns the string of HTML of the rendered view.

Sends a blur event to the view and returns the rendered result.

Sends a form change event to the view and returns the rendered result.

Sends a click event to the view and returns the rendered result.

Sends a focus event to the view and returns the rendered result.

Sends a keydown event to the view and returns the rendered result.

Sends a keyup event to the view and returns the rendered result.

Simulates a live_link click to the view and returns the rendered result.

Sends a form submit event to the view and returns the rendered result.

Stops a LiveView process.

Link to this section Functions

Link to this macro

assert_redirect(view, to, func) View Source (macro)

Asserts a redirect was peformed after execution of the provided function.

Examples

assert_redirect view, "/path", fn ->
  assert render_click(view, :event_that_triggers_redirect)
end
Link to this macro

assert_remove(view, reason, timeout \\ 100) View Source (macro)

Asserts a view was removed by a parent or shutdown itself.

Examples

[child1, child2] = children(parent_view)
send(parent_view.pid, :msg_that_removes_child)

assert_remove child1, _
assert_remove child2, {:shutdown, :removed}

Returns the current list of children of the parent live view.

Children are return in the order they appear in the rendered HTML.

Examples

{:ok, view, _html} = live(conn, "/thermo")
assert [clock_view] = children(view)
assert render_click(clock_view, :snooze) =~ "snoozing"
Link to this macro

live(conn, path_or_opts \\ []) View Source (macro)

Spawns a connected LiveView process.

Accepts either a previously rendered %Plug.Conn{} or an unsent %Plug.Conn{}. The latter case is a conveience to perform the get/2 and connected mount in a single step.

Options

  • :connect_params - the map of params available in connected mount

Examples

{:ok, view, html} = live(conn, "/path")

assert view.module = MyLive

assert html =~ "the count is 3"

assert {:error, %{redirect: %{to: "/somewhere"}}} = live(conn, "/path")

{:ok, view, html} =
  conn
  |> get("/path")
  |> live()
Link to this macro

live(conn, path, opts) View Source (macro)

Returns the string of HTML of the rendered view.

Link to this function

render_blur(view, event, value \\ %{}) View Source

Sends a blur event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_blur(view, :inactive) =~ "Tap to wake"
Link to this function

render_change(view, event, value \\ %{}) View Source

Sends a form change event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_change(view, :validate, %{deg: 123}) =~ "123 exceeds limits"
Link to this function

render_click(view, event, value \\ %{}) View Source

Sends a click event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temperature is: 30℉"
assert render_click(view, :inc) =~ "The temperature is: 31℉"
Link to this function

render_focus(view, event, value \\ %{}) View Source

Sends a focus event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_blur(view, :inactive) =~ "Tap to wake"
assert render_focus(view, :active) =~ "Waking up..."
Link to this function

render_keydown(view, event, key_code) View Source

Sends a keydown event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_keyup(view, :inc, :ArrowUp) =~ "The temp is: 32℉"
Link to this function

render_keyup(view, event, key_code) View Source

Sends a keyup event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_keyup(view, :inc, :ArrowUp) =~ "The temp is: 32℉"
Link to this function

render_live_link(view, path) View Source

Simulates a live_link click to the view and returns the rendered result.

Link to this function

render_submit(view, event, value \\ %{}) View Source

Sends a form submit event to the view and returns the rendered result.

Examples

{:ok, view, html} = live(conn, "/thermo")
assert html =~ "The temp is: 30℉"
assert render_submit(view, :refresh, %{deg: 32}) =~ "The temp is: 32℉"

Stops a LiveView process.

Examples

stop(view)
assert_remove view, {:shutdown, :stop}