diff --git a/PROGRESS.md b/PROGRESS.md index ab81ec8..673933a 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -43,11 +43,11 @@ Plans: [admin-redesign.md](docs/plans/admin-redesign.md) | [setup-wizard.md](doc | ~~11~~ | ~~Theme editor back-to-admin link~~ | 6 | 30m | done | | ~~4~~ | ~~Admin bar on shop pages~~ | — | 1h | done | | ~~12~~ | ~~Consolidate settings page~~ | 6, 7 | 2-3h | done | +| ~~13~~ | ~~Admin dashboard (+ setup checklist)~~ | 6, 7, 9 | 2h | done | +| ~~15~~ | ~~Setup wizard + admin tests~~ | 13 | 1.5h | done | | | **Next up** | | | | -| 13 | Admin dashboard (+ setup checklist) | 6, 7, 9 | 2h | | | 5 | Search (functional search with results) | — | 3-4h | | | | **Needs admin stable** | | | | -| 15 | Setup wizard + admin tests | 13 | 1.5h | | | 16 | Variant refinement with live data | — | 2-3h | | | 17 | Wire real product data to shop pages | — | 2-3h | | | 18 | Shipping costs at checkout | 17 | 2-3h | | diff --git a/lib/simpleshop_theme_web/live/admin/providers/index.html.heex b/lib/simpleshop_theme_web/live/admin/providers/index.html.heex index 441a2be..69f16c2 100644 --- a/lib/simpleshop_theme_web/live/admin/providers/index.html.heex +++ b/lib/simpleshop_theme_web/live/admin/providers/index.html.heex @@ -8,7 +8,7 @@
diff --git a/lib/simpleshop_theme_web/theme_hook.ex b/lib/simpleshop_theme_web/theme_hook.ex index 8eac592..5f0e8f0 100644 --- a/lib/simpleshop_theme_web/theme_hook.ex +++ b/lib/simpleshop_theme_web/theme_hook.ex @@ -51,8 +51,8 @@ defmodule SimpleshopThemeWeb.ThemeHook do {:cont, socket} not SimpleshopTheme.Accounts.has_admin?() -> - # Fresh install — no admin yet, show the demo shop - {:cont, socket} + # Fresh install — send to registration + {:halt, Phoenix.LiveView.redirect(socket, to: "/users/register")} true -> {:halt, Phoenix.LiveView.redirect(socket, to: "/coming-soon")} diff --git a/lib/simpleshop_theme_web/user_auth.ex b/lib/simpleshop_theme_web/user_auth.ex index e81e0c4..dfc24b9 100644 --- a/lib/simpleshop_theme_web/user_auth.ex +++ b/lib/simpleshop_theme_web/user_auth.ex @@ -257,9 +257,9 @@ defmodule SimpleshopThemeWeb.UserAuth do end @doc "Returns the path to redirect to after log in." - # the user was already logged in, redirect to admin settings + # the user was already logged in, redirect to admin dashboard def signed_in_path(%Plug.Conn{assigns: %{current_scope: %Scope{user: %Accounts.User{}}}}) do - ~p"/admin/settings" + ~p"/admin" end def signed_in_path(_), do: ~p"/" diff --git a/test/simpleshop_theme_web/controllers/page_controller_test.exs b/test/simpleshop_theme_web/controllers/page_controller_test.exs index c792246..fbd16fb 100644 --- a/test/simpleshop_theme_web/controllers/page_controller_test.exs +++ b/test/simpleshop_theme_web/controllers/page_controller_test.exs @@ -1,5 +1,13 @@ defmodule SimpleshopThemeWeb.PageControllerTest do - use SimpleshopThemeWeb.ConnCase + use SimpleshopThemeWeb.ConnCase, async: false + + import SimpleshopTheme.AccountsFixtures + + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end test "GET / renders the shop home page", %{conn: conn} do conn = get(conn, ~p"/") diff --git a/test/simpleshop_theme_web/live/admin/providers_test.exs b/test/simpleshop_theme_web/live/admin/providers_test.exs new file mode 100644 index 0000000..4dcb752 --- /dev/null +++ b/test/simpleshop_theme_web/live/admin/providers_test.exs @@ -0,0 +1,256 @@ +defmodule SimpleshopThemeWeb.Admin.ProvidersTest do + use SimpleshopThemeWeb.ConnCase, async: false + + import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures + import SimpleshopTheme.ProductsFixtures + import Mox + + alias SimpleshopTheme.Providers.MockProvider + + setup :verify_on_exit! + + setup do + user = user_fixture() + %{user: user} + end + + # -- Index page -- + + describe "index - unauthenticated" do + test "redirects to login", %{conn: conn} do + {:error, redirect} = live(conn, ~p"/admin/providers") + assert {:redirect, %{to: path}} = redirect + assert path == ~p"/users/log-in" + end + end + + describe "index - empty state" do + setup %{conn: conn, user: user} do + %{conn: log_in_user(conn, user)} + end + + test "shows empty state when no connections exist", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/admin/providers") + + assert html =~ "Connect your Printify account" + end + + test "shows connect button", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/admin/providers") + + assert has_element?(view, ~s(a[href="/admin/providers/new"])) + end + end + + describe "index - with connection" do + setup %{conn: conn, user: user} do + connection = + provider_connection_fixture(%{ + provider_type: "printify", + name: "My Printify Shop" + }) + + %{conn: log_in_user(conn, user), connection: connection} + end + + test "lists provider connections", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/admin/providers") + + assert html =~ "Printify" + assert html =~ "My Printify Shop" + end + + test "shows edit link", %{conn: conn, connection: connection} do + {:ok, view, _html} = live(conn, ~p"/admin/providers") + + assert has_element?(view, ~s(a[href="/admin/providers/#{connection.id}/edit"])) + end + + test "shows product count", %{conn: conn, connection: connection} do + product_fixture(%{provider_connection: connection}) + + {:ok, _view, html} = live(conn, ~p"/admin/providers") + + assert html =~ "1 product" + end + + test "shows never synced warning", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/admin/providers") + + assert html =~ "Never synced" + end + end + + describe "index - delete" do + setup %{conn: conn, user: user} do + connection = + provider_connection_fixture(%{ + provider_type: "printify", + name: "Delete Me Shop" + }) + + %{conn: log_in_user(conn, user), connection: connection} + end + + test "deletes connection and shows flash", %{conn: conn, connection: connection} do + {:ok, view, _html} = live(conn, ~p"/admin/providers") + + html = render_click(view, "delete", %{"id" => to_string(connection.id)}) + + assert html =~ "Provider connection deleted" + refute html =~ "Delete Me Shop" + end + end + + describe "index - sync" do + setup %{conn: conn, user: user} do + Application.put_env(:simpleshop_theme, :provider_modules, %{ + "printify" => MockProvider + }) + + on_exit(fn -> Application.delete_env(:simpleshop_theme, :provider_modules) end) + + connection = + provider_connection_fixture(%{ + provider_type: "printify", + name: "Sync Test Shop" + }) + + %{conn: log_in_user(conn, user), connection: connection} + end + + test "starts sync and shows flash", %{conn: conn, connection: connection} do + stub(MockProvider, :fetch_products, fn _conn -> {:ok, []} end) + + {:ok, view, _html} = live(conn, ~p"/admin/providers") + + html = render_click(view, "sync", %{"id" => to_string(connection.id)}) + + assert html =~ "Sync started" + end + end + + # -- Form page -- + + describe "form - new" do + setup %{conn: conn, user: user} do + %{conn: log_in_user(conn, user)} + end + + test "renders new form", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/admin/providers/new") + + assert html =~ "Connect to Printify" + assert html =~ "connection key" + assert html =~ "Log in to Printify" + end + + test "test connection shows error when no api key", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/admin/providers/new") + + html = render_click(view, "test_connection") + + assert html =~ "Please enter your connection key" + end + + test "saves new connection", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/admin/providers/new") + + {:ok, _view, html} = + view + |> form("#provider-form", %{ + "provider_connection" => %{ + "api_key" => "test_key_123" + } + }) + |> render_submit() + |> follow_redirect(conn, ~p"/admin/settings") + + assert html =~ "Connected to Printify" + end + end + + describe "form - test connection" do + setup %{conn: conn, user: user} do + Application.put_env(:simpleshop_theme, :provider_modules, %{ + "printify" => MockProvider + }) + + on_exit(fn -> Application.delete_env(:simpleshop_theme, :provider_modules) end) + + %{conn: log_in_user(conn, user)} + end + + test "shows success when connection is valid", %{conn: conn} do + expect(MockProvider, :test_connection, fn _conn -> + {:ok, %{shop_name: "My Printify Shop", shop_id: 12345}} + end) + + {:ok, view, _html} = live(conn, ~p"/admin/providers/new") + + # Validate first to set pending_api_key + view + |> form("#provider-form", %{ + "provider_connection" => %{"api_key" => "valid_key_123"} + }) + |> render_change() + + html = render_click(view, "test_connection") + + assert html =~ "Connected to My Printify Shop" + end + + test "shows error when connection fails", %{conn: conn} do + expect(MockProvider, :test_connection, fn _conn -> + {:error, :unauthorized} + end) + + {:ok, view, _html} = live(conn, ~p"/admin/providers/new") + + view + |> form("#provider-form", %{ + "provider_connection" => %{"api_key" => "bad_key"} + }) + |> render_change() + + html = render_click(view, "test_connection") + + assert html =~ "doesn't seem to be valid" + end + end + + describe "form - edit" do + setup %{conn: conn, user: user} do + connection = + provider_connection_fixture(%{ + provider_type: "printify", + name: "Edit Me Shop" + }) + + %{conn: log_in_user(conn, user), connection: connection} + end + + test "renders edit form", %{conn: conn, connection: connection} do + {:ok, _view, html} = live(conn, ~p"/admin/providers/#{connection.id}/edit") + + assert html =~ "Printify settings" + assert html =~ "Connection enabled" + assert html =~ "Save changes" + end + + test "saves changes", %{conn: conn, connection: connection} do + {:ok, view, _html} = live(conn, ~p"/admin/providers/#{connection.id}/edit") + + {:ok, _view, html} = + view + |> form("#provider-form", %{ + "provider_connection" => %{"enabled" => "true"} + }) + |> render_submit() + |> follow_redirect(conn, ~p"/admin/settings") + + assert html =~ "Settings saved" + end + end +end diff --git a/test/simpleshop_theme_web/live/shop/cart_test.exs b/test/simpleshop_theme_web/live/shop/cart_test.exs index 6083b10..8961df6 100644 --- a/test/simpleshop_theme_web/live/shop/cart_test.exs +++ b/test/simpleshop_theme_web/live/shop/cart_test.exs @@ -2,9 +2,16 @@ defmodule SimpleshopThemeWeb.Shop.CartTest do use SimpleshopThemeWeb.ConnCase, async: false import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures alias SimpleshopTheme.ProductsFixtures + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end + defp create_cart_with_product(_context) do product = ProductsFixtures.complete_product_fixture(%{title: "Test Art Print"}) variant = List.first(product.variants) diff --git a/test/simpleshop_theme_web/live/shop/collection_test.exs b/test/simpleshop_theme_web/live/shop/collection_test.exs index ba0ed91..1020012 100644 --- a/test/simpleshop_theme_web/live/shop/collection_test.exs +++ b/test/simpleshop_theme_web/live/shop/collection_test.exs @@ -2,9 +2,16 @@ defmodule SimpleshopThemeWeb.Shop.CollectionTest do use SimpleshopThemeWeb.ConnCase, async: false import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures alias SimpleshopTheme.Theme.PreviewData + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end + describe "Collection page" do test "renders collection page for /collections/all", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/collections/all") diff --git a/test/simpleshop_theme_web/live/shop/coming_soon_test.exs b/test/simpleshop_theme_web/live/shop/coming_soon_test.exs index 1191476..eb1dc34 100644 --- a/test/simpleshop_theme_web/live/shop/coming_soon_test.exs +++ b/test/simpleshop_theme_web/live/shop/coming_soon_test.exs @@ -51,11 +51,9 @@ defmodule SimpleshopThemeWeb.Shop.ComingSoonTest do 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" + test "redirects to registration on fresh install (no admin)", %{conn: conn} do + # No admin created — redirect to registration + assert {:error, {:redirect, %{to: "/users/register"}}} = live(conn, ~p"/") end test "gates all public shop routes", %{conn: conn} do diff --git a/test/simpleshop_theme_web/live/shop/content_test.exs b/test/simpleshop_theme_web/live/shop/content_test.exs index 6410b2c..f863045 100644 --- a/test/simpleshop_theme_web/live/shop/content_test.exs +++ b/test/simpleshop_theme_web/live/shop/content_test.exs @@ -2,6 +2,13 @@ defmodule SimpleshopThemeWeb.Shop.ContentTest do use SimpleshopThemeWeb.ConnCase, async: false import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures + + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end describe "About page" do test "renders about page", %{conn: conn} do diff --git a/test/simpleshop_theme_web/live/shop/home_test.exs b/test/simpleshop_theme_web/live/shop/home_test.exs index 3ecc259..4804f48 100644 --- a/test/simpleshop_theme_web/live/shop/home_test.exs +++ b/test/simpleshop_theme_web/live/shop/home_test.exs @@ -2,9 +2,16 @@ defmodule SimpleshopThemeWeb.Shop.HomeTest do use SimpleshopThemeWeb.ConnCase, async: false import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures alias SimpleshopTheme.Theme.PreviewData + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end + describe "Home page" do test "renders the home page", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/") diff --git a/test/simpleshop_theme_web/live/shop/product_show_test.exs b/test/simpleshop_theme_web/live/shop/product_show_test.exs index 689b751..1055246 100644 --- a/test/simpleshop_theme_web/live/shop/product_show_test.exs +++ b/test/simpleshop_theme_web/live/shop/product_show_test.exs @@ -2,9 +2,16 @@ defmodule SimpleshopThemeWeb.Shop.ProductShowTest do use SimpleshopThemeWeb.ConnCase, async: false import Phoenix.LiveViewTest + import SimpleshopTheme.AccountsFixtures alias SimpleshopTheme.Theme.PreviewData + setup do + user_fixture() + {:ok, _} = SimpleshopTheme.Settings.set_site_live(true) + :ok + end + describe "Product detail page" do test "renders product page with product name", %{conn: conn} do product = List.first(PreviewData.products()) diff --git a/test/simpleshop_theme_web/user_auth_test.exs b/test/simpleshop_theme_web/user_auth_test.exs index 08586a4..cadde1a 100644 --- a/test/simpleshop_theme_web/user_auth_test.exs +++ b/test/simpleshop_theme_web/user_auth_test.exs @@ -74,13 +74,13 @@ defmodule SimpleshopThemeWeb.UserAuthTest do assert max_age == @remember_me_cookie_max_age end - test "redirects to settings when user is already logged in", %{conn: conn, user: user} do + test "redirects to dashboard when user is already logged in", %{conn: conn, user: user} do conn = conn |> assign(:current_scope, Scope.for_user(user)) |> UserAuth.log_in_user(user) - assert redirected_to(conn) == ~p"/admin/settings" + assert redirected_to(conn) == ~p"/admin" end test "writes a cookie if remember_me was set in previous session", %{conn: conn, user: user} do