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:
190
test/simpleshop_theme_web/live/admin/orders_test.exs
Normal file
190
test/simpleshop_theme_web/live/admin/orders_test.exs
Normal file
@@ -0,0 +1,190 @@
|
||||
defmodule SimpleshopThemeWeb.Admin.OrdersTest do
|
||||
use SimpleshopThemeWeb.ConnCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import SimpleshopTheme.AccountsFixtures
|
||||
import SimpleshopTheme.OrdersFixtures
|
||||
|
||||
setup do
|
||||
user = user_fixture()
|
||||
%{user: user}
|
||||
end
|
||||
|
||||
describe "unauthenticated" do
|
||||
test "redirects to login", %{conn: conn} do
|
||||
{:error, redirect} = live(conn, ~p"/admin/orders")
|
||||
assert {:redirect, %{to: path}} = redirect
|
||||
assert path == ~p"/users/log-in"
|
||||
end
|
||||
end
|
||||
|
||||
describe "order list" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "renders empty state when no orders", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders")
|
||||
|
||||
assert html =~ "No orders yet"
|
||||
assert html =~ "Orders"
|
||||
end
|
||||
|
||||
test "renders orders table", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid", customer_email: "test@shop.com")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders")
|
||||
|
||||
assert html =~ order.order_number
|
||||
assert html =~ "test@shop.com"
|
||||
assert html =~ "paid"
|
||||
end
|
||||
|
||||
test "filters by status", %{conn: conn} do
|
||||
paid = order_fixture(payment_status: "paid")
|
||||
_pending = order_fixture()
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/orders")
|
||||
|
||||
html = render_click(view, "filter", %{"status" => "paid"})
|
||||
|
||||
assert html =~ paid.order_number
|
||||
end
|
||||
|
||||
test "navigates to order detail", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders")
|
||||
|
||||
assert html =~ ~p"/admin/orders/#{order}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "order detail" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "renders order details", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid", customer_email: "buyer@example.com")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ order.order_number
|
||||
assert html =~ "buyer@example.com"
|
||||
assert html =~ "paid"
|
||||
assert html =~ "Order details"
|
||||
end
|
||||
|
||||
test "shows line items", %{conn: conn} do
|
||||
order = order_fixture(product_name: "Cool T-shirt", variant_title: "Blue / XL")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "Cool T-shirt"
|
||||
assert html =~ "Blue / XL"
|
||||
end
|
||||
|
||||
test "shows shipping address", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, updated_order} =
|
||||
SimpleshopTheme.Orders.update_order(order, %{
|
||||
shipping_address: %{
|
||||
"name" => "Jane Doe",
|
||||
"line1" => "42 Test Street",
|
||||
"city" => "London",
|
||||
"postal_code" => "SW1A 1AA",
|
||||
"country" => "GB"
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{updated_order}")
|
||||
|
||||
assert html =~ "Jane Doe"
|
||||
assert html =~ "42 Test Street"
|
||||
assert html =~ "London"
|
||||
assert html =~ "SW1A 1AA"
|
||||
end
|
||||
|
||||
test "redirects when order not found", %{conn: conn} do
|
||||
fake_id = Ecto.UUID.generate()
|
||||
|
||||
{:error, {:live_redirect, %{to: "/admin/orders"}}} =
|
||||
live(conn, ~p"/admin/orders/#{fake_id}")
|
||||
end
|
||||
|
||||
test "shows fulfilment card", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "Fulfilment"
|
||||
assert html =~ "unfulfilled"
|
||||
end
|
||||
|
||||
test "shows submit button for paid unfulfilled orders", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "Submit to provider"
|
||||
end
|
||||
|
||||
test "shows retry button for failed fulfilment", %{conn: conn} do
|
||||
order = order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, order} =
|
||||
SimpleshopTheme.Orders.update_fulfilment(order, %{fulfilment_status: "failed"})
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "Retry submission"
|
||||
end
|
||||
|
||||
test "shows refresh button for submitted orders", %{conn: conn} do
|
||||
{order, _v, _p, _c} =
|
||||
SimpleshopTheme.OrdersFixtures.submitted_order_fixture()
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "Refresh status"
|
||||
end
|
||||
|
||||
test "shows tracking info when available", %{conn: conn} do
|
||||
{order, _v, _p, _c} =
|
||||
SimpleshopTheme.OrdersFixtures.submitted_order_fixture()
|
||||
|
||||
{:ok, order} =
|
||||
SimpleshopTheme.Orders.update_fulfilment(order, %{
|
||||
fulfilment_status: "shipped",
|
||||
tracking_number: "TRACK123",
|
||||
tracking_url: "https://track.example.com/TRACK123",
|
||||
shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
|
||||
|
||||
assert html =~ "TRACK123"
|
||||
assert html =~ "https://track.example.com/TRACK123"
|
||||
end
|
||||
end
|
||||
|
||||
describe "order list fulfilment column" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "shows fulfilment badge in table", %{conn: conn} do
|
||||
order_fixture(payment_status: "paid")
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/orders")
|
||||
|
||||
assert html =~ "Fulfilment"
|
||||
assert html =~ "unfulfilled"
|
||||
end
|
||||
end
|
||||
end
|
||||
138
test/simpleshop_theme_web/live/admin/settings_test.exs
Normal file
138
test/simpleshop_theme_web/live/admin/settings_test.exs
Normal file
@@ -0,0 +1,138 @@
|
||||
defmodule SimpleshopThemeWeb.Admin.SettingsTest do
|
||||
use SimpleshopThemeWeb.ConnCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import SimpleshopTheme.AccountsFixtures
|
||||
|
||||
alias SimpleshopTheme.Settings
|
||||
|
||||
setup do
|
||||
user = user_fixture()
|
||||
%{user: user}
|
||||
end
|
||||
|
||||
describe "shop status toggle" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "shows offline status by default", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
assert html =~ "Offline"
|
||||
assert html =~ "coming soon"
|
||||
assert html =~ "Go live"
|
||||
end
|
||||
|
||||
test "can go live", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html = render_click(view, "toggle_site_live")
|
||||
|
||||
assert html =~ "Shop is now live"
|
||||
assert html =~ "Live"
|
||||
assert html =~ "Take offline"
|
||||
assert Settings.site_live?()
|
||||
end
|
||||
|
||||
test "can take offline after going live", %{conn: conn} do
|
||||
{:ok, _} = Settings.set_site_live(true)
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html = render_click(view, "toggle_site_live")
|
||||
|
||||
assert html =~ "Shop taken offline"
|
||||
assert html =~ "Offline"
|
||||
assert html =~ "Go live"
|
||||
refute Settings.site_live?()
|
||||
end
|
||||
end
|
||||
|
||||
describe "unauthenticated" do
|
||||
test "redirects to login", %{conn: conn} do
|
||||
{:error, redirect} = live(conn, ~p"/admin/settings")
|
||||
assert {:redirect, %{to: path}} = redirect
|
||||
assert path == ~p"/users/log-in"
|
||||
end
|
||||
end
|
||||
|
||||
describe "authenticated - not configured" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "renders setup form when Stripe is not configured", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
assert html =~ "Settings"
|
||||
assert html =~ "Not connected"
|
||||
assert html =~ "Connect Stripe"
|
||||
assert html =~ "Stripe dashboard"
|
||||
end
|
||||
|
||||
test "shows error for empty API key", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form", %{stripe: %{api_key: ""}})
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Please enter your Stripe secret key"
|
||||
end
|
||||
end
|
||||
|
||||
describe "authenticated - connected (localhost)" do
|
||||
setup %{conn: conn, user: user} do
|
||||
# Pre-configure a Stripe API key
|
||||
Settings.put_secret("stripe_api_key", "sk_test_simulated_key_12345")
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "renders dev mode view with CLI instructions", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
assert html =~ "Dev mode"
|
||||
assert html =~ "sk_test_•••345"
|
||||
assert html =~ "stripe listen"
|
||||
assert html =~ "Webhook signing secret"
|
||||
end
|
||||
|
||||
test "saves manual signing secret", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form", %{webhook: %{signing_secret: "whsec_test_manual_456"}})
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Webhook signing secret saved"
|
||||
assert html =~ "whsec_te•••456"
|
||||
end
|
||||
|
||||
test "shows error for empty signing secret", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form", %{webhook: %{signing_secret: ""}})
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Please enter a signing secret"
|
||||
end
|
||||
|
||||
test "disconnect clears configuration", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/settings")
|
||||
|
||||
html = render_click(view, "disconnect_stripe")
|
||||
|
||||
assert html =~ "Stripe disconnected"
|
||||
assert html =~ "Not connected"
|
||||
assert html =~ "Connect Stripe"
|
||||
refute Settings.has_secret?("stripe_api_key")
|
||||
end
|
||||
end
|
||||
end
|
||||
216
test/simpleshop_theme_web/live/admin/theme_test.exs
Normal file
216
test/simpleshop_theme_web/live/admin/theme_test.exs
Normal file
@@ -0,0 +1,216 @@
|
||||
defmodule SimpleshopThemeWeb.Admin.ThemeTest do
|
||||
use SimpleshopThemeWeb.ConnCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import SimpleshopTheme.AccountsFixtures
|
||||
|
||||
alias SimpleshopTheme.Settings
|
||||
|
||||
setup do
|
||||
user = user_fixture()
|
||||
%{user: user}
|
||||
end
|
||||
|
||||
describe "Index (unauthenticated)" do
|
||||
test "redirects to login when not authenticated", %{conn: conn} do
|
||||
{:error, redirect} = live(conn, ~p"/admin/theme")
|
||||
|
||||
assert {:redirect, %{to: path}} = redirect
|
||||
assert path == ~p"/users/log-in"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Index (authenticated)" do
|
||||
setup %{conn: conn, user: user} do
|
||||
conn = log_in_user(conn, user)
|
||||
%{conn: conn}
|
||||
end
|
||||
|
||||
test "renders theme editor page", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
assert html =~ "Theme Studio"
|
||||
assert html =~ "preset"
|
||||
end
|
||||
|
||||
test "displays all 8 presets", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
assert html =~ "gallery"
|
||||
assert html =~ "studio"
|
||||
assert html =~ "boutique"
|
||||
assert html =~ "bold"
|
||||
assert html =~ "playful"
|
||||
assert html =~ "minimal"
|
||||
assert html =~ "night"
|
||||
assert html =~ "classic"
|
||||
end
|
||||
|
||||
test "displays current theme settings", %{conn: conn} do
|
||||
{:ok, _settings} = Settings.apply_preset(:gallery)
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
assert html =~ "warm"
|
||||
assert html =~ "editorial"
|
||||
assert html =~ "soft"
|
||||
assert html =~ "spacious"
|
||||
end
|
||||
|
||||
test "displays generated CSS in preview", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# CSS generator outputs accent colors and layout variables for shop pages
|
||||
assert html =~ ".themed {"
|
||||
assert html =~ "--t-accent-h:"
|
||||
end
|
||||
|
||||
test "applies preset and updates preview", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button", "gallery")
|
||||
|> render_click()
|
||||
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.mood == "warm"
|
||||
assert theme_settings.typography == "editorial"
|
||||
assert html =~ "warm"
|
||||
assert html =~ "editorial"
|
||||
end
|
||||
|
||||
test "switches preview page", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("button", "Collection")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ "All Products"
|
||||
end
|
||||
|
||||
test "theme settings are saved when applying a preset", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Apply a preset
|
||||
view
|
||||
|> element("button", "gallery")
|
||||
|> render_click()
|
||||
|
||||
# Verify settings were persisted
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.mood == "warm"
|
||||
assert theme_settings.typography == "editorial"
|
||||
end
|
||||
|
||||
test "all preview page buttons are present", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
assert html =~ "Home"
|
||||
assert html =~ "Collection"
|
||||
assert html =~ "Product"
|
||||
assert html =~ "Cart"
|
||||
assert html =~ "About"
|
||||
assert html =~ "Contact"
|
||||
assert html =~ "404"
|
||||
end
|
||||
|
||||
test "mood customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "dark" mood button
|
||||
html =
|
||||
view
|
||||
|> element("button[phx-value-setting_value='dark']")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.mood == "dark"
|
||||
assert html =~ "dark"
|
||||
end
|
||||
|
||||
test "shape customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "round" shape button
|
||||
view
|
||||
|> element("button[phx-value-setting_value='round']")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.shape == "round"
|
||||
end
|
||||
|
||||
test "density customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "compact" density button
|
||||
view
|
||||
|> element("button[phx-value-setting_value='compact']")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.density == "compact"
|
||||
end
|
||||
|
||||
test "grid columns customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "2 columns" grid columns button
|
||||
view
|
||||
|> element("button", "2 columns")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.grid_columns == "2"
|
||||
end
|
||||
|
||||
test "typography customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "modern" typography button
|
||||
view
|
||||
|> element("button[phx-value-setting_value='modern']")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.typography == "modern"
|
||||
end
|
||||
|
||||
test "header layout customization buttons work", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Click the "centered" header layout button
|
||||
view
|
||||
|> element("button[phx-value-setting_value='centered']")
|
||||
|> render_click()
|
||||
|
||||
# Verify the setting was updated
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
assert theme_settings.header_layout == "centered"
|
||||
end
|
||||
|
||||
test "CSS regenerates when settings change", %{conn: conn} do
|
||||
{:ok, view, html} = live(conn, ~p"/admin/theme")
|
||||
|
||||
# Capture initial CSS
|
||||
initial_css = html
|
||||
|
||||
# Change a setting
|
||||
new_html =
|
||||
view
|
||||
|> element("button[phx-value-setting_value='dark']")
|
||||
|> render_click()
|
||||
|
||||
# Verify CSS has changed
|
||||
refute initial_css == new_html
|
||||
assert new_html =~ "--t-accent-h:"
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user