berrypod/test/berrypod_web/page_editor_hook_test.exs

314 lines
9.9 KiB
Elixir
Raw Normal View History

defmodule BerrypodWeb.PageEditorHookTest do
use BerrypodWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import Berrypod.AccountsFixtures
import Berrypod.ProductsFixtures
alias Berrypod.Pages
alias Berrypod.Pages.PageCache
setup do
PageCache.invalidate_all()
user = user_fixture()
{:ok, _} = Berrypod.Settings.set_site_live(true)
provider_conn = provider_connection_fixture()
product =
product_fixture(%{
provider_connection: provider_conn,
title: "Test Product",
category: "Test Category"
})
product_variant_fixture(%{product: product, title: "Standard", price: 1999})
Berrypod.Products.recompute_cached_fields(product)
%{user: user, product: product}
end
describe "non-admin cannot access edit mode" do
test "visiting with ?edit=true shows normal page, no sidebar", %{conn: conn} do
{:ok, _view, html} = live(conn, "/?edit=true")
refute html =~ "page-editor-sidebar"
refute html =~ "page-editor-live"
end
end
describe "edit button visibility" do
test "admin sees edit pencil in shop header", %{conn: conn, user: user} do
conn = log_in_user(conn, user)
{:ok, _view, html} = live(conn, "/")
assert html =~ "Edit page"
end
test "non-admin does not see edit pencil", %{conn: conn} do
{:ok, _view, html} = live(conn, "/")
refute html =~ "Edit page"
end
end
describe "entering and exiting edit mode" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "admin enters edit mode with ?edit=true", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
assert has_element?(view, ".page-editor-sidebar")
assert has_element?(view, ".page-editor-content")
assert has_element?(view, ".block-card")
end
test "sidebar shows the page title", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
assert has_element?(view, ".page-editor-sidebar-title", "Home page")
end
test "done button exits edit mode", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# editor_done uses push_navigate, which causes a redirect
{:error, {:live_redirect, %{to: "/"}}} =
view |> element("button[phx-click='editor_done']") |> render_click()
end
test "toggle sidebar hides and shows via header pencil", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# Sidebar starts open, pencil button in header is hidden
assert has_element?(view, "[data-sidebar-open='true']")
refute has_element?(
view,
"button[phx-click='editor_toggle_sidebar'][aria-label='Show editor sidebar']"
)
# Close the sidebar via the X button
view
|> element("button[phx-click='editor_toggle_sidebar'][aria-label='Close sidebar']")
|> render_click()
assert has_element?(view, "[data-sidebar-open='false']")
# Pencil button appears in header to re-open
assert has_element?(
view,
"button[phx-click='editor_toggle_sidebar'][aria-label='Show editor sidebar']"
)
# Re-open via pencil in header
view
|> element("button[phx-click='editor_toggle_sidebar'][aria-label='Show editor sidebar']")
|> render_click()
assert has_element?(view, "[data-sidebar-open='true']")
end
test "clicking backdrop hides the sidebar", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# Backdrop present when sidebar is open
assert has_element?(view, ".page-editor-backdrop")
# Click backdrop to dismiss
view |> element(".page-editor-backdrop") |> render_click()
assert has_element?(view, "[data-sidebar-open='false']")
# Backdrop gone when sidebar is hidden
refute has_element?(view, ".page-editor-backdrop")
end
end
describe "block manipulation in edit mode" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "move block down reorders the list", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# Home page default: hero is first block
first_card = view |> element(".block-card:first-child")
first_html = render(first_card)
assert first_html =~ "Hero"
# Get the hero block's ID and move it down
blocks = Pages.get_page("home").blocks
hero_id = List.first(blocks)["id"]
view
|> element("button[phx-click='editor_move_down'][phx-value-id='#{hero_id}']")
|> render_click()
# After move, hero is no longer the first card
updated_first = view |> element(".block-card:first-child") |> render()
refute updated_first =~ "Hero"
end
test "dirty indicator appears after changes", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
refute has_element?(view, ".admin-badge-warning", "Unsaved changes")
# Move a block to trigger dirty state
blocks = Pages.get_page("home").blocks
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
assert has_element?(view, ".admin-badge-warning", "Unsaved changes")
end
end
describe "save and reset" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "save persists block changes", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# Move a block to make changes
blocks = Pages.get_page("home").blocks
original_first_type = List.first(blocks)["type"]
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
# Save
view |> element("button[phx-click='editor_save']") |> render_click()
assert has_element?(view, "#shop-flash-info", "Page saved")
# Verify persistence
updated = Pages.get_page("home")
refute List.first(updated.blocks)["type"] == original_first_type
end
test "reset restores default blocks", %{conn: conn} do
# First, save a modified page
original = Pages.get_page("home")
reordered = Enum.reverse(original.blocks)
Pages.save_page("home", %{title: original.title, blocks: reordered})
PageCache.invalidate_all()
{:ok, view, _html} = live(conn, "/?edit=true")
# Reset
view |> element("button[phx-click='editor_reset_defaults']") |> render_click()
assert has_element?(view, "#shop-flash-info", "Page reset to defaults")
# Verify the blocks are back to defaults
reset_page = Pages.get_page("home")
assert List.first(reset_page.blocks)["type"] == List.first(original.blocks)["type"]
end
end
describe "undo and redo" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "undo reverts the last change", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
blocks = Pages.get_page("home").blocks
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
# After move_up, second block is now first
first_html = view |> element(".block-card:first-child") |> render()
refute first_html =~ "Hero"
# Undo
view |> element("button[phx-click='editor_undo']") |> render_click()
# Hero should be back as first block
restored_first = view |> element(".block-card:first-child") |> render()
assert restored_first =~ "Hero"
end
test "redo restores an undone change", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
blocks = Pages.get_page("home").blocks
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
view |> element("button[phx-click='editor_undo']") |> render_click()
view |> element("button[phx-click='editor_redo']") |> render_click()
# After redo, hero should no longer be first
first_html = view |> element(".block-card:first-child") |> render()
refute first_html =~ "Hero"
end
test "history clears on save", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
blocks = Pages.get_page("home").blocks
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
view |> element("button[phx-click='editor_save']") |> render_click()
# After save, undo should be disabled
assert has_element?(view, "button[phx-click='editor_undo'][disabled]")
end
test "undo/redo buttons reflect stack state", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?edit=true")
# Initially both disabled
assert has_element?(view, "button[phx-click='editor_undo'][disabled]")
assert has_element?(view, "button[phx-click='editor_redo'][disabled]")
# Make a change
blocks = Pages.get_page("home").blocks
second_id = Enum.at(blocks, 1)["id"]
view
|> element("button[phx-click='editor_move_up'][phx-value-id='#{second_id}']")
|> render_click()
# Undo enabled, redo still disabled
refute has_element?(view, "button[phx-click='editor_undo'][disabled]")
assert has_element?(view, "button[phx-click='editor_redo'][disabled]")
end
end
describe "content pages (deferred init)" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "editing works on about page via deferred init", %{conn: conn} do
{:ok, view, _html} = live(conn, "/about?edit=true")
assert has_element?(view, ".page-editor-sidebar")
assert has_element?(view, ".page-editor-sidebar-title", "About")
assert has_element?(view, ".block-card")
end
end
end