From c173a796255b29a2e71d1ce382fba78c7352f2d7 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Fri, 23 Aug 2024 11:48:37 +0200
Subject: [PATCH 01/11] Add floki dependency in demo
---
demo/mix.exs | 3 ++-
demo/mix.lock | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/demo/mix.exs b/demo/mix.exs
index 3380be48..5b32d0bd 100644
--- a/demo/mix.exs
+++ b/demo/mix.exs
@@ -66,7 +66,8 @@ defmodule Demo.MixProject do
{:tesla, "~> 1.4"},
{:jason, ">= 1.0.0"},
{:bandit, "~> 1.0"},
- {:heroicons, github: "tailwindlabs/heroicons", tag: "v2.1.5", sparse: "optimized", app: false, compile: false}
+ {:heroicons, github: "tailwindlabs/heroicons", tag: "v2.1.5", sparse: "optimized", app: false, compile: false},
+ {:floki, ">= 0.30.0", only: :test}
]
end
diff --git a/demo/mix.lock b/demo/mix.lock
index 0534d526..73b91d42 100644
--- a/demo/mix.lock
+++ b/demo/mix.lock
@@ -18,6 +18,7 @@
"expo": {:hex, :expo, "1.0.0", "647639267e088717232f4d4451526e7a9de31a3402af7fcbda09b27e9a10395a", [:mix], [], "hexpm", "18d2093d344d97678e8a331ca0391e85d29816f9664a25653fd7e6166827827c"},
"faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
+ "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
"gettext": {:hex, :gettext, "0.25.0", "98a95a862a94e2d55d24520dd79256a15c87ea75b49673a2e2f206e6ebc42e5d", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "38e5d754e66af37980a94fb93bb20dcde1d2361f664b0a19f01e87296634051f"},
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
From 433e79f84e431d71ce09d7aaaa725906f4069fbd Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Fri, 23 Aug 2024 11:48:49 +0200
Subject: [PATCH 02/11] Refactor pagination info component
---
lib/backpex/html/resource.ex | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/lib/backpex/html/resource.ex b/lib/backpex/html/resource.ex
index 00710ff3..234ea136 100644
--- a/lib/backpex/html/resource.ex
+++ b/lib/backpex/html/resource.ex
@@ -383,15 +383,19 @@ defmodule Backpex.HTML.Resource do
def pagination_info(assigns) do
%{query_options: %{page: page, per_page: per_page}} = assigns
- assigns =
- assigns
- |> assign(:from, (page - 1) * per_page + 1)
- |> assign(:to, min(page * per_page, assigns.total))
+ from = (page - 1) * per_page + 1
+ to = min(page * per_page, assigns.total)
+
+ from_to_string = Backpex.translate({"Items %{from} to %{to}", %{from: from, to: to}})
+ total_string = "(#{assigns.total} #{Backpex.translate("total")})"
+
+ label = from_to_string <> " " <> total_string
+
+ assigns = assign(assigns, :label, label)
~H"""
0} class="text-base-content pr-2 text-sm">
- <%= Backpex.translate({"Items %{from} to %{to}", %{from: @from, to: @to}}) %>
- <%= "(#{@total} #{Backpex.translate("total")})" %>
+ <%= @label %>
"""
end
From 494721d4dd1998fa6fed491a13f60a66e4995a2a Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Fri, 23 Aug 2024 11:48:58 +0200
Subject: [PATCH 03/11] Add test helpers
---
demo/test/support/conn_case.ex | 38 ++++++++++++++++++++++
demo/test/support/data_case.ex | 58 ++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 demo/test/support/conn_case.ex
create mode 100644 demo/test/support/data_case.ex
diff --git a/demo/test/support/conn_case.ex b/demo/test/support/conn_case.ex
new file mode 100644
index 00000000..de3dc600
--- /dev/null
+++ b/demo/test/support/conn_case.ex
@@ -0,0 +1,38 @@
+defmodule DemoWeb.ConnCase do
+ @moduledoc """
+ This module defines the test case to be used by
+ tests that require setting up a connection.
+
+ Such tests rely on `Phoenix.ConnTest` and also
+ import other functionality to make it easier
+ to build common data structures and query the data layer.
+
+ 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 DemoWeb.ConnCase, async: true`, although
+ this option is not recommended for other databases.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ # The default endpoint for testing
+ @endpoint DemoWeb.Endpoint
+
+ use DemoWeb, :verified_routes
+
+ # Import conveniences for testing with connections
+ import Plug.Conn
+ import Phoenix.ConnTest
+ import DemoWeb.ConnCase
+ end
+ end
+
+ setup tags do
+ Demo.DataCase.setup_sandbox(tags)
+ {:ok, conn: Phoenix.ConnTest.build_conn()}
+ end
+end
diff --git a/demo/test/support/data_case.ex b/demo/test/support/data_case.ex
new file mode 100644
index 00000000..ba77b779
--- /dev/null
+++ b/demo/test/support/data_case.ex
@@ -0,0 +1,58 @@
+defmodule Demo.DataCase do
+ @moduledoc """
+ This module defines the setup for tests requiring
+ access to the application's data layer.
+
+ You may define functions here to be used as helpers in
+ your tests.
+
+ 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 Demo.DataCase, async: true`, although
+ this option is not recommended for other databases.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ alias Demo.Repo
+
+ import Ecto
+ import Ecto.Changeset
+ import Ecto.Query
+ import Demo.DataCase
+ end
+ end
+
+ setup tags do
+ Demo.DataCase.setup_sandbox(tags)
+ :ok
+ end
+
+ @doc """
+ Sets up the sandbox based on the test tags.
+ """
+ def setup_sandbox(tags) do
+ pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Demo.Repo, shared: not tags[:async])
+ on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
+ end
+
+ @doc """
+ A helper that transforms changeset errors into a map of messages.
+
+ assert {:error, changeset} = Accounts.create_user(%{password: "short"})
+ assert "password is too short" in errors_on(changeset).password
+ assert %{password: ["password is too short"]} = errors_on(changeset)
+
+ """
+ def errors_on(changeset) do
+ Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
+ Regex.replace(~r"%{(\w+)}", message, fn _, key ->
+ opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
+ end)
+ end)
+ end
+end
From fd5bcc46e4dc67fee0434a6e390f4f9321810732 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Fri, 23 Aug 2024 11:49:06 +0200
Subject: [PATCH 04/11] Test tag live resource
---
demo/test/demo_web/tag_live_test.exs | 56 ++++++++++++
demo/test/support/live_resource_tests.ex | 106 +++++++++++++++++++++++
2 files changed, 162 insertions(+)
create mode 100644 demo/test/demo_web/tag_live_test.exs
create mode 100644 demo/test/support/live_resource_tests.ex
diff --git a/demo/test/demo_web/tag_live_test.exs b/demo/test/demo_web/tag_live_test.exs
new file mode 100644
index 00000000..4138faa6
--- /dev/null
+++ b/demo/test/demo_web/tag_live_test.exs
@@ -0,0 +1,56 @@
+defmodule DemoWeb.TagLiveTest do
+ use DemoWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import DemoWeb.LiveResourceTests
+
+ alias Demo.Tag
+ alias Demo.Repo
+
+
+ setup do
+ tags =
+ for entry <- data() do
+ Tag.create_changeset(%Tag{}, entry)
+ |> Repo.insert!()
+ end
+
+ %{tags: tags}
+ end
+
+ describe "tags live resource index" do
+ test "is rendered", %{conn: conn} do
+ {:ok, view, html} = live(conn, "/admin/tags")
+
+ assert has_element?(view, "h1", "Tags")
+ assert has_element?(view, "button", "New Tag")
+ assert has_element?(view, "button[disabled]", "Delete")
+
+ assert html =~ "Items 1 to 3 (3 total)"
+ end
+
+ test "delete button becomes enabled when clicking checkbox", %{conn: conn, tags: tags} do
+ delete_button_disabled_enabled_test(conn, "/admin/tags", tags)
+ end
+
+ test "table body contains exact amount of rows", %{conn: conn, tags: tags} do
+ table_rows_count_test(conn, "/admin/tags", Enum.count(tags))
+ end
+
+ test "show item action redirects to show view", %{conn: conn, tags: tags} do
+ show_action_redirect_test(conn, "/admin/tags", tags)
+ end
+
+ test "edit item action redirects to edit view", %{conn: conn, tags: tags} do
+ edit_action_redirect_test(conn, "/admin/tags", tags)
+ end
+ end
+
+ defp data do
+ [
+ %{name: "Expert"},
+ %{name: "Beginner"},
+ %{name: "DIY"}
+ ]
+ end
+end
diff --git a/demo/test/support/live_resource_tests.ex b/demo/test/support/live_resource_tests.ex
new file mode 100644
index 00000000..6c396bda
--- /dev/null
+++ b/demo/test/support/live_resource_tests.ex
@@ -0,0 +1,106 @@
+defmodule DemoWeb.LiveResourceTests do
+ @moduledoc """
+ Defines macros that can be used to include basic live resource tests.
+ """
+
+ @doc """
+ Tests whether the table body contains expected amount of rows.
+ """
+ defmacro table_rows_count_test(conn, base_path, expected_rows_count) do
+ quote do
+ conn = unquote(conn)
+ base_path = unquote(base_path)
+ expected_rows_count = unquote(expected_rows_count)
+
+ {:ok, view, _html} = live(conn, base_path)
+
+ assert view
+ |> element("table tbody")
+ |> render()
+ |> Floki.parse_fragment!()
+ |> Floki.find("tr")
+ |> Enum.count() == expected_rows_count
+ end
+ end
+
+ @doc """
+ Tests whether delete button becomes enabled when clicking checkbox.
+ """
+ defmacro delete_button_disabled_enabled_test(conn, base_path, items) do
+ quote do
+ conn = unquote(conn)
+ base_path = unquote(base_path)
+ items = unquote(items)
+
+ if Enum.count(items) == 0 do
+ raise "Cannot test show redirect with 0 items"
+ end
+
+ {:ok, view, _html} = live(conn, base_path)
+
+ refute has_element?(view, "button:not([disabled])", "Delete")
+
+ [%{id: first_item_id} | _items] = items
+
+ view
+ |> element("#select-input-#{first_item_id}")
+ |> render_click()
+
+ assert has_element?(view, "button:not([disabled])", "Delete")
+ end
+ end
+
+ @doc """
+ Tests whether the show item action actually redirects to the show view.
+ """
+ defmacro show_action_redirect_test(conn, base_path, items) do
+ quote do
+ conn = unquote(conn)
+ base_path = unquote(base_path)
+ items = unquote(items)
+
+ if Enum.count(items) == 0 do
+ raise "Cannot test show redirect with 0 items"
+ end
+
+ {:ok, view, _html} = live(conn, base_path)
+
+ [%{id: first_item_id} | _items] = items
+
+ view
+ |> element("button[aria-label='Show'][phx-value-item-id='#{first_item_id}']")
+ |> render_click()
+
+ path = assert_patch view
+
+ assert path == "#{base_path}/#{first_item_id}/show"
+ end
+ end
+
+ @doc """
+ Tests whether the edit item action actually redirects to the edit view.
+ """
+ defmacro edit_action_redirect_test(conn, base_path, items) do
+ quote do
+ conn = unquote(conn)
+ base_path = unquote(base_path)
+ items = unquote(items)
+
+ if Enum.count(items) == 0 do
+ raise "Cannot test edit redirect with 0 items"
+ end
+
+ {:ok, view, _html} = live(conn, base_path)
+
+ [%{id: first_item_id} | _items] = items
+
+ view
+ |> element("button[aria-label='Edit'][phx-value-item-id='#{first_item_id}']")
+ |> render_click()
+
+ path = assert_patch view
+
+ assert path == "#{base_path}/#{first_item_id}/edit"
+ end
+ end
+end
From 04b2941918796373533603176560ff382792dcbf Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Fri, 23 Aug 2024 11:55:56 +0200
Subject: [PATCH 05/11] Format
---
demo/test/demo_web/tag_live_test.exs | 1 -
demo/test/support/live_resource_tests.ex | 14 +++++++-------
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/demo/test/demo_web/tag_live_test.exs b/demo/test/demo_web/tag_live_test.exs
index 4138faa6..3fd3ce00 100644
--- a/demo/test/demo_web/tag_live_test.exs
+++ b/demo/test/demo_web/tag_live_test.exs
@@ -7,7 +7,6 @@ defmodule DemoWeb.TagLiveTest do
alias Demo.Tag
alias Demo.Repo
-
setup do
tags =
for entry <- data() do
diff --git a/demo/test/support/live_resource_tests.ex b/demo/test/support/live_resource_tests.ex
index 6c396bda..9be446bc 100644
--- a/demo/test/support/live_resource_tests.ex
+++ b/demo/test/support/live_resource_tests.ex
@@ -15,11 +15,11 @@ defmodule DemoWeb.LiveResourceTests do
{:ok, view, _html} = live(conn, base_path)
assert view
- |> element("table tbody")
- |> render()
- |> Floki.parse_fragment!()
- |> Floki.find("tr")
- |> Enum.count() == expected_rows_count
+ |> element("table tbody")
+ |> render()
+ |> Floki.parse_fragment!()
+ |> Floki.find("tr")
+ |> Enum.count() == expected_rows_count
end
end
@@ -71,7 +71,7 @@ defmodule DemoWeb.LiveResourceTests do
|> element("button[aria-label='Show'][phx-value-item-id='#{first_item_id}']")
|> render_click()
- path = assert_patch view
+ path = assert_patch(view)
assert path == "#{base_path}/#{first_item_id}/show"
end
@@ -98,7 +98,7 @@ defmodule DemoWeb.LiveResourceTests do
|> element("button[aria-label='Edit'][phx-value-item-id='#{first_item_id}']")
|> render_click()
- path = assert_patch view
+ path = assert_patch(view)
assert path == "#{base_path}/#{first_item_id}/edit"
end
From e89c20f50a85f02d44b3eafa5cd9ffd20114b6b4 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:09:47 +0200
Subject: [PATCH 06/11] Install and use phoenix_test dependency
---
demo/config/test.exs | 2 +
demo/mix.exs | 3 +-
demo/mix.lock | 2 +
demo/test/demo_web/tag_live_test.exs | 67 +++++++++-------------
demo/test/support/conn_case.ex | 2 +
demo/test/support/live_resource_tests.ex | 72 +++++++++++-------------
6 files changed, 69 insertions(+), 79 deletions(-)
diff --git a/demo/config/test.exs b/demo/config/test.exs
index a766cabb..c5348f26 100644
--- a/demo/config/test.exs
+++ b/demo/config/test.exs
@@ -15,3 +15,5 @@ config :demo, DemoWeb.Endpoint, server: false
config :demo, Demo.Repo, pool: Ecto.Adapters.SQL.Sandbox
config :phoenix, :plug_init_mode, :runtime
+
+config :phoenix_test, :endpoint, DemoWeb.Endpoint
diff --git a/demo/mix.exs b/demo/mix.exs
index 5b32d0bd..e3e99d4d 100644
--- a/demo/mix.exs
+++ b/demo/mix.exs
@@ -67,7 +67,8 @@ defmodule Demo.MixProject do
{:jason, ">= 1.0.0"},
{:bandit, "~> 1.0"},
{:heroicons, github: "tailwindlabs/heroicons", tag: "v2.1.5", sparse: "optimized", app: false, compile: false},
- {:floki, ">= 0.30.0", only: :test}
+ {:floki, ">= 0.30.0", only: :test},
+ {:phoenix_test, "~> 0.3.1", only: :test, runtime: false}
]
end
diff --git a/demo/mix.lock b/demo/mix.lock
index 73b91d42..ca79b35a 100644
--- a/demo/mix.lock
+++ b/demo/mix.lock
@@ -8,6 +8,7 @@
"csv": {:hex, :csv, "3.2.1", "6d401f1ed33acb2627682a9ab6021e96d33ca6c1c6bccc243d8f7e2197d032f5", [:mix], [], "hexpm", "8f55a0524923ae49e97ff2642122a2ce7c61e159e7fe1184670b2ce847aee6c8"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
+ "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.8.0", "440719cd74f09b3f01c84455707a2c3972b725c513808e68eb6c5b0ab82bf523", [:mix], [{:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 0.18.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1 or ~> 4.0.0", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "f1512812dc196bcb932a96c82e55f69b543dc125e9d39f5e3631a9c4ec65ef12"},
@@ -50,6 +51,7 @@
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
"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.3.1", "adf5d67cb152fa1e0220527a9827db7f865a20817c7c0161315ba6fe86a8946c", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {: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", "03f24332a966673ff40d35c45f427c7671537ea508b5777141dd60e60cdae22a"},
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [: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", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
diff --git a/demo/test/demo_web/tag_live_test.exs b/demo/test/demo_web/tag_live_test.exs
index 3fd3ce00..4dcaf25b 100644
--- a/demo/test/demo_web/tag_live_test.exs
+++ b/demo/test/demo_web/tag_live_test.exs
@@ -1,55 +1,44 @@
defmodule DemoWeb.TagLiveTest do
use DemoWeb.ConnCase
+ import Demo.Factory
import Phoenix.LiveViewTest
import DemoWeb.LiveResourceTests
- alias Demo.Tag
- alias Demo.Repo
-
- setup do
- tags =
- for entry <- data() do
- Tag.create_changeset(%Tag{}, entry)
- |> Repo.insert!()
- end
-
- %{tags: tags}
- end
-
describe "tags live resource index" do
test "is rendered", %{conn: conn} do
- {:ok, view, html} = live(conn, "/admin/tags")
-
- assert has_element?(view, "h1", "Tags")
- assert has_element?(view, "button", "New Tag")
- assert has_element?(view, "button[disabled]", "Delete")
-
- assert html =~ "Items 1 to 3 (3 total)"
+ insert_list(3, :tag)
+
+ conn
+ |> visit("/admin/tags")
+ |> assert_has("h1", text: "Tags", exact: true)
+ |> assert_has("button", text: "New Tag", exact: true)
+ |> assert_has("button[disabled]", text: "Delete", exact: true)
+ |> assert_has("div", text: "Items 1 to 3 (3 total)", exact: true)
end
- test "delete button becomes enabled when clicking checkbox", %{conn: conn, tags: tags} do
- delete_button_disabled_enabled_test(conn, "/admin/tags", tags)
+ test "search for items", %{conn: conn} do
+ insert(:tag, %{name: "Elixir"})
+ insert(:tag, %{name: "Phoenix"})
+
+ conn
+ |> visit("/admin/tags")
+ |> unwrap(fn view ->
+ view
+ |> form("form[phx-change='index-search']", %{"index_search[value]" => "Elixir"})
+ |> render_change()
+ end)
+ |> refute_has("tr", text: "Phoenix")
+ |> assert_has("tr", text: "Elixir")
end
- test "table body contains exact amount of rows", %{conn: conn, tags: tags} do
- table_rows_count_test(conn, "/admin/tags", Enum.count(tags))
- end
-
- test "show item action redirects to show view", %{conn: conn, tags: tags} do
- show_action_redirect_test(conn, "/admin/tags", tags)
- end
+ test "basic functionality", %{conn: conn} do
+ tags = insert_list(3, :tag)
- test "edit item action redirects to edit view", %{conn: conn, tags: tags} do
- edit_action_redirect_test(conn, "/admin/tags", tags)
+ test_table_rows_count(conn, "/admin/tags", Enum.count(tags))
+ test_delete_button_disabled_enabled(conn, "/admin/tags", tags)
+ test_show_action_redirect(conn, "/admin/tags", tags)
+ test_edit_action_redirect(conn, "/admin/tags", tags)
end
end
-
- defp data do
- [
- %{name: "Expert"},
- %{name: "Beginner"},
- %{name: "DIY"}
- ]
- end
end
diff --git a/demo/test/support/conn_case.ex b/demo/test/support/conn_case.ex
index de3dc600..b3a03784 100644
--- a/demo/test/support/conn_case.ex
+++ b/demo/test/support/conn_case.ex
@@ -24,6 +24,8 @@ defmodule DemoWeb.ConnCase do
use DemoWeb, :verified_routes
+ import PhoenixTest
+
# Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
diff --git a/demo/test/support/live_resource_tests.ex b/demo/test/support/live_resource_tests.ex
index 9be446bc..567dba16 100644
--- a/demo/test/support/live_resource_tests.ex
+++ b/demo/test/support/live_resource_tests.ex
@@ -6,27 +6,22 @@ defmodule DemoWeb.LiveResourceTests do
@doc """
Tests whether the table body contains expected amount of rows.
"""
- defmacro table_rows_count_test(conn, base_path, expected_rows_count) do
+ defmacro test_table_rows_count(conn, base_path, expected_rows_count) do
quote do
conn = unquote(conn)
base_path = unquote(base_path)
expected_rows_count = unquote(expected_rows_count)
- {:ok, view, _html} = live(conn, base_path)
-
- assert view
- |> element("table tbody")
- |> render()
- |> Floki.parse_fragment!()
- |> Floki.find("tr")
- |> Enum.count() == expected_rows_count
+ conn
+ |> visit(base_path)
+ |> assert_has(".table tbody tr", count: expected_rows_count)
end
end
@doc """
Tests whether delete button becomes enabled when clicking checkbox.
"""
- defmacro delete_button_disabled_enabled_test(conn, base_path, items) do
+ defmacro test_delete_button_disabled_enabled(conn, base_path, items) do
quote do
conn = unquote(conn)
base_path = unquote(base_path)
@@ -36,24 +31,25 @@ defmodule DemoWeb.LiveResourceTests do
raise "Cannot test show redirect with 0 items"
end
- {:ok, view, _html} = live(conn, base_path)
-
- refute has_element?(view, "button:not([disabled])", "Delete")
-
[%{id: first_item_id} | _items] = items
- view
- |> element("#select-input-#{first_item_id}")
- |> render_click()
-
- assert has_element?(view, "button:not([disabled])", "Delete")
+ conn
+ |> visit(base_path)
+ |> refute_has("button:not([disabled])", text: "Delete")
+ |> assert_has("#select-input-#{first_item_id}")
+ |> unwrap(fn view ->
+ view
+ |> element("#select-input-#{first_item_id}")
+ |> render_click()
+ end)
+ |> assert_has("button:not([disabled])", text: "Delete", exact: true)
end
end
@doc """
Tests whether the show item action actually redirects to the show view.
"""
- defmacro show_action_redirect_test(conn, base_path, items) do
+ defmacro test_show_action_redirect(conn, base_path, items) do
quote do
conn = unquote(conn)
base_path = unquote(base_path)
@@ -63,24 +59,23 @@ defmodule DemoWeb.LiveResourceTests do
raise "Cannot test show redirect with 0 items"
end
- {:ok, view, _html} = live(conn, base_path)
-
[%{id: first_item_id} | _items] = items
- view
- |> element("button[aria-label='Show'][phx-value-item-id='#{first_item_id}']")
- |> render_click()
-
- path = assert_patch(view)
-
- assert path == "#{base_path}/#{first_item_id}/show"
+ conn
+ |> visit(base_path)
+ |> unwrap(fn view ->
+ view
+ |> element("button[aria-label='Show'][phx-value-item-id='#{first_item_id}']")
+ |> render_click()
+ end)
+ |> assert_path("#{base_path}/#{first_item_id}/show")
end
end
@doc """
Tests whether the edit item action actually redirects to the edit view.
"""
- defmacro edit_action_redirect_test(conn, base_path, items) do
+ defmacro test_edit_action_redirect(conn, base_path, items) do
quote do
conn = unquote(conn)
base_path = unquote(base_path)
@@ -90,17 +85,16 @@ defmodule DemoWeb.LiveResourceTests do
raise "Cannot test edit redirect with 0 items"
end
- {:ok, view, _html} = live(conn, base_path)
-
[%{id: first_item_id} | _items] = items
- view
- |> element("button[aria-label='Edit'][phx-value-item-id='#{first_item_id}']")
- |> render_click()
-
- path = assert_patch(view)
-
- assert path == "#{base_path}/#{first_item_id}/edit"
+ conn
+ |> visit(base_path)
+ |> unwrap(fn view ->
+ view
+ |> element("button[aria-label='Edit'][phx-value-item-id='#{first_item_id}']")
+ |> render_click()
+ end)
+ |> assert_path("#{base_path}/#{first_item_id}/edit")
end
end
end
From 5024fa8ec479419f298bc7a10d4ab5a55157d6ff Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:11:47 +0200
Subject: [PATCH 07/11] Format
---
demo/test/demo_web/tag_live_test.exs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/demo/test/demo_web/tag_live_test.exs b/demo/test/demo_web/tag_live_test.exs
index 4dcaf25b..7598ed95 100644
--- a/demo/test/demo_web/tag_live_test.exs
+++ b/demo/test/demo_web/tag_live_test.exs
@@ -24,9 +24,9 @@ defmodule DemoWeb.TagLiveTest do
conn
|> visit("/admin/tags")
|> unwrap(fn view ->
- view
- |> form("form[phx-change='index-search']", %{"index_search[value]" => "Elixir"})
- |> render_change()
+ view
+ |> form("form[phx-change='index-search']", %{"index_search[value]" => "Elixir"})
+ |> render_change()
end)
|> refute_has("tr", text: "Phoenix")
|> assert_has("tr", text: "Elixir")
From 6041e98146aa8c8f6f2510aef0288bb6a2b7e64d Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:17:20 +0200
Subject: [PATCH 08/11] Alias `Ecto.Adapters.SQL.Sandbox` module
---
demo/test/support/data_case.ex | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/demo/test/support/data_case.ex b/demo/test/support/data_case.ex
index ba77b779..bf4377c4 100644
--- a/demo/test/support/data_case.ex
+++ b/demo/test/support/data_case.ex
@@ -16,6 +16,8 @@ defmodule Demo.DataCase do
use ExUnit.CaseTemplate
+ alias Ecto.Adapters.SQL.Sandbox
+
using do
quote do
alias Demo.Repo
@@ -36,8 +38,8 @@ defmodule Demo.DataCase do
Sets up the sandbox based on the test tags.
"""
def setup_sandbox(tags) do
- pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Demo.Repo, shared: not tags[:async])
- on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
+ pid = Sandbox.start_owner!(Demo.Repo, shared: not tags[:async])
+ on_exit(fn -> Sandbox.stop_owner(pid) end)
end
@doc """
From bae5d2a496d68640620f8ce8b19a0d31f7e790fb Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:17:43 +0200
Subject: [PATCH 09/11] Use `Enum.empty?/1` instead of `Enum.count/1`
---
demo/test/support/live_resource_tests.ex | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/demo/test/support/live_resource_tests.ex b/demo/test/support/live_resource_tests.ex
index 567dba16..fde10df5 100644
--- a/demo/test/support/live_resource_tests.ex
+++ b/demo/test/support/live_resource_tests.ex
@@ -27,7 +27,7 @@ defmodule DemoWeb.LiveResourceTests do
base_path = unquote(base_path)
items = unquote(items)
- if Enum.count(items) == 0 do
+ if Enum.empty?(items) do
raise "Cannot test show redirect with 0 items"
end
@@ -55,7 +55,7 @@ defmodule DemoWeb.LiveResourceTests do
base_path = unquote(base_path)
items = unquote(items)
- if Enum.count(items) == 0 do
+ if Enum.empty?(items) do
raise "Cannot test show redirect with 0 items"
end
@@ -81,7 +81,7 @@ defmodule DemoWeb.LiveResourceTests do
base_path = unquote(base_path)
items = unquote(items)
- if Enum.count(items) == 0 do
+ if Enum.empty?(items) do
raise "Cannot test edit redirect with 0 items"
end
From 1574c657c3b87a7148ac87bea418cad67431d438 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:33:29 +0200
Subject: [PATCH 10/11] Update error message
---
demo/test/support/live_resource_tests.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demo/test/support/live_resource_tests.ex b/demo/test/support/live_resource_tests.ex
index fde10df5..95a61994 100644
--- a/demo/test/support/live_resource_tests.ex
+++ b/demo/test/support/live_resource_tests.ex
@@ -28,7 +28,7 @@ defmodule DemoWeb.LiveResourceTests do
items = unquote(items)
if Enum.empty?(items) do
- raise "Cannot test show redirect with 0 items"
+ raise "Cannot test delete button with 0 items"
end
[%{id: first_item_id} | _items] = items
From 52be83de167df71a2e80e3a38e27bea3650e2d31 Mon Sep 17 00:00:00 2001
From: Florian Arens <60519307+Flo0807@users.noreply.github.com>
Date: Mon, 26 Aug 2024 09:56:24 +0200
Subject: [PATCH 11/11] Add more tests for tag live resource
---
demo/test/demo_web/tag_live_test.exs | 63 ++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/demo/test/demo_web/tag_live_test.exs b/demo/test/demo_web/tag_live_test.exs
index 7598ed95..df459339 100644
--- a/demo/test/demo_web/tag_live_test.exs
+++ b/demo/test/demo_web/tag_live_test.exs
@@ -23,11 +23,13 @@ defmodule DemoWeb.TagLiveTest do
conn
|> visit("/admin/tags")
+ |> assert_has(".table tbody tr", count: 2)
|> unwrap(fn view ->
view
|> form("form[phx-change='index-search']", %{"index_search[value]" => "Elixir"})
|> render_change()
end)
+ |> assert_has(".table tbody tr", count: 1)
|> refute_has("tr", text: "Phoenix")
|> assert_has("tr", text: "Elixir")
end
@@ -41,4 +43,65 @@ defmodule DemoWeb.TagLiveTest do
test_edit_action_redirect(conn, "/admin/tags", tags)
end
end
+
+ describe "tags live resource show" do
+ test "is rendered", %{conn: conn} do
+ tag = insert(:tag)
+
+ conn
+ |> visit("/admin/tags/#{tag.id}/show")
+ |> assert_has("h1", text: "Tag", exact: true)
+ |> assert_has("p", text: "Name", exact: true)
+ |> assert_has("p", text: "Inserted At", exact: true)
+ |> assert_has("p", text: tag.name, exact: true)
+ end
+ end
+
+ describe "tags live resource edit" do
+ test "is rendered", %{conn: conn} do
+ tag = insert(:tag)
+
+ conn
+ |> visit("/admin/tags/#{tag.id}/edit")
+ |> assert_has("h1", text: "Edit Tag", exact: true)
+ |> assert_has("button", text: "Cancel", exact: true)
+ |> assert_has("button", text: "Save", exact: true)
+ end
+
+ test "submit form", %{conn: conn} do
+ tag = insert(:tag, %{name: "Elixir"})
+
+ conn
+ |> visit("/admin/tags/#{tag.id}/edit")
+ |> unwrap(fn view ->
+ view
+ |> form("#resource-form", %{"change[name]" => "Phoenix"})
+ |> render_submit()
+ end)
+ |> assert_has(".table tbody tr", count: 1)
+ |> assert_has("p", text: "Phoenix", exact: true)
+ end
+ end
+
+ describe "tags live resource new" do
+ test "is rendered", %{conn: conn} do
+ conn
+ |> visit("/admin/tags/new")
+ |> assert_has("h1", text: "New Tag", exact: true)
+ |> assert_has("button", text: "Cancel", exact: true)
+ |> assert_has("button", text: "Save", exact: true)
+ end
+
+ test "submit form", %{conn: conn} do
+ conn
+ |> visit("/admin/tags/new")
+ |> unwrap(fn view ->
+ view
+ |> form("#resource-form", %{"change[name]" => "Phoenix"})
+ |> render_submit()
+ end)
+ |> assert_has(".table tbody tr", count: 1)
+ |> assert_has("p", text: "Phoenix", exact: true)
+ end
+ end
end