add no-JS progressive enhancement for all shop flows
All checks were successful
deploy / deploy (push) Successful in 1m23s
All checks were successful
deploy / deploy (push) Successful in 1m23s
Every key shop flow now works via plain HTML forms when JS is unavailable. LiveView progressively enhances when JS connects. - PDP: form wraps variant/qty/add-to-cart with action="/cart/add" - Cart page: qty +/- and remove use form POST fallbacks - Cart/search header icons are now links with phx-click enhancement - Collection sort form has GET action + noscript submit button - New /search page with form-based search for no-JS users - CartController gains add/remove/update_item POST actions - CartHook gains update_quantity_form and remove_item_form handlers - Fix flaky analytics tests caused by event table pollution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,11 @@ defmodule Berrypod.Analytics.RetentionWorkerTest do
|
||||
alias Berrypod.Analytics.{Event, RetentionWorker}
|
||||
alias Berrypod.Repo
|
||||
|
||||
setup do
|
||||
Repo.delete_all(Event)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "deletes events older than 12 months" do
|
||||
old = DateTime.add(DateTime.utc_now(), -400, :day) |> DateTime.truncate(:second)
|
||||
recent = DateTime.utc_now() |> DateTime.truncate(:second)
|
||||
|
||||
111
test/berrypod_web/controllers/cart_controller_test.exs
Normal file
111
test/berrypod_web/controllers/cart_controller_test.exs
Normal file
@@ -0,0 +1,111 @@
|
||||
defmodule BerrypodWeb.CartControllerTest do
|
||||
use BerrypodWeb.ConnCase, async: false
|
||||
|
||||
import Berrypod.AccountsFixtures
|
||||
|
||||
alias Berrypod.ProductsFixtures
|
||||
|
||||
setup do
|
||||
user_fixture()
|
||||
{:ok, _} = Berrypod.Settings.set_site_live(true)
|
||||
:ok
|
||||
end
|
||||
|
||||
defp create_variant(_context) do
|
||||
product = ProductsFixtures.complete_product_fixture(%{title: "Test Print"})
|
||||
variant = List.first(product.variants)
|
||||
%{product: product, variant: variant}
|
||||
end
|
||||
|
||||
defp conn_with_cart(conn, variant_id, qty) do
|
||||
Phoenix.ConnTest.init_test_session(conn, %{"cart" => [{variant_id, qty}]})
|
||||
end
|
||||
|
||||
describe "POST /cart/add" do
|
||||
setup [:create_variant]
|
||||
|
||||
test "adds item to session cart and redirects to /cart", %{conn: conn, variant: variant} do
|
||||
conn = post(conn, ~p"/cart/add", %{"variant_id" => variant.id, "quantity" => "1"})
|
||||
|
||||
assert redirected_to(conn) == "/cart"
|
||||
assert Phoenix.Flash.get(conn.assigns.flash, :info) == "Added to basket"
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert [{variant_id, 1}] = cart
|
||||
assert variant_id == variant.id
|
||||
end
|
||||
|
||||
test "increments quantity if item already in cart", %{conn: conn, variant: variant} do
|
||||
conn =
|
||||
conn
|
||||
|> conn_with_cart(variant.id, 2)
|
||||
|> post(~p"/cart/add", %{"variant_id" => variant.id, "quantity" => "3"})
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert [{_, 5}] = cart
|
||||
end
|
||||
|
||||
test "defaults quantity to 1 for invalid values", %{conn: conn, variant: variant} do
|
||||
conn = post(conn, ~p"/cart/add", %{"variant_id" => variant.id, "quantity" => "abc"})
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert [{_, 1}] = cart
|
||||
end
|
||||
|
||||
test "handles missing params gracefully", %{conn: conn} do
|
||||
conn = post(conn, ~p"/cart/add", %{})
|
||||
|
||||
assert redirected_to(conn) == "/cart"
|
||||
assert Phoenix.Flash.get(conn.assigns.flash, :error) == "Could not add item to basket"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /cart/remove" do
|
||||
setup [:create_variant]
|
||||
|
||||
test "removes item from session cart", %{conn: conn, variant: variant} do
|
||||
conn =
|
||||
conn
|
||||
|> conn_with_cart(variant.id, 2)
|
||||
|> post(~p"/cart/remove", %{"variant_id" => variant.id})
|
||||
|
||||
assert redirected_to(conn) == "/cart"
|
||||
assert Phoenix.Flash.get(conn.assigns.flash, :info) == "Removed from basket"
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert cart == []
|
||||
end
|
||||
|
||||
test "handles removing non-existent item", %{conn: conn} do
|
||||
conn = post(conn, ~p"/cart/remove", %{"variant_id" => Ecto.UUID.generate()})
|
||||
|
||||
assert redirected_to(conn) == "/cart"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /cart/update" do
|
||||
setup [:create_variant]
|
||||
|
||||
test "updates quantity in session cart", %{conn: conn, variant: variant} do
|
||||
conn =
|
||||
conn
|
||||
|> conn_with_cart(variant.id, 1)
|
||||
|> post(~p"/cart/update", %{"variant_id" => variant.id, "quantity" => "5"})
|
||||
|
||||
assert redirected_to(conn) == "/cart"
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert [{_, 5}] = cart
|
||||
end
|
||||
|
||||
test "removes item when quantity is 0 or less", %{conn: conn, variant: variant} do
|
||||
conn =
|
||||
conn
|
||||
|> conn_with_cart(variant.id, 3)
|
||||
|> post(~p"/cart/update", %{"variant_id" => variant.id, "quantity" => "0"})
|
||||
|
||||
cart = get_session(conn, "cart")
|
||||
assert cart == []
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,7 @@ defmodule BerrypodWeb.Admin.AnalyticsTest do
|
||||
setup do
|
||||
send(Buffer, :flush)
|
||||
:timer.sleep(50)
|
||||
Repo.delete_all(Event)
|
||||
|
||||
user = user_fixture()
|
||||
%{user: user}
|
||||
|
||||
@@ -68,41 +68,39 @@ defmodule BerrypodWeb.Shop.CartTest do
|
||||
|
||||
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()
|
||||
|> form("#main-content form[phx-submit='update_quantity_form']")
|
||||
|> render_submit(%{"quantity" => "2"})
|
||||
|
||||
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()
|
||||
|> form("#main-content form[phx-submit='update_quantity_form']")
|
||||
|> render_submit(%{"quantity" => "0"})
|
||||
|
||||
assert html =~ "Your basket is empty"
|
||||
end
|
||||
|
||||
test "remove button removes the item", %{conn: conn, product: product, variant: variant} do
|
||||
test "remove button removes the item", %{conn: conn, 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()
|
||||
|> form("#main-content form[phx-submit='remove_item_form']")
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Your basket is empty"
|
||||
end
|
||||
|
||||
@@ -292,11 +292,11 @@ defmodule BerrypodWeb.Shop.ProductShowTest do
|
||||
view |> element("button[phx-click='increment_quantity']") |> render_click()
|
||||
view |> element("button[phx-click='increment_quantity']") |> render_click()
|
||||
|
||||
# Add to cart
|
||||
# Add to cart via form submit
|
||||
html =
|
||||
view
|
||||
|> element("button", "Add to basket")
|
||||
|> render_click()
|
||||
|> form("form[phx-submit='add_to_cart']")
|
||||
|> render_submit()
|
||||
|
||||
# Decrement should be disabled again (quantity reset to 1)
|
||||
assert html =~ ~s(phx-click="decrement_quantity")
|
||||
@@ -310,8 +310,8 @@ defmodule BerrypodWeb.Shop.ProductShowTest do
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button", "Add to basket")
|
||||
|> render_click()
|
||||
|> form("form[phx-submit='add_to_cart']")
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "added to cart"
|
||||
end
|
||||
@@ -321,8 +321,8 @@ defmodule BerrypodWeb.Shop.ProductShowTest do
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button", "Add to basket")
|
||||
|> render_click()
|
||||
|> form("form[phx-submit='add_to_cart']")
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Mountain Sunrise Print"
|
||||
end
|
||||
|
||||
57
test/berrypod_web/live/shop/search_page_test.exs
Normal file
57
test/berrypod_web/live/shop/search_page_test.exs
Normal file
@@ -0,0 +1,57 @@
|
||||
defmodule BerrypodWeb.Shop.SearchPageTest do
|
||||
use BerrypodWeb.ConnCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import Berrypod.AccountsFixtures
|
||||
|
||||
alias Berrypod.ProductsFixtures
|
||||
|
||||
setup do
|
||||
user_fixture()
|
||||
{:ok, _} = Berrypod.Settings.set_site_live(true)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "GET /search" do
|
||||
test "renders search page with empty query", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/search")
|
||||
|
||||
assert html =~ "Search"
|
||||
assert html =~ ~s(placeholder="Search products...")
|
||||
end
|
||||
|
||||
test "renders search results for a matching query", %{conn: conn} do
|
||||
product =
|
||||
ProductsFixtures.complete_product_fixture(%{title: "Blue Mountain Print"})
|
||||
|
||||
Berrypod.Search.index_product(product)
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/search?q=mountain")
|
||||
|
||||
assert html =~ "Blue Mountain Print"
|
||||
assert html =~ "1 result"
|
||||
end
|
||||
|
||||
test "renders no-results message for unmatched query", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/search?q=nonexistent")
|
||||
|
||||
assert html =~ "No products found"
|
||||
end
|
||||
|
||||
test "search form submits via LiveView and updates URL", %{conn: conn} do
|
||||
product =
|
||||
ProductsFixtures.complete_product_fixture(%{title: "Red Sunset Poster"})
|
||||
|
||||
Berrypod.Search.index_product(product)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/search")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form(".search-page-form", %{"q" => "sunset"})
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Red Sunset Poster"
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user