berrypod/test/berrypod_web/page_renderer_test.exs
jamey 3336b3aa26
All checks were successful
deploy / deploy (push) Successful in 1m24s
add page builder polish: utility blocks, templates, duplicate
New block types: spacer, divider, button/CTA, video embed (YouTube,
Vimeo with privacy-enhanced embeds, fallback for unknown URLs).

Page templates (blank, content, landing) shown when creating custom
pages. Duplicate page action on admin index with slug deduplication.

Fix block picker on shop edit sidebar being cut off on mobile by
accounting for bottom nav and making the grid scrollable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 17:33:25 +00:00

367 lines
10 KiB
Elixir

defmodule BerrypodWeb.PageRendererTest do
use BerrypodWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Berrypod.Pages
alias Berrypod.Settings.ThemeSettings
alias BerrypodWeb.PageRenderer
# Minimal assigns that every page needs (the stuff shop_layout requires)
defp base_assigns do
%{
__changed__: nil,
theme_settings: %ThemeSettings{},
logo_image: nil,
header_image: nil,
mode: :shop,
cart_items: [],
cart_count: 0,
cart_subtotal: "£0.00",
cart_total: nil,
cart_drawer_open: false,
cart_status: nil,
is_admin: false,
search_query: "",
search_results: [],
search_open: false,
categories: [],
shipping_estimate: nil,
country_code: "GB",
available_countries: [],
flash: %{}
}
end
defp render_page(slug, extra_assigns \\ %{}) do
page = Pages.get_page(slug)
assigns =
base_assigns()
|> Map.merge(extra_assigns)
|> Map.put(:page, page)
PageRenderer.render_page(assigns)
|> rendered_to_string()
end
describe "render_page/1" do
test "home page renders hero and featured products" do
html = render_page("home", %{products: []})
assert html =~ "Original designs, printed on demand"
assert html =~ "Shop the collection"
assert html =~ "Featured products"
assert html =~ "Made with passion, printed with care"
end
test "about page renders hero and content area" do
html =
render_page("about", %{content_blocks: [%{type: :paragraph, text: "Test about text"}]})
assert html =~ "About the studio"
assert html =~ "content-body"
end
test "delivery page renders hero" do
html = render_page("delivery", %{content_blocks: []})
assert html =~ "Delivery &amp; returns"
end
test "privacy page renders hero" do
html = render_page("privacy", %{content_blocks: []})
assert html =~ "Privacy policy"
end
test "terms page renders hero" do
html = render_page("terms", %{content_blocks: []})
assert html =~ "Terms of service"
end
test "contact page renders hero, form, and sidebar blocks" do
html = render_page("contact", %{tracking_state: :idle})
assert html =~ "Get in touch"
assert html =~ "Send a message"
assert html =~ "Track your order"
assert html =~ "Handy to know"
assert html =~ "Newsletter"
end
test "collection page renders header, filter bar, and grid" do
html = render_page("collection", %{products: [], collection_title: "All Products"})
assert html =~ "All Products"
assert html =~ "filter-bar"
end
test "pdp page renders breadcrumb and product hero when product provided" do
product = %{
id: "test-id",
title: "Test Product",
slug: "test-product",
category: "Art Prints",
description: "A lovely test product",
cheapest_price: 2500,
compare_at_price: nil,
on_sale: false,
in_stock: true,
provider_data: %{}
}
html =
render_page("pdp", %{
product: product,
gallery_images: [],
display_price: 2500,
selected_variant: nil,
option_types: [],
selected_options: %{},
available_options: %{},
option_urls: %{},
quantity: 1,
related_products: [],
reviews: [],
average_rating: 5,
total_count: 0
})
assert html =~ "Art Prints"
assert html =~ "Test Product"
assert html =~ "pdp-grid"
assert html =~ "Add to basket"
end
test "cart page renders empty state when no items" do
html = render_page("cart")
assert html =~ "Your basket"
assert html =~ "cart-empty"
end
test "cart page renders items when present" do
items = [
%{
variant_id: "v1",
name: "Test T-Shirt",
variant: "Black / M",
price: 2500,
quantity: 1,
image: nil,
product_id: "test-t-shirt",
in_stock: true
}
]
html =
render_page("cart", %{
cart_items: items,
cart_count: 1,
cart_subtotal: "£25.00",
cart_page_subtotal: 2500
})
assert html =~ "Your basket"
assert html =~ "cart-page-list"
end
test "search page renders search form" do
html = render_page("search", %{search_page_query: "", search_page_results: []})
assert html =~ "Search"
assert html =~ ~s(name="q")
assert html =~ "Search products..."
end
test "checkout success renders pending state when no order" do
html = render_page("checkout_success", %{order: nil})
assert html =~ "Processing your payment"
end
test "orders page renders empty state when orders nil" do
html = render_page("orders", %{orders: nil, lookup_email: nil})
assert html =~ "Your orders"
assert html =~ "expired or is invalid"
end
test "orders page renders no orders message" do
html = render_page("orders", %{orders: [], lookup_email: "test@example.com"})
assert html =~ "test@example.com"
assert html =~ "No orders found"
end
test "order detail page renders nothing when no order" do
html = render_page("order_detail", %{order: nil})
# Should not crash, just render empty
assert html =~ "main"
end
test "error page renders error hero" do
html =
render_page("error", %{
error_code: "404",
error_title: "Page not found",
error_description: "Sorry, we couldn't find that page.",
products: []
})
assert html =~ "404"
assert html =~ "Page not found"
assert html =~ "Go to Homepage"
end
end
describe "page_main_class/1" do
test "returns correct classes for each page" do
assert PageRenderer.page_main_class("contact") == "page-container contact-main"
assert PageRenderer.page_main_class("cart") == "page-container"
assert PageRenderer.page_main_class("checkout_success") == "page-container checkout-main"
assert PageRenderer.page_main_class("orders") == "page-container orders-main"
assert PageRenderer.page_main_class("order_detail") == "page-container order-detail-main"
assert PageRenderer.page_main_class("error") == "error-main"
assert PageRenderer.page_main_class("about") == "content-page"
assert PageRenderer.page_main_class("home") == nil
end
end
describe "utility blocks" do
setup do
on_exit(fn ->
import Ecto.Query
Berrypod.Repo.delete_all(from p in Berrypod.Pages.Page, where: p.slug == "home")
Berrypod.Pages.PageCache.invalidate_all()
end)
:ok
end
test "spacer block renders with size" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [%{"id" => "blk_sp", "type" => "spacer", "settings" => %{"size" => "large"}}]
})
html = render_page("home", %{products: []})
assert html =~ ~s(class="block-spacer")
assert html =~ ~s(data-size="large")
end
test "divider block renders with style" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [%{"id" => "blk_dv", "type" => "divider", "settings" => %{"style" => "dots"}}]
})
html = render_page("home", %{products: []})
assert html =~ ~s(class="block-divider")
assert html =~ ~s(data-style="dots")
end
test "button block renders themed link" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [
%{
"id" => "blk_btn",
"type" => "button",
"settings" => %{
"text" => "Shop now",
"href" => "/collections/all",
"style" => "primary",
"alignment" => "centre"
}
}
]
})
html = render_page("home", %{products: []})
assert html =~ "Shop now"
assert html =~ ~s(data-align="centre")
assert html =~ "themed-button"
end
test "video embed renders YouTube iframe" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [
%{
"id" => "blk_vid",
"type" => "video_embed",
"settings" => %{
"url" => "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"caption" => "Demo video"
}
}
]
})
html = render_page("home", %{products: []})
assert html =~ "youtube-nocookie.com/embed/dQw4w9WgXcQ"
assert html =~ "Demo video"
assert html =~ "video-embed"
end
test "video embed renders Vimeo iframe" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [
%{
"id" => "blk_vim",
"type" => "video_embed",
"settings" => %{"url" => "https://vimeo.com/123456789"}
}
]
})
html = render_page("home", %{products: []})
assert html =~ "player.vimeo.com/video/123456789"
end
test "video embed shows fallback link for unknown URLs" do
{:ok, _} =
Pages.save_page("home", %{
title: "Home",
blocks: [
%{
"id" => "blk_unk",
"type" => "video_embed",
"settings" => %{"url" => "https://example.com/video", "caption" => "My video"}
}
]
})
html = render_page("home", %{products: []})
assert html =~ "video-embed-fallback"
assert html =~ "My video"
refute html =~ "<iframe"
end
end
describe "format_order_status/1" do
test "maps status strings to display text" do
assert PageRenderer.format_order_status("unfulfilled") == "Being prepared"
assert PageRenderer.format_order_status("shipped") == "On its way"
assert PageRenderer.format_order_status("delivered") == "Delivered"
assert PageRenderer.format_order_status("unknown") == "unknown"
end
end
end