Skip to content

Commit

Permalink
🚧 Adopt PhoenixTest
Browse files Browse the repository at this point in the history
Mostly works, but I've run into a couple of issues:

- [`CaseClauseError` when multiple matches in `assert_has`](germsvel/phoenix_test#18)
- [No obvious way to follow redirects after a form submition](germsvel/phoenix_test#19)
  • Loading branch information
randycoulman committed Feb 10, 2024
1 parent 193bd9d commit 525c107
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 69 deletions.
2 changes: 2 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ config :logger, level: :warning

# Initialize plugs at runtime for faster test compilation
config :phoenix, :plug_init_mode, :runtime

config :phoenix_test, :endpoint, FreedomAccountWeb.Endpoint
2 changes: 1 addition & 1 deletion lib/freedom_account_web/live/fund_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</.link>
</:action> --%>
</.table>
<div :if={@funds == []}>
<div :if={@funds == []} id="no-funds">
This account has no funds yet. Use the Add Fund button to add one.
</div>

Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ defmodule FreedomAccount.MixProject do
{:phoenix_live_dashboard, "~> 0.8.3"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.20.5"},
{:phoenix_test, "~> 0.2.2", only: :test, runtime: false},
{:phoenix, "~> 1.7"},
{:postgrex, ">= 0.0.0"},
{:styler, "~> 0.11.9", only: [:dev, :test], runtime: false},
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.5", "6207acfdc6a824327d8d55c59b9c3398c0ddeac8935501ac0686921ad30482f3", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2c4d28e91f531a48e24a0ae2b24aad3d56ad122ddd424d2e220070dd9930f0c4"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_test": {:hex, :phoenix_test, "0.2.2", "71debe865d1937d9d448eb470be9279e50a9a4928e3a3f8307c2721a46906ca4", [:mix], [{:floki, ">= 0.30.0", [hex: :floki, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7.10", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "052ec702af3d6fb63c749e0134103770a86d5587650c1b415606fea9d14029a0"},
"plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"},
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
"postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"},
Expand Down
45 changes: 17 additions & 28 deletions test/freedom_account_web/live/account_live_test.exs
Original file line number Diff line number Diff line change
@@ -1,51 +1,40 @@
defmodule FreedomAccountWeb.AccountLiveTest do
@moduledoc false

use FreedomAccountWeb.ConnCase, async: true

import Phoenix.LiveViewTest
use FreedomAccountWeb.FeatureCase, async: true

alias FreedomAccount.Factory

@invalid_attrs %{deposits_per_year: nil, name: nil}

defp create_account(_context) do
account = Factory.account()
%{account: account}
%{account: Factory.account()}
end

describe "Show" do
setup [:create_account]

test "displays account", %{conn: conn, account: account} do
{:ok, _show_live, html} = live(conn, ~p"/")

assert html =~ "Freedom Account"
assert html =~ escaped(account.name)
conn
|> visit(~p"/")
|> assert_has("h1", "Freedom Account")
|> assert_has("h2", escaped(account.name))
end

test "updates account within modal", %{conn: conn} do
update_attrs = Factory.account_attrs()

{:ok, show_live, _html} = live(conn, ~p"/")

assert show_live |> element("a", "Edit") |> render_click() =~
"Account Settings"

assert_patch(show_live, ~p"/edit")

