An all-star cast of ARIA roles for Phoenix LiveViewTest.
Ensemble is a library for testing Phoenix LiveView. You write your test using ARIA roles instead of CSS selectors. It means you are incentivised to improve your accessibility, have easier to read tests, and your test is less coupled to a particular implementation making refactoring easier.
Ensemble sits alongside LiveView’s built-in Phoenix.LiveViewTest
. Ensemble provides you substitutes for Phoenix.LiveViewTest.element/3
and Phoenix.LiveViewTest.has_element?/3
:
Here’s a before with CSS selectors:
import Phoenix.LiveViewTest
import Ensemble
test "has home link", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
assert view |> has_element?(~S|a[href="/"]|)
end
test "can subscribe to newsletter", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
view
|> element(~S|button[phx-action="subscribe"]|)
|> render_click() =~ "You are now subscribed."
end
And after with ARIA roles and accessible names:
import Phoenix.LiveViewTest
import Ensemble
test "has home link", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
assert view |> has_role?(:link, "Home")
end
test "can subscribe to newsletter", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
view
|> role(:button, "Subscribe")
|> render_click() =~ "You are now subscribed."
end
There’s less focus on details such as HTML structure or LiveView attributes that can change, and more focus on the semantic layer presented to users. Your tests become like a screen reader. This means your app will likely work better with real screen readers.
If you are new to the concepts of ARIA roles or accessible names I recommend reading W3C’s guides:
Install via Hex by adding ensemble
to your list of dependencies in mix.exs
:
def deps do
[
{:ensemble, "~> 0.0.1"}
]
end
Write a LiveViewTest and import Ensemble
.
defmodule TodoLiveTest do
use YourAppWeb.ConnCase, async: true
import Phoenix.LiveViewTest
import Ensemble
test "has navigation", %{conn: conn} do
{:ok, view, _html} = live(conn, "/todos")
assert view |> has_role?(:banner)
assert view |> has_role?(:contentinfo)
assert view |> has_role?(:navigation, "Primary")
assert view |> has_role?(:link, "Home")
assert view |> has_role?(:link, "About")
assert view |> has_role?(:button, "Sign Out")
refute view |> has_role?(:link, "Does not exist")
assert view
|> role(:link, "Home")
|> render() == ~S|<a href="/">Home</a>|
end
test "form", %{conn: conn} do
{:ok, view, _html} = live(conn, "/todos")
assert view |> has_role?(:form, "New todo item")
assert view |> has_role?(:textbox, "Description")
assert view |> has_role?(:checkbox, "High priority")
assert view
|> role(:button, "Add item")
|> render_click() =~ "Item created."
end
end