berrypod/test/simpleshop_theme/cart_test.exs
jamey f244a42546 feat: add cart page, cart drawer, and shared cart infrastructure
- Cart context with pure functions for add/remove/update/hydrate
- Price formatting via ex_money (replaces all float division)
- CartHook on_mount with attach_hook for shared event handlers
  (open/close drawer, remove item, PubSub sync)
- Accessible cart drawer with focus trap, scroll lock, aria-live
- Cart page with increment/decrement quantity controls
- Preview mode cart drawer support in theme editor
- Cart persistence to session via JS hook + API endpoint
- 19 tests covering all Cart pure functions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 00:51:16 +00:00

174 lines
4.6 KiB
Elixir

defmodule SimpleshopTheme.CartTest do
use ExUnit.Case, async: true
alias SimpleshopTheme.Cart
describe "add_item/3" do
test "adds a new item to an empty cart" do
cart = Cart.add_item([], "variant-1")
assert cart == [{"variant-1", 1}]
end
test "adds a new item with custom quantity" do
cart = Cart.add_item([], "variant-1", 3)
assert cart == [{"variant-1", 3}]
end
test "increments quantity for existing item" do
cart = [{"variant-1", 2}]
cart = Cart.add_item(cart, "variant-1", 1)
assert cart == [{"variant-1", 3}]
end
test "adds different items separately" do
cart =
[]
|> Cart.add_item("variant-1", 1)
|> Cart.add_item("variant-2", 2)
assert cart == [{"variant-1", 1}, {"variant-2", 2}]
end
end
describe "update_quantity/3" do
test "updates the quantity of an existing item" do
cart = [{"variant-1", 2}]
cart = Cart.update_quantity(cart, "variant-1", 5)
assert cart == [{"variant-1", 5}]
end
test "removes item when quantity is zero" do
cart = [{"variant-1", 2}]
cart = Cart.update_quantity(cart, "variant-1", 0)
assert cart == []
end
test "removes item when quantity is negative" do
cart = [{"variant-1", 2}]
cart = Cart.update_quantity(cart, "variant-1", -1)
assert cart == []
end
test "does nothing for non-existent item" do
cart = [{"variant-1", 2}]
cart = Cart.update_quantity(cart, "variant-999", 5)
assert cart == [{"variant-1", 2}]
end
end
describe "remove_item/2" do
test "removes an item from the cart" do
cart = [{"variant-1", 2}, {"variant-2", 1}]
cart = Cart.remove_item(cart, "variant-1")
assert cart == [{"variant-2", 1}]
end
test "does nothing for non-existent item" do
cart = [{"variant-1", 2}]
cart = Cart.remove_item(cart, "variant-999")
assert cart == [{"variant-1", 2}]
end
test "returns empty list when removing last item" do
cart = Cart.remove_item([{"variant-1", 1}], "variant-1")
assert cart == []
end
end
describe "get_quantity/2" do
test "returns quantity for existing item" do
cart = [{"variant-1", 3}]
assert Cart.get_quantity(cart, "variant-1") == 3
end
test "returns 0 for non-existent item" do
assert Cart.get_quantity([], "variant-1") == 0
end
end
describe "item_count/1" do
test "sums all quantities" do
cart = [{"variant-1", 2}, {"variant-2", 3}]
assert Cart.item_count(cart) == 5
end
test "returns 0 for empty cart" do
assert Cart.item_count([]) == 0
end
end
describe "format_price/1" do
test "formats pence as GBP string" do
assert Cart.format_price(2400) == "£24.00"
end
test "formats with pence correctly" do
assert Cart.format_price(1499) == "£14.99"
end
test "formats zero" do
assert Cart.format_price(0) == "£0.00"
end
test "formats single digit pence with padding" do
assert Cart.format_price(105) == "£1.05"
end
test "returns fallback for non-integer" do
assert Cart.format_price(nil) == "£0.00"
assert Cart.format_price("foo") == "£0.00"
end
end
describe "calculate_subtotal/1" do
test "sums price * quantity for all items" do
items = [
%{price: 2400, quantity: 1},
%{price: 1499, quantity: 2}
]
assert Cart.calculate_subtotal(items) == 5398
end
test "returns 0 for empty list" do
assert Cart.calculate_subtotal([]) == 0
end
end
describe "serialize/1 and deserialize/1" do
test "round-trips cart data" do
cart = [{"variant-1", 2}, {"variant-2", 1}]
assert cart == cart |> Cart.serialize() |> Cart.deserialize()
end
test "serialize converts tuples to lists" do
assert Cart.serialize([{"v1", 3}]) == [["v1", 3]]
end
test "deserialize drops malformed entries" do
assert Cart.deserialize([[1, 2]]) == []
assert Cart.deserialize([["valid", 1], [nil, 2]]) == [{"valid", 1}]
end
test "deserialize handles non-list input" do
assert Cart.deserialize(nil) == []
assert Cart.deserialize("garbage") == []
end
end
describe "get_from_session/1" do
test "returns cart items from session" do
session = %{"cart" => [{"v1", 2}]}
assert Cart.get_from_session(session) == [{"v1", 2}]
end
test "returns empty list when no cart in session" do
assert Cart.get_from_session(%{}) == []
end
test "returns empty list for invalid cart data" do
assert Cart.get_from_session(%{"cart" => "garbage"}) == []
end
end
end