assert show_live
|> form("#account-form", account: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank"

{:ok, _live, html} =
show_live
|> form("#account-form", account: update_attrs)
|> render_submit()
|> follow_redirect(conn, ~p"/")

assert html =~ "Account updated successfully"
assert html =~ escaped(update_attrs[:name])
conn
|> visit(~p"/")
|> click_link("Edit")
|> assert_has("h2", "Settings")
|> fill_form("#account-form", account: @invalid_attrs)
# |> assert_has("p", "can't be blank")
|> fill_form("#account-form", account: update_attrs)
|> click_button("Save Account")

# |> assert_has("p", "Account updated successfully")
# |> assert_has("h2", escaped(update_attrs[:name]))
end
end
end
55 changes: 23 additions & 32 deletions test/freedom_account_web/live/fund_live_test.exs
Original file line number Diff line number Diff line change
@@ -1,62 +1,53 @@
defmodule FreedomAccountWeb.FundLiveTest do
@moduledoc false

use FreedomAccountWeb.ConnCase, async: true
use FreedomAccountWeb.FeatureCase, async: true

import Phoenix.LiveViewTest
# import FreedomAccount.FundsFixtures

alias FreedomAccount.Factory

@invalid_attrs %{icon: nil, name: nil}

defp create_account(_context) do
account = Factory.account()

%{account: account}
%{account: Factory.account()}
end

describe "Index" do
setup [:create_account]

test "lists all funds", %{account: account, conn: conn} do
fund = Factory.fund(account)
{:ok, _index_live, html} = live(conn, ~p"/")

assert html =~ "Funds"
assert html =~ fund.icon
assert html =~ escaped(fund.name)
conn
|> visit(~p"/")
|> assert_has("h2", "Funds")
|> assert_has("span", fund.icon)
|> assert_has("span", escaped(fund.name))
end

test "shows prompt when list is empty", %{conn: conn} do
{:ok, _index_live, html} = live(conn, ~p"/")

assert html =~ "Funds"
assert html =~ "no funds"
conn
|> visit(~p"/")
|> assert_has("h2", "Funds")
|> assert_has("#no-funds", "This account has no funds yet. Use the Add Fund button to add one.")
end

test "saves new fund", %{conn: conn} do
attrs = Factory.fund_attrs()
{:ok, index_live, _html} = live(conn, ~p"/")

assert index_live |> element("a", "Add Fund") |> render_click() =~
"Add Fund"

assert_patch(index_live, ~p"/funds/new")

assert index_live
|> form("#fund-form", fund: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank"

{:ok, _view, html} =
index_live
|> form("#fund-form", fund: attrs)
|> render_submit()
|> follow_redirect(conn, ~p"/")

assert html =~ "Fund created successfully"
assert html =~ attrs[:icon]
assert html =~ escaped(attrs[:name])
conn
|> visit(~p"/")
|> click_link("Add Fund")
|> assert_has("h2", "Add Fund")
|> fill_form("#fund-form", fund: @invalid_attrs)
# |> assert_has("p", "can't be blank")
|> fill_form("#fund-form", fund: attrs)
|> click_button("Save Fund")

# |> assert_has("p", "Fund created successfully")
# |> assert_has("span", attrs[:icon])
# |> assert_has("span", escaped(attrs[:name]))
end

# test "updates fund in listing", %{conn: conn, fund: fund} do
Expand Down
12 changes: 8 additions & 4 deletions test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,31 @@ defmodule FreedomAccountWeb.ConnCase do

use ExUnit.CaseTemplate

alias FreedomAccount.DataCase
alias Phoenix.ConnTest
alias Phoenix.HTML

using do
quote do
# The default endpoint for testing
use FreedomAccountWeb, :verified_routes

# Import conveniences for testing with connections
import FreedomAccountWeb.ConnCase
import Phoenix.ConnTest
import Plug.Conn
import unquote(__MODULE__)

@endpoint FreedomAccountWeb.Endpoint
end
end

setup tags do
FreedomAccount.DataCase.setup_sandbox(tags)
{:ok, conn: Phoenix.ConnTest.build_conn()}
DataCase.setup_sandbox(tags)
{:ok, conn: ConnTest.build_conn()}
end

@spec escaped(String.t()) :: String.t()
def escaped(string) do
string |> Phoenix.HTML.html_escape() |> Phoenix.HTML.safe_to_string()
string |> HTML.html_escape() |> HTML.safe_to_string()
end
end
10 changes: 6 additions & 4 deletions test/support/data_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ defmodule FreedomAccount.DataCase do
use ExUnit.CaseTemplate

alias Ecto.Adapters.SQL.Sandbox
alias Ecto.Changeset
alias FreedomAccount.Repo

using do
quote do
import Ecto
import Ecto.Changeset
import Ecto.Query
import FreedomAccount.DataCase
import unquote(__MODULE__)

alias FreedomAccount.Repo
end
Expand All @@ -39,7 +41,7 @@ defmodule FreedomAccount.DataCase do
"""
@spec setup_sandbox(map) :: :ok
def setup_sandbox(tags) do
pid = Sandbox.start_owner!(FreedomAccount.Repo, shared: not tags[:async])
pid = Sandbox.start_owner!(Repo, shared: not tags[:async])
on_exit(fn -> Sandbox.stop_owner(pid) end)
end

Expand All @@ -51,9 +53,9 @@ defmodule FreedomAccount.DataCase do
assert %{password: ["password is too short"]} = errors_on(changeset)
"""
@spec errors_on(Ecto.Changeset.t()) :: %{optional(atom) => list}
@spec errors_on(Changeset.t()) :: %{optional(atom) => list}
def errors_on(changeset) do
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
Changeset.traverse_errors(changeset, fn {message, opts} ->
Regex.replace(~r"%{(\w+)}", message, fn _match, key ->
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
end)
Expand Down
41 changes: 41 additions & 0 deletions test/support/feature_case.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule FreedomAccountWeb.FeatureCase do
@moduledoc """
This module defines the test case to be used by tests that require setting up
a connection to test feature tests.
Such tests rely on `PhoenixTest` and also import other functionality to
make it easier to build common data structures and interact with pages.
Finally, if the test case interacts with the database, we enable the SQL
sandbox, so changes done to the database are reverted at the end of every
test. If you are using PostgreSQL, you can even run database tests
asynchronously by setting `use FreedomAccountWeb.FeatureCase, async: true`, although
this option is not recommended for other databases.
"""

use ExUnit.CaseTemplate

alias FreedomAccount.DataCase
alias Phoenix.ConnTest
alias Phoenix.HTML

using do
quote do
use FreedomAccountWeb, :verified_routes

import PhoenixTest
import unquote(__MODULE__)
end
end

setup tags do
DataCase.setup_sandbox(tags)

{:ok, conn: ConnTest.build_conn()}
end

@spec escaped(String.t()) :: String.t()
def escaped(string) do
string |> HTML.html_escape() |> HTML.safe_to_string()
end
end

0 comments on commit 525c107

Please sign in to comment.