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 "editor sheet is not shown for non-admins", %{conn: conn} do {:ok, _view, html} = live(conn, "/") refute html =~ "editor-sheet" refute html =~ "Edit page" end end describe "editor sheet visibility" do test "admin sees editor sheet with edit button", %{conn: conn, user: user} do conn = log_in_user(conn, user) {:ok, view, _html} = live(conn, "/") assert has_element?(view, ".editor-sheet") assert has_element?(view, "button", "Edit page") end test "non-admin does not see editor sheet", %{conn: conn} do {:ok, view, _html} = live(conn, "/") refute has_element?(view, ".editor-sheet") refute has_element?(view, "button", "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 "clicking edit button enters edit mode", %{conn: conn} do {:ok, view, _html} = live(conn, "/") # Sheet starts collapsed assert has_element?(view, ".editor-sheet[data-state='collapsed']") # Click edit button (the specific edit button class) view |> element(".editor-sheet-edit-btn") |> render_click() # Now editing, sheet expanded assert has_element?(view, ".editor-sheet[data-editing='true']") assert has_element?(view, ".editor-sheet-content") assert has_element?(view, ".block-card") end test "sheet shows the page title when editing", %{conn: conn} do {:ok, view, _html} = live(conn, "/") view |> element(".editor-sheet-edit-btn") |> render_click() assert has_element?(view, ".editor-sheet-page-title", "Home page") end test "sheet state changes when entering edit mode and collapsing", %{conn: conn} do {:ok, view, _html} = live(conn, "/") # Starts collapsed assert has_element?(view, ".editor-sheet[data-state='collapsed']") # Enter edit mode - expands to open view |> element(".editor-sheet-edit-btn") |> render_click() assert has_element?(view, ".editor-sheet[data-state='open']") # Collapse sheet (still in edit mode, just previewing) render_click(view, "editor_set_sheet_state", %{"state" => "collapsed"}) assert has_element?(view, ".editor-sheet[data-state='collapsed']") # Still in edit mode assert has_element?(view, ".editor-sheet[data-editing='true']") 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() # 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() refute has_element?(view, ".editor-sheet-dirty") # 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, ".editor-sheet-dirty") 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() # 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() # Verify persistence updated = Pages.get_page("home") refute List.first(updated.blocks)["type"] == original_first_type end test "reset restores default blocks and is undoable", %{conn: conn} do # First, save a modified page (reverse block order) 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() # Get the first block type before reset (should be reversed, so last default) first_before_reset = view |> element(".block-card:first-child") |> render() # Reset view |> element("button[phx-click='editor_reset_defaults']") |> render_click() # First block should now be the default first (Hero) first_after_reset = view |> element(".block-card:first-child") |> render() assert first_after_reset =~ "Hero" refute first_after_reset == first_before_reset # Reset should be undoable refute has_element?(view, "button[phx-click='editor_undo'][disabled]") # Undo the reset view |> element("button[phx-click='editor_undo']") |> render_click() # Should be back to the reversed order first_after_undo = view |> element(".block-card:first-child") |> render() refute first_after_undo =~ "Hero" 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() 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, "/") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() # 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" do setup %{conn: conn, user: user} do %{conn: log_in_user(conn, user)} end test "editing works on about page", %{conn: conn} do {:ok, view, _html} = live(conn, "/about") # Editor sheet visible for admin assert has_element?(view, ".editor-sheet") # Enter edit mode view |> element(".editor-sheet-edit-btn") |> render_click() assert has_element?(view, ".editor-sheet-content") assert has_element?(view, ".editor-sheet-page-title", "About") assert has_element?(view, ".block-card") end end end