restructure LiveView directories: admin/, shop/, auth/

Consolidates admin_live/, theme_live/, provider_live/ into admin/
(with theme/ and providers/ subdirs). Renames shop_live/ to shop/
and user_live/ to auth/. Updates all module names, router refs,
test files, CSS source paths, and dialyzer ignore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-12 00:16:32 +00:00
parent e64bf40a71
commit deea04885f
38 changed files with 56 additions and 56 deletions

View File

@@ -0,0 +1,111 @@
defmodule SimpleshopThemeWeb.Shop.CartTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias SimpleshopTheme.ProductsFixtures
defp create_cart_with_product(_context) do
product = ProductsFixtures.complete_product_fixture(%{title: "Test Art Print"})
variant = List.first(product.variants)
%{product: product, variant: variant}
end
defp conn_with_cart(conn, variant_id, qty \\ 1) do
conn
|> Phoenix.ConnTest.init_test_session(%{"cart" => [{variant_id, qty}]})
end
describe "Empty cart" do
test "renders empty cart state", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/cart")
assert html =~ "Your basket"
assert html =~ "Your basket is empty"
end
test "shows continue shopping link when empty", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/cart")
assert html =~ "Continue shopping"
end
end
describe "Cart with items" do
setup [:create_cart_with_product]
test "displays cart item name", %{conn: conn, product: product, variant: variant} do
{:ok, _view, html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
assert html =~ product.title
end
test "displays order summary", %{conn: conn, variant: variant} do
{:ok, _view, html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
assert html =~ "Order summary"
assert html =~ "Subtotal"
end
test "displays formatted subtotal", %{conn: conn, variant: variant} do
{:ok, _view, html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
assert html =~ SimpleshopTheme.Cart.format_price(variant.price)
end
test "displays checkout button", %{conn: conn, variant: variant} do
{:ok, _view, html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
assert html =~ "Checkout"
end
test "incrementing quantity updates the display", %{
conn: conn,
product: product,
variant: variant
} do
{:ok, view, _html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
html =
view
|> element("#main-content button[aria-label='Increase quantity of #{product.title}']")
|> render_click()
assert html =~ "Quantity updated to 2"
end
test "decrementing to zero removes the item", %{
conn: conn,
product: product,
variant: variant
} do
{:ok, view, _html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
html =
view
|> element("#main-content button[aria-label='Decrease quantity of #{product.title}']")
|> render_click()
assert html =~ "Your basket is empty"
end
test "remove button removes the item", %{conn: conn, product: product, variant: variant} do
{:ok, view, _html} = conn |> conn_with_cart(variant.id) |> live(~p"/cart")
html =
view
|> element("#main-content button[aria-label='Remove #{product.title} from cart']")
|> render_click()
assert html =~ "Your basket is empty"
end
end
describe "Cart page title" do
test "page title is Cart", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/cart")
assert html =~ "<title>Cart</title>"
end
end
end

View File

@@ -0,0 +1,154 @@
defmodule SimpleshopThemeWeb.Shop.CollectionTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias SimpleshopTheme.Theme.PreviewData
describe "Collection page" do
test "renders collection page for /collections/all", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
assert html =~ "All Products"
end
test "renders collection page for specific category", %{conn: conn} do
category = List.first(PreviewData.categories())
{:ok, _view, html} = live(conn, ~p"/collections/#{category.slug}")
assert html =~ category.name
end
test "displays products", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
products = PreviewData.products()
first_product = List.first(products)
assert html =~ first_product.name
end
test "displays category filter buttons", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
categories = PreviewData.categories()
for category <- categories do
assert html =~ category.name
end
end
test "displays sort dropdown", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
assert html =~ "Featured"
assert html =~ "Newest"
assert html =~ "Price: Low to High"
assert html =~ "Price: High to Low"
assert html =~ "Name: A-Z"
assert html =~ "Name: Z-A"
end
test "redirects to /collections/all for unknown category", %{conn: conn} do
{:error, {:live_redirect, %{to: to, flash: flash}}} =
live(conn, ~p"/collections/nonexistent")
assert to == "/collections/all"
assert flash["error"] == "Collection not found"
end
test "filters products by category", %{conn: conn} do
category = List.first(PreviewData.categories())
{:ok, _view, html} = live(conn, ~p"/collections/#{category.slug}")
products = PreviewData.products_by_category(category.slug)
for product <- products do
assert html =~ product.name
end
end
end
describe "Sorting" do
test "sorts by price ascending", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/collections/all")
html =
view
|> element("form[phx-change='sort_changed']")
|> render_change(%{sort: "price_asc"})
assert html =~ "Price: Low to High"
end
test "sorts by price descending", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/collections/all")
html =
view
|> element("form[phx-change='sort_changed']")
|> render_change(%{sort: "price_desc"})
assert html =~ "Price: High to Low"
end
test "sorts by name ascending", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/collections/all")
html =
view
|> element("form[phx-change='sort_changed']")
|> render_change(%{sort: "name_asc"})
assert html =~ "Name: A-Z"
end
test "sorts by name descending", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/collections/all")
html =
view
|> element("form[phx-change='sort_changed']")
|> render_change(%{sort: "name_desc"})
assert html =~ "Name: Z-A"
end
test "sort parameter is preserved in URL", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/collections/all?sort=price_asc")
html = render(view)
assert html =~ "selected"
end
test "default sort is featured", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
assert html =~ ~r/<option[^>]*value="featured"[^>]*selected/
end
end
describe "Navigation" do
test "category links preserve sort order", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all?sort=price_desc")
category = List.first(PreviewData.categories())
assert html =~ "/collections/#{category.slug}?sort=price_desc"
end
test "All button link preserves sort order", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/art-prints?sort=name_asc")
assert html =~ "/collections/all?sort=name_asc"
end
test "featured sort does not include query param in links", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/collections/all")
category = List.first(PreviewData.categories())
assert html =~ ~s(href="/collections/#{category.slug}")
refute html =~ "/collections/#{category.slug}?sort=featured"
end
end
end

View File

@@ -0,0 +1,69 @@
defmodule SimpleshopThemeWeb.Shop.ComingSoonTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import SimpleshopTheme.AccountsFixtures
alias SimpleshopTheme.Settings
describe "coming soon page" do
test "renders when site is not live and admin exists", %{conn: conn} do
user_fixture()
{:ok, _view, html} = live(conn, ~p"/coming-soon")
assert html =~ "Coming soon"
assert html =~ "getting things ready"
end
test "displays the shop name", %{conn: conn} do
{:ok, _} = Settings.update_theme_settings(%{site_name: "My Test Shop"})
{:ok, _view, html} = live(conn, ~p"/coming-soon")
assert html =~ "My Test Shop"
end
end
describe "site live gate" do
test "redirects unauthenticated visitors to coming soon when not live", %{conn: conn} do
# Create admin so the gate activates (fresh installs bypass)
user_fixture()
assert {:error, {:redirect, %{to: "/coming-soon"}}} = live(conn, ~p"/")
end
test "allows authenticated admin through when not live", %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Shop the collection"
end
test "allows everyone through when site is live", %{conn: conn} do
user_fixture()
{:ok, _} = Settings.set_site_live(true)
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Shop the collection"
end
test "allows everyone through on fresh install (no admin)", %{conn: conn} do
# No admin created — fresh install shows the demo shop
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Shop the collection"
end
test "gates all public shop routes", %{conn: conn} do
user_fixture()
assert {:error, {:redirect, %{to: "/coming-soon"}}} = live(conn, ~p"/about")
assert {:error, {:redirect, %{to: "/coming-soon"}}} = live(conn, ~p"/collections/all")
assert {:error, {:redirect, %{to: "/coming-soon"}}} = live(conn, ~p"/cart")
end
end
end

View File

@@ -0,0 +1,89 @@
defmodule SimpleshopThemeWeb.Shop.ContentTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
describe "About page" do
test "renders about page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/about")
assert html =~ "About the studio"
assert html =~ "sample about page"
end
test "displays about image", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/about")
assert html =~ "night-sky-blanket"
end
end
describe "Delivery page" do
test "renders delivery page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/delivery")
assert html =~ "Delivery &amp; returns"
assert html =~ "shipping and returns"
end
test "displays delivery content", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/delivery")
assert html =~ "Shipping"
assert html =~ "Returns &amp; exchanges"
assert html =~ "Cancellations"
end
test "displays list items", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/delivery")
assert html =~ "United Kingdom"
assert html =~ "58 business days"
end
end
describe "Privacy page" do
test "renders privacy page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/privacy")
assert html =~ "Privacy policy"
assert html =~ "personal information"
end
test "displays privacy content", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/privacy")
assert html =~ "What we collect"
assert html =~ "Cookies"
assert html =~ "Your rights"
end
end
describe "Terms page" do
test "renders terms page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/terms")
assert html =~ "Terms of service"
assert html =~ "The legal bits"
end
test "displays terms content", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/terms")
assert html =~ "Products"
assert html =~ "Orders &amp; payment"
assert html =~ "Intellectual property"
end
end
describe "Footer links" do
test "footer contains policy page links", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/about")
assert html =~ ~s(href="/delivery")
assert html =~ ~s(href="/privacy")
assert html =~ ~s(href="/terms")
assert html =~ ~s(href="/contact")
end
end
end

View File

@@ -0,0 +1,89 @@
defmodule SimpleshopThemeWeb.Shop.HomeTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias SimpleshopTheme.Theme.PreviewData
describe "Home page" do
test "renders the home page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Original designs, printed on demand"
end
test "renders hero section with CTA", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Shop the collection"
end
test "renders category navigation", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
categories = PreviewData.categories()
for category <- Enum.take(categories, 3) do
assert html =~ category.name
end
end
test "renders featured products section", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Featured products"
products = PreviewData.products()
first_product = List.first(products)
assert html =~ first_product.name
end
test "renders image and text section", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ "Made with passion, printed with care"
assert html =~ "Learn more about the studio"
end
test "renders header with shop name", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
# Header should be present (part of shop_layout)
assert html =~ "SimpleShop"
end
test "renders footer with links", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ ~s(href="/about")
assert html =~ ~s(href="/contact")
end
end
describe "Navigation links" do
test "category links point to collections", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ ~s(href="/collections/)
end
test "product links point to product pages", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ ~s(href="/products/)
end
test "hero CTA links to collections", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ ~s(href="/collections/all")
end
test "about link in image section", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
assert html =~ ~s(href="/about")
end
end
end

View File

@@ -0,0 +1,284 @@
defmodule SimpleshopThemeWeb.Shop.ProductShowTest do
use SimpleshopThemeWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias SimpleshopTheme.Theme.PreviewData
describe "Product detail page" do
test "renders product page with product name", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ product.name
end
test "renders product description", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ product.description
end
test "renders product price", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ SimpleshopTheme.Cart.format_price(product.price)
end
test "renders breadcrumb with category link", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ product.category
assert html =~ "/collections/"
end
test "renders add to cart button", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ "Add to basket"
end
test "renders related products section", %{conn: conn} do
product = List.first(PreviewData.products())
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
# Should show other products, not the current one
other_product = Enum.at(PreviewData.products(), 1)
assert html =~ other_product.name
end
end
describe "Variant selection" do
test "renders variant selectors for product with options", %{conn: conn} do
# Product "1" (Mountain Sunrise Art Print) has Size options
{:ok, _view, html} = live(conn, ~p"/products/1")
assert html =~ "Size"
assert html =~ "8×10"
assert html =~ "12×18"
assert html =~ "18×24"
end
test "renders color and size selectors for apparel", %{conn: conn} do
# Product "6" (Forest Silhouette T-Shirt) has Color and Size options
{:ok, _view, html} = live(conn, ~p"/products/6")
assert html =~ "Color"
assert html =~ "Size"
end
test "selecting a size updates the price", %{conn: conn} do
# Product "1" has variants: 8×10 = £19.99, 12×18 = £24.00, 18×24 = £32.00
{:ok, view, _html} = live(conn, ~p"/products/1")
html =
view
|> element("button[phx-value-value='18×24']")
|> render_click()
assert html =~ "£32.00"
end
test "selecting a colour updates available sizes", %{conn: conn} do
# Product "6": White / XL and White / 2XL are unavailable
{:ok, view, _html} = live(conn, ~p"/products/6")
html =
view
|> element("button[aria-label='Select White']")
|> render_click()
# XL should be disabled (unavailable in White)
assert html =~ "disabled"
end
test "shows single variant for products with one option", %{conn: conn} do
# Product "2" (Ocean Waves Art Print) has a single size variant
{:ok, _view, html} = live(conn, ~p"/products/2")
assert html =~ "12×18"
end
end
describe "Quantity selector" do
test "renders quantity selector with initial value of 1", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, "button[phx-click='decrement_quantity']")
assert has_element?(view, "button[phx-click='increment_quantity']")
end
test "decrement button is disabled at quantity 1", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, "button[phx-click='decrement_quantity'][disabled]")
end
test "increment increases quantity", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
html =
view
|> element("button[phx-click='increment_quantity']")
|> render_click()
# Quantity should now be 2, decrement no longer disabled
refute html =~ ~s(phx-click="decrement_quantity" disabled)
end
test "decrement decreases quantity", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
# Increment twice to get to 3
view |> element("button[phx-click='increment_quantity']") |> render_click()
view |> element("button[phx-click='increment_quantity']") |> render_click()
html =
view
|> element("button[phx-click='decrement_quantity']")
|> render_click()
# Should be back to 2 — decrement still enabled
refute html =~ ~s(phx-click="decrement_quantity" disabled)
end
test "quantity resets to 1 after adding to cart", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
# Increment to 3
view |> element("button[phx-click='increment_quantity']") |> render_click()
view |> element("button[phx-click='increment_quantity']") |> render_click()
# Add to cart
html =
view
|> element("button", "Add to basket")
|> render_click()
# Decrement should be disabled again (quantity reset to 1)
assert html =~ ~s(phx-click="decrement_quantity")
assert html =~ "disabled"
end
end
describe "Add to cart" do
test "add to cart opens the cart drawer", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
html =
view
|> element("button", "Add to basket")
|> render_click()
# Cart drawer should now be open (the aria live region gets updated)
assert html =~ "added to cart"
end
test "add to cart updates cart count", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
html =
view
|> element("button", "Add to basket")
|> render_click()
# The cart drawer should show the item
assert html =~ "Mountain Sunrise Art Print"
end
end
describe "Product gallery" do
test "renders carousel with hook and accessibility attrs", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/products/1")
assert html =~ ~s(phx-hook="ProductImageScroll")
assert html =~ ~s(role="region")
assert html =~ ~s(aria-label="Product images")
end
test "renders all gallery images with alt text", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/products/1")
product = List.first(PreviewData.products())
# Each image should have descriptive alt text
assert html =~ "#{product.name} — image 1 of"
assert html =~ "#{product.name} — image 2 of"
end
test "renders dot indicators for multi-image gallery", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, ".product-image-dots")
assert has_element?(view, ".product-image-dot")
end
test "renders thumbnail grid for multi-image gallery", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, ".pdp-gallery-thumbs")
assert has_element?(view, ".pdp-thumbnail")
end
test "renders prev/next navigation arrows", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, "button.pdp-nav-prev")
assert has_element?(view, "button.pdp-nav-next")
assert has_element?(view, ~s(button[aria-label="Previous image"]))
assert has_element?(view, ~s(button[aria-label="Next image"]))
end
test "renders lightbox dialog", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, "dialog#pdp-lightbox")
end
test "renders lightbox click target for desktop", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
assert has_element?(view, ".pdp-lightbox-click")
end
test "thumbnails have correct aria-labels", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/products/1")
assert html =~ "View image 1 of"
assert html =~ "View image 2 of"
end
end
describe "Product gallery edge cases" do
import Phoenix.LiveViewTest
test "single image renders without carousel or dots", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/products/1")
# The live page always has multiple images due to padding, so test
# that the component correctly renders by checking structure exists
# (single-image case would need component-level testing)
assert has_element?(view, ".pdp-gallery-carousel")
end
end
describe "Navigation" do
test "product links navigate to correct product page", %{conn: conn} do
product = Enum.at(PreviewData.products(), 1)
{:ok, _view, html} = live(conn, ~p"/products/#{product.id}")
assert html =~ product.name
end
test "falls back to first product for unknown ID", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/products/nonexistent")
first_product = List.first(PreviewData.products())
assert html =~ first_product.name
end
end
end