defmodule Adh do
@moduledoc """
Adh is a tiny library of helpers to make assertions on DOM.
Powered by Floki.
"""
use ExUnit.Case
@typedoc """
An HTML document as a string
"""
@type html :: String.t()
@typedoc """
An assertion
"""
@type assertion :: {atom(), String.t() | tuple()}
@typedoc """
A list of assertions
"""
@type assertions :: [assertions()]
@typedoc """
The assertions result
"""
@type assert_result :: [:ok | {:fail, String.t()}]
@doc """
Run the assertions against the given html string.
## Examples
iex> Adh.dom_assert("<p class='red'>Red Text</p>", [{:dom_present, ".red"}])
[:ok]
iex> Adh.dom_assert("<p class='red'>Red Text</p>", [{:dom_present, ".yellow"}])
[{:fail, "dom present: `.yellow` is not present."}]
"""
@spec dom_assert(html(), assertions()) :: assert_result() | no_return()
def dom_assert(html, assertions) when is_list(assertions) do
document = Floki.parse_document!(html)
assertions
|> Enum.map(&check(document, &1))
end
def dom_assert(_, assertions) do
raise(Adh.InvalidAssertions, assertions)
end
defp check(document, {:dom_count, {selector, count}}) do
got = document |> Floki.find(selector) |> length()
ok_fail(got == count, "dom count: expected #{count}, got #{got} instead.")
end
defp check(document, {:dom_present, selector}) do
got = document |> Floki.find(selector) |> length()
ok_fail(got > 0, "dom present: `#{selector}` is not present.")
end
defp check(document, {:dom_absent, selector}) do
got = document |> Floki.find(selector)
ok_fail(got == [], "dom absent: `#{selector}` is not absent.")
end
defp check(document, {:dom_single, selector}) do
got = document |> Floki.find(selector) |> length()
ok_fail(got == 1, "dom single: `#{selector}` is not single.")
end
defp check(document, {:dom_multi, selector}) do
got = document |> Floki.find(selector) |> length()
ok_fail(got > 1, "dom multi: `#{selector}` is not multi.")
end
defp check(document, {:dom_text, {selector, expected}}) when is_binary(expected) do
[got] = document |> Floki.find(selector) |> Enum.map(&Floki.text(&1))
ok_fail(got == expected, "dom text: got `#{got}` instead of `#{expected}`.")
end
defp check(document, {:dom_text, {selector, expected}}) when is_list(expected) do
got = document |> Floki.find(selector) |> Enum.map(&Floki.text(&1))
ok_fail(
got == expected,
"dom text: got `#{Enum.join(got, ",")}` instead of `#{Enum.join(expected, ", ")}`."
)
end
defp check(_, x), do: raise(Adh.InvalidAssertion, x)
defp ok_fail(true, _), do: :ok
defp ok_fail(false, message), do: {:fail, message}
end