phoenix_live_view v0.2.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 thehandle_event/3
callback.render_submit/3
- sends a form phx-submit event and value and returns the rendered result of thehandle_event/3
callback.render_change/3
- sends a form phx-change event and value and returns the rendered result of thehandle_event/3
callback.render_keydown/3
- sends a form phx-keydown event and value and returns the rendered result of thehandle_event/3
callback.render_keyup/3
- sends a form phx-keyup event and value and returns the rendered result of thehandle_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.
Spawns a connected LiveView process mounted in isolation as the sole rendered element.
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
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
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}
children(parent) View Source
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"
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 convenience
to perform the get/2
and connected mount in a single
step.
Options
:connect_params
- the map of params available in the socket connected mount. SeePhoenix.LiveView.get_connect_params/1
for more information.
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()
live(conn, path, opts) View Source (macro)
live_isolated(conn, live_view, router, pipelines, opts \\ []) View Source (macro)
Spawns a connected LiveView process mounted in isolation as the sole rendered element.
Usefule for testing LiveViews that are not directly routable, such as those built as small compontents to be re-used in multiple parents. Testing routable LiveViews is still recommended whenever possible since features such as live navigation require routable LiveViews.
Options
:connect_params
- the map of params available in connected mount mount. SeePhoenix.LiveView.get_connect_params/1
for more information.
All other options are forwarded to the live view for rendering. Refer to
Phoenix.LiveView.live_render/3
for list of supported render options.
Examples
{:ok, view, html} =
live_isolated(conn, AppWeb.ClockLive, AppWeb.Router, [:browser], session: %{tz: "EST"})
render(view) View Source
Returns the string of HTML of the rendered view.
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"
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"
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℉"
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..."
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℉"
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℉"
render_live_link(view, path) View Source
Simulates a live_link click to the view and returns the rendered result.
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℉"
stop(view) View Source
Stops a LiveView process.
Examples
stop(view)
assert_remove view, {:shutdown, :stop}