Skip to content

Commit

Permalink
Improve Assertions using Query.find
Browse files Browse the repository at this point in the history
Having separated `Query.find` from the `find!` (raising) version, we can
now use that in our Assertions to provide better error messages.

This commit cleans up our `assert_has` and `refute_has` logic.

It also reveals that we're handling `Query.find/3` (when we provide
text) slightly differently than `Query.find/2`. The return values are
different when multiple records are found. We hope to improve that in a
future commit and make it consistent.

Testing note
------------

We also fully specify the error messages in assertion tests. Since these
are really important, we want to make sure that the errors not only
match with a regex, but that they're exactly as we expect them.
  • Loading branch information
germsvel committed Feb 3, 2024
1 parent 3c8dcfc commit c995fc1
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 51 deletions.
68 changes: 37 additions & 31 deletions lib/phoenix_test/assertions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,58 @@ defmodule PhoenixTest.Assertions do

import ExUnit.Assertions

alias PhoenixTest.Html
alias PhoenixTest.Query

def assert_has(session, css, text) do
found =
session
|> PhoenixTest.Driver.render_html()
|> Query.find!(css, text)
|> Html.text()

if found =~ text do
assert true
else
raise """
Expected to find #{inspect(text)} somewhere in here:
#{found}
"""
def assert_has(session, selector, text) do
session
|> PhoenixTest.Driver.render_html()
|> Query.find(selector, text)
|> case do
{:found, _found} ->
assert true

{:not_found, []} ->
raise """
Could not find any elements with selector #{inspect(selector)}.
"""

{:not_found, elements_matched_selector} ->
raise """
Could not find element with text #{inspect(text)}.
Found other elements matching the selector #{inspect(selector)}:
#{format_found_elements(elements_matched_selector)}
"""
end

session
end

def refute_has(session, css, text) do
def refute_has(session, selector, text) do
session
|> PhoenixTest.Driver.render_html()
|> Html.parse()
|> Html.all(css)
|> Query.find(selector, text)
|> case do
[] ->
{:not_found, _} ->
refute false

elements ->
if Enum.any?(elements, &element_with_text?(&1, text)) do
raise """
Expected not to find an element.
{:found, element} ->
raise """
Expected not to find an element.
But found an element with selector #{inspect(selector)} and text #{inspect(text)}:
But found an element with selector #{inspect(css)} and text #{inspect(text)}.
"""
else
refute false
end
#{format_found_element(element)}
"""
end
end

defp element_with_text?(el, text) do
Html.text(el) == text
defp format_found_elements(elements) do
Enum.map_join(elements, "\n", &format_found_element/1)
end

defp format_found_element({tag, _attrs, [content]}) do
"<#{tag}> with content #{inspect(content)}"
end
end
6 changes: 3 additions & 3 deletions lib/phoenix_test/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ defmodule PhoenixTest.Query do
end

defp find_with_text(html, selector, text) do
elements =
elements_matched_selector =
html
|> Html.all(selector)

Enum.find(elements, :not_found, fn element ->
Enum.find(elements_matched_selector, :not_found, fn element ->
Html.text(element) =~ text
end)
|> then(fn
:not_found -> {:not_found, elements}
:not_found -> {:not_found, elements_matched_selector}
found -> {:found, found}
end)
end
Expand Down
62 changes: 45 additions & 17 deletions test/phoenix_test/assertions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,32 @@ defmodule PhoenixTest.AssertionsTest do
|> assert_has(".has_extra_space", "Has extra space")
end

test "raises an error if the element cannot be found", %{conn: conn} do
conn =
conn
|> visit("/page/index")
test "raises an error if the element cannot be found at all", %{conn: conn} do
conn = visit(conn, "/page/index")

msg = ~r/Could not find any elements with selector "#nonexistent-id"/

assert_raise RuntimeError, msg, fn ->
conn |> assert_has("#nonexistent-id", "Main page")
end
end

test "raises error if element cannot be found but selector matches other elements", %{
conn: conn
} do
conn = visit(conn, "/page/index")

msg = """
Could not find element with text "Super page".
Found other elements matching the selector "h1":
<h1> with content "Main page"
"""

assert_raise ArgumentError,
~r/Could not find element with selector "#nonexistent-id"/,
fn ->
conn |> assert_has("#nonexistent-id", "Main page")
end
assert_raise RuntimeError, msg, fn ->
conn |> assert_has("h1", "Super page")
end
end
end

Expand Down Expand Up @@ -81,21 +97,33 @@ defmodule PhoenixTest.AssertionsTest do
end

test "raises an error if one element is found", %{conn: conn} do
conn =
conn
|> visit("/page/index")
conn = visit(conn, "/page/index")

msg = """
Expected not to find an element.
But found an element with selector "#title" and text "Main page":
<h1> with content "Main page"
"""

assert_raise RuntimeError, ~r/Expected not to find an element/, fn ->
assert_raise RuntimeError, msg, fn ->
conn |> refute_has("#title", "Main page")
end
end

test "raises an error if multiple elements are found", %{conn: conn} do
conn =
conn
|> visit("/page/index")
conn = visit(conn, "/page/index")

msg = """
Expected not to find an element.
But found an element with selector ".multiple_links" and text "Multiple links":
<a> with content "Multiple links"
"""

assert_raise RuntimeError, ~r/Expected not to find an element/, fn ->
assert_raise RuntimeError, msg, fn ->
conn |> refute_has(".multiple_links", "Multiple links")
end
end
Expand Down

0 comments on commit c995fc1

Please sign in to comment.