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:
111
test/simpleshop_theme_web/live/shop/cart_test.exs
Normal file
111
test/simpleshop_theme_web/live/shop/cart_test.exs
Normal 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
|
||||
154
test/simpleshop_theme_web/live/shop/collection_test.exs
Normal file
154
test/simpleshop_theme_web/live/shop/collection_test.exs
Normal 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
|
||||
69
test/simpleshop_theme_web/live/shop/coming_soon_test.exs
Normal file
69
test/simpleshop_theme_web/live/shop/coming_soon_test.exs
Normal 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
|
||||
89
test/simpleshop_theme_web/live/shop/content_test.exs
Normal file
89
test/simpleshop_theme_web/live/shop/content_test.exs
Normal 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 & 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 & 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 =~ "5–8 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 & 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
|
||||
89
test/simpleshop_theme_web/live/shop/home_test.exs
Normal file
89
test/simpleshop_theme_web/live/shop/home_test.exs
Normal 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
|
||||
284
test/simpleshop_theme_web/live/shop/product_show_test.exs
Normal file
284
test/simpleshop_theme_web/live/shop/product_show_test.exs
Normal 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
|
||||
Reference in New Issue
Block a user