add admin CRUD for custom CMS pages
All checks were successful
deploy / deploy (push) Successful in 1m21s

New settings form for creating and editing custom page metadata
(title, slug, meta description, published, nav settings). Pages
index shows custom pages section with draft badges and delete.
Editor shows settings button for custom pages, hides reset to
defaults. 20 new tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-28 09:43:03 +00:00
parent bb6b28a163
commit 045be2ed7e
8 changed files with 473 additions and 8 deletions

View File

@@ -701,6 +701,202 @@ defmodule BerrypodWeb.Admin.PagesTest do
end
end
describe "custom pages on index" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "shows new page button", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, "a[href='/admin/pages/new']", "New page")
end
test "shows custom pages section when pages exist", %{conn: conn} do
{:ok, _} = Pages.create_custom_page(%{slug: "faq", title: "FAQ"})
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, ".page-group-title", "Custom pages")
assert has_element?(view, ".page-card-title", "FAQ")
end
test "does not show custom pages section when empty", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages")
refute has_element?(view, ".page-group-title", "Custom pages")
end
test "shows draft badge for unpublished pages", %{conn: conn} do
{:ok, _} =
Pages.create_custom_page(%{slug: "draft-page", title: "Draft", published: false})
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, ".admin-badge-warning", "Draft")
end
test "custom page card links to editor", %{conn: conn} do
{:ok, _} = Pages.create_custom_page(%{slug: "faq", title: "FAQ"})
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, "a[href='/admin/pages/faq']")
end
test "shows slug in card meta", %{conn: conn} do
{:ok, _} = Pages.create_custom_page(%{slug: "our-story", title: "Our story"})
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, ".page-card-meta", "/our-story")
end
test "delete removes page from list", %{conn: conn} do
{:ok, _} = Pages.create_custom_page(%{slug: "faq", title: "FAQ"})
{:ok, view, _html} = live(conn, ~p"/admin/pages")
assert has_element?(view, ".page-card-title", "FAQ")
view
|> element("button[phx-click='delete_custom_page'][phx-value-slug='faq']")
|> render_click()
refute has_element?(view, ".page-card-title", "FAQ")
assert render(view) =~ "Page deleted"
end
end
describe "custom page creation" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "renders creation form", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
assert has_element?(view, "h1", "New page")
assert has_element?(view, "#custom-page-form")
end
test "creating with valid data redirects to editor", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
view
|> form("#custom-page-form", page: %{title: "FAQ", slug: "faq"})
|> render_submit()
assert_redirect(view, ~p"/admin/pages/faq")
end
test "creating with empty title shows error", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
view
|> form("#custom-page-form", page: %{title: "", slug: "faq"})
|> render_submit()
assert has_element?(view, "#custom-page-form")
end
test "creating with reserved slug shows error", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
html =
view
|> form("#custom-page-form", page: %{title: "Admin", slug: "admin"})
|> render_submit()
assert html =~ "is reserved"
end
test "creating with duplicate slug shows error", %{conn: conn} do
{:ok, _} = Pages.create_custom_page(%{slug: "faq", title: "FAQ"})
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
html =
view
|> form("#custom-page-form", page: %{title: "FAQ 2", slug: "faq"})
|> render_submit()
assert html =~ "has already been taken"
end
test "auto-slugifies title during validation", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/new")
html =
view
|> form("#custom-page-form", page: %{title: "Our Story"})
|> render_change()
assert html =~ ~s(value="our-story")
end
end
describe "custom page settings" do
setup %{conn: conn, user: user} do
{:ok, _} = Pages.create_custom_page(%{slug: "help-page", title: "Help"})
%{conn: log_in_user(conn, user)}
end
test "renders settings form", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/help-page/settings")
assert has_element?(view, "h1", "Page settings")
assert has_element?(view, "#custom-page-form")
end
test "updating title saves correctly", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/help-page/settings")
view
|> form("#custom-page-form", page: %{title: "Help centre"})
|> render_submit()
assert_redirect(view, ~p"/admin/pages/help-page")
page = Pages.get_page("help-page")
assert page.title == "Help centre"
end
test "updating slug redirects to new editor path", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/help-page/settings")
view
|> form("#custom-page-form", page: %{slug: "support"})
|> render_submit()
assert_redirect(view, ~p"/admin/pages/support")
end
test "settings for nonexistent page redirects", %{conn: conn} do
assert {:error, {:live_redirect, %{to: "/admin/pages"}}} =
live(conn, ~p"/admin/pages/nonexistent/settings")
end
end
describe "editor for custom pages" do
setup %{conn: conn, user: user} do
{:ok, _} = Pages.create_custom_page(%{slug: "size-guide", title: "Size guide"})
%{conn: log_in_user(conn, user)}
end
test "shows settings button for custom pages", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/size-guide")
assert has_element?(view, "a[href='/admin/pages/size-guide/settings']", "Settings")
end
test "does not show reset to defaults for custom pages", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/size-guide")
refute has_element?(view, "button", "Reset to defaults")
end
test "shows reset to defaults for system pages", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/pages/home")
assert has_element?(view, "button", "Reset to defaults")
refute has_element?(view, "a[href='/admin/pages/home/settings']")
end
end
defp count_repeater_items(html) do
Regex.scan(~r/class="repeater-item"/, html) |> length()
end