2026-02-28 11:18:37 +00:00
|
|
|
defmodule BerrypodWeb.Admin.NavigationTest do
|
|
|
|
|
use BerrypodWeb.ConnCase, async: false
|
|
|
|
|
|
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
|
import Berrypod.AccountsFixtures
|
|
|
|
|
|
|
|
|
|
alias Berrypod.{Pages, Settings}
|
|
|
|
|
alias Berrypod.Pages.PageCache
|
|
|
|
|
|
|
|
|
|
setup do
|
|
|
|
|
PageCache.invalidate_all()
|
|
|
|
|
user = user_fixture()
|
|
|
|
|
%{user: user}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "unauthenticated" do
|
|
|
|
|
test "redirects to login", %{conn: conn} do
|
|
|
|
|
{:error, redirect} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
assert {:redirect, %{to: path}} = redirect
|
|
|
|
|
assert path == ~p"/users/log-in"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "navigation editor" do
|
|
|
|
|
setup %{conn: conn, user: user} do
|
|
|
|
|
%{conn: log_in_user(conn, user)}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "renders with header and footer sections", %{conn: conn} do
|
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
assert html =~ "Navigation"
|
|
|
|
|
assert html =~ "Header navigation"
|
|
|
|
|
assert html =~ "Footer navigation"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "shows default header items", %{conn: conn} do
|
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
assert html =~ "Home"
|
|
|
|
|
assert html =~ "Shop"
|
|
|
|
|
assert html =~ "About"
|
|
|
|
|
assert html =~ "Contact"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "shows default footer items", %{conn: conn} do
|
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
assert html =~ "Delivery & returns"
|
|
|
|
|
assert html =~ "Privacy policy"
|
|
|
|
|
assert html =~ "Terms of service"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "adding an item appends to list", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
render_click(view, "add_item", %{"section" => "header"})
|
|
|
|
|
|
|
|
|
|
# Should have 5 items now (4 defaults + 1 new)
|
|
|
|
|
html = render(view)
|
2026-03-08 02:10:06 +00:00
|
|
|
# Count header item forms by their phx-value-section attribute
|
|
|
|
|
assert Regex.scan(~r/phx-value-section="header".*?phx-value-index/, html)
|
|
|
|
|
|> length() >= 5
|
2026-02-28 11:18:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "removing an item removes from list", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
render_click(view, "remove_item", %{"section" => "header", "index" => "0"})
|
|
|
|
|
|
|
|
|
|
html = render(view)
|
|
|
|
|
# "Home" should be gone (it was index 0)
|
|
|
|
|
refute html =~ ~s(value="Home")
|
|
|
|
|
# "Shop" should still be there
|
|
|
|
|
assert html =~ ~s(value="Shop")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "moving item up reorders", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
render_click(view, "move_item", %{
|
|
|
|
|
"section" => "header",
|
|
|
|
|
"index" => "1",
|
|
|
|
|
"dir" => "up"
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Save and check order
|
|
|
|
|
render_click(view, "save")
|
|
|
|
|
|
|
|
|
|
items = Settings.get_setting("header_nav")
|
|
|
|
|
assert Enum.at(items, 0)["label"] == "Shop"
|
|
|
|
|
assert Enum.at(items, 1)["label"] == "Home"
|
|
|
|
|
end
|
|
|
|
|
|
2026-03-08 02:10:06 +00:00
|
|
|
test "custom pages appear in destination dropdown", %{conn: conn} do
|
2026-02-28 11:18:37 +00:00
|
|
|
{:ok, _} = Pages.create_custom_page(%{slug: "faq", title: "FAQ"})
|
|
|
|
|
|
2026-03-08 02:10:06 +00:00
|
|
|
{:ok, _view, html} = live(conn, ~p"/admin/navigation")
|
2026-02-28 11:18:37 +00:00
|
|
|
|
2026-03-08 02:10:06 +00:00
|
|
|
# Custom page should appear in the dropdown options
|
|
|
|
|
assert html =~ "FAQ"
|
2026-02-28 11:18:37 +00:00
|
|
|
assert html =~ ~s(value="/faq")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "save persists to settings", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
render_click(view, "remove_item", %{"section" => "footer", "index" => "0"})
|
|
|
|
|
render_click(view, "save")
|
|
|
|
|
|
2026-03-08 02:10:06 +00:00
|
|
|
# Inline feedback shows "Saved"
|
|
|
|
|
assert has_element?(view, ".admin-inline-feedback-saved", "Saved")
|
2026-02-28 11:18:37 +00:00
|
|
|
|
|
|
|
|
items = Settings.get_setting("footer_nav")
|
|
|
|
|
assert length(items) == 3
|
|
|
|
|
assert Enum.at(items, 0)["label"] == "Privacy policy"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "reset defaults restores original items", %{conn: conn} do
|
|
|
|
|
# Save custom nav
|
|
|
|
|
Settings.put_setting("header_nav", [%{"label" => "Only", "href" => "/only"}], "json")
|
|
|
|
|
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
# Should show the custom item
|
|
|
|
|
assert render(view) =~ ~s(value="Only")
|
|
|
|
|
|
|
|
|
|
render_click(view, "reset_defaults")
|
|
|
|
|
|
|
|
|
|
html = render(view)
|
|
|
|
|
assert html =~ ~s(value="Home")
|
|
|
|
|
assert html =~ ~s(value="Shop")
|
|
|
|
|
refute html =~ ~s(value="Only")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "dirty flag appears after changes", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
refute has_element?(view, ".admin-badge-warning")
|
|
|
|
|
|
|
|
|
|
render_click(view, "add_item", %{"section" => "header"})
|
|
|
|
|
|
|
|
|
|
assert has_element?(view, ".admin-badge-warning", "Unsaved changes")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "dirty flag clears after save", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
render_click(view, "add_item", %{"section" => "header"})
|
|
|
|
|
assert has_element?(view, ".admin-badge-warning")
|
|
|
|
|
|
2026-03-08 02:10:06 +00:00
|
|
|
# Fill in required fields for the new item before saving
|
|
|
|
|
render_change(view, "nav_item_change", %{
|
|
|
|
|
"section" => "header",
|
|
|
|
|
"index" => "4",
|
|
|
|
|
"label" => "Test",
|
|
|
|
|
"dest" => "/"
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 11:18:37 +00:00
|
|
|
render_click(view, "save")
|
|
|
|
|
refute has_element?(view, ".admin-badge-warning")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
test "save button disabled when not dirty", %{conn: conn} do
|
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/admin/navigation")
|
|
|
|
|
|
|
|
|
|
assert has_element?(view, "button[disabled]", "Save")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|