Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions e2e/wishlist.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { expect, type Page, test } from "@playwright/test";

/**
* Wishlist E2E coverage.
*
* 1) Guests are redirected to account sign-in/register when trying to wishlist.
* 2) Authenticated customers can add and remove wishlist items and see empty state.
*/

const BASE = "/us/en";

test("guest is redirected to account when clicking wishlist on PDP", async ({
page,
}): Promise<void> => {
await page.goto(`${BASE}/products`);

const firstProduct = page.locator('a[href*="/products/"]').first();
await expect(firstProduct).toBeVisible({ timeout: 15_000 });
await firstProduct.click();
await page.waitForURL(/\/products\/[^/]+/);

const productPath = new URL(page.url()).pathname;

await page.getByRole("button", { name: /add to wishlist/i }).click();

await expect(page).toHaveURL(/\/us\/en\/account\?redirect=/);

const redirectParam = new URL(page.url()).searchParams.get("redirect");
expect(redirectParam).toBe(productPath);
});

test("authenticated user can add and remove a wishlist item", async ({
page,
}): Promise<void> => {
const email = `wishlist-e2e-${Date.now()}@example.com`;
const password = "Password123!";

await registerUser(page, email, password);

await page.goto(`${BASE}/products`);
const firstProduct = page.locator('a[href*="/products/"]').first();
await expect(firstProduct).toBeVisible({ timeout: 15_000 });
await firstProduct.click();
await page.waitForURL(/\/products\/[^/]+/);

const addButton = page.getByRole("button", { name: /add to wishlist/i });
await expect(addButton).toBeVisible();
await addButton.click();

await expect(
page.getByRole("button", {
name: /added to wishlist|remove from wishlist/i,
}),
).toBeVisible({ timeout: 10_000 });

await page.goto(`${BASE}/wishlist`);
await expect(
page.getByRole("heading", { name: /my wishlist/i }),
).toBeVisible();

const removeButton = page.getByRole("button", { name: /^remove$/i }).first();
await expect(removeButton).toBeVisible();
await removeButton.click();

await expect(
page.getByRole("heading", { name: /your wishlist is empty/i }),
).toBeVisible({ timeout: 10_000 });
});

async function registerUser(
page: Page,
email: string,
password: string,
): Promise<void> {
await page.goto(`${BASE}/account/register`);

await page.getByLabel(/^first name$/i).fill("Wishlist");
await page.getByLabel(/^last name$/i).fill("Tester");
await page.getByLabel(/^email$/i).fill(email);
await page.locator("#password").fill(password);
await page.locator("#passwordConfirmation").fill(password);

await page.getByRole("checkbox", { name: /i agree to the/i }).check();
await page.getByRole("button", { name: /^create account$/i }).click();

await expect(page).toHaveURL(/\/us\/en\/account$/);
}
26 changes: 25 additions & 1 deletion messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"openSearch": "Suche öffnen",
"closeSearch": "Suche schließen",
"myAccount": "Mein Konto",
"wishlist": "Wunschliste",
"wholesale": "Großhandel"
},
"regionPreferences": {
Expand All @@ -67,6 +68,7 @@
"policies": "Richtlinien",
"poweredBy": "Betrieben von",
"copyright": "© {year} {storeName}. Unterstützt durch Spree Commerce.",
"wishlist": "Wunschliste",
"wholesale": "Handel & Großhandel"
},
"home": {
Expand Down Expand Up @@ -332,7 +334,9 @@
"showPassword": "Passwort anzeigen",
"hidePassword": "Passwort verbergen",
"forgotPassword": "Passwort vergessen?",
"policyConsentRequired": "Sie müssen den Geschäftsrichtlinien zustimmen, um ein Konto zu erstellen"
"policyConsentRequired": "Sie müssen den Geschäftsrichtlinien zustimmen, um ein Konto zu erstellen",
"wishlist": "Wunschliste",
"wishlistDescription": "Verwalten Sie Ihre gemerkten Produkte"
},
"register": {
"createAccount": "Konto erstellen",
Expand Down Expand Up @@ -546,6 +550,26 @@
"finalizingPayment": "Zahlung wird abgeschlossen...",
"or": "oder"
},
"wishlist": {
"myWishlist": "Meine Wunschliste",
"addToWishlist": "Zur Wunschliste hinzufügen",
"addedToWishlist": "Zur Wunschliste hinzugefügt",
"removeFromWishlist": "Von der Wunschliste entfernen",
"emptyTitle": "Ihre Wunschliste ist leer",
"emptyDescription": "Speichern Sie Produkte, die Ihnen gefallen, und finden Sie sie später hier wieder.",
"signInRequired": "Melden Sie sich an, um Ihre Wunschliste anzusehen und zu verwalten.",
"signInToView": "Anmelden, um die Wunschliste zu sehen",
"failedToLoad": "Ihre Wunschliste konnte nicht geladen werden. Bitte versuchen Sie es erneut.",
"failedToAdd": "Dieser Artikel konnte nicht zur Wunschliste hinzugefügt werden. Bitte versuchen Sie es erneut.",
"failedToRemove": "Dieser Artikel konnte nicht von der Wunschliste entfernt werden. Bitte versuchen Sie es erneut.",
"addToCart": "In den Warenkorb",
"addingToCart": "Wird hinzugefügt...",
"remove": "Entfernen",
"quantity": "Menge: {quantity}",
"refresh": "Aktualisieren",
"outlineHeart": "♡",
"filledHeart": "♥"
},
"wholesale": {
"portalTitle": "Großhandelsportal",
"portalName": "Großhandelsportal",
Expand Down
26 changes: 25 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"openSearch": "Open search",
"closeSearch": "Close search",
"myAccount": "My Account",
"wishlist": "Wishlist",
"wholesale": "Wholesale"
},
"regionPreferences": {
Expand All @@ -67,6 +68,7 @@
"policies": "Policies",
"poweredBy": "Powered by",
"copyright": "© {year} {storeName}. Powered by Spree Commerce.",
"wishlist": "Wishlist",
"wholesale": "Trade & Wholesale"
},
"home": {
Expand Down Expand Up @@ -332,7 +334,9 @@
"showPassword": "Show password",
"hidePassword": "Hide password",
"forgotPassword": "Forgot password?",
"policyConsentRequired": "You must agree to the store policies to create an account"
"policyConsentRequired": "You must agree to the store policies to create an account",
"wishlist": "Wishlist",
"wishlistDescription": "Manage your saved products"
},
"register": {
"createAccount": "Create Account",
Expand Down Expand Up @@ -546,6 +550,26 @@
"finalizingPayment": "Finalizing your payment...",
"or": "or"
},
"wishlist": {
"myWishlist": "My Wishlist",
"addToWishlist": "Add to wishlist",
"addedToWishlist": "Added to wishlist",
"removeFromWishlist": "Remove from wishlist",
"emptyTitle": "Your wishlist is empty",
"emptyDescription": "Save products you love and find them here later.",
"signInRequired": "Sign in to view and manage your wishlist.",
"signInToView": "Sign in to view wishlist",
"failedToLoad": "Could not load your wishlist. Please try again.",
"failedToAdd": "Could not add this item to your wishlist. Please try again.",
"failedToRemove": "Could not remove this item from your wishlist. Please try again.",
"addToCart": "Add to cart",
"addingToCart": "Adding...",
"remove": "Remove",
"quantity": "Qty: {quantity}",
"refresh": "Refresh",
"outlineHeart": "♡",
"filledHeart": "♥"
},
"wholesale": {
"portalTitle": "Wholesale Portal",
"portalName": "Wholesale Portal",
Expand Down
26 changes: 25 additions & 1 deletion messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"openSearch": "Abrir busqueda",
"closeSearch": "Cerrar busqueda",
"myAccount": "Mi cuenta",
"wishlist": "Lista de deseos",
"wholesale": "Mayorista"
},
"regionPreferences": {
Expand All @@ -67,6 +68,7 @@
"policies": "Politicas",
"poweredBy": "Impulsado por",
"copyright": "© {year} {storeName}. Impulsado por Spree Commerce.",
"wishlist": "Lista de deseos",
"wholesale": "Comercio y venta al por mayor"
},
"home": {
Expand Down Expand Up @@ -332,7 +334,9 @@
"showPassword": "Mostrar contrasena",
"hidePassword": "Ocultar contrasena",
"forgotPassword": "¿Olvidaste tu contrasena?",
"policyConsentRequired": "Debes aceptar las politicas de la tienda para crear una cuenta"
"policyConsentRequired": "Debes aceptar las politicas de la tienda para crear una cuenta",
"wishlist": "Lista de deseos",
"wishlistDescription": "Gestiona tus productos guardados"
},
"register": {
"createAccount": "Crear cuenta",
Expand Down Expand Up @@ -546,6 +550,26 @@
"finalizingPayment": "Finalizando tu pago...",
"or": "o"
},
"wishlist": {
"myWishlist": "Mi lista de deseos",
"addToWishlist": "Agregar a la lista de deseos",
"addedToWishlist": "Agregado a la lista de deseos",
"removeFromWishlist": "Quitar de la lista de deseos",
"emptyTitle": "Tu lista de deseos está vacía",
"emptyDescription": "Guarda los productos que te encantan y encuéntralos aquí más tarde.",
"signInRequired": "Inicia sesión para ver y administrar tu lista de deseos.",
"signInToView": "Inicia sesión para ver la lista de deseos",
"failedToLoad": "No se pudo cargar tu lista de deseos. Inténtalo de nuevo.",
"failedToAdd": "No se pudo agregar este artículo a tu lista de deseos. Inténtalo de nuevo.",
"failedToRemove": "No se pudo quitar este artículo de tu lista de deseos. Inténtalo de nuevo.",
"addToCart": "Agregar al carrito",
"addingToCart": "Agregando...",
"remove": "Quitar",
"quantity": "Cant.: {quantity}",
"refresh": "Actualizar",
"outlineHeart": "♡",
"filledHeart": "♥"
},
"wholesale": {
"portalTitle": "Portal mayorista",
"portalName": "Portal mayorista",
Expand Down
26 changes: 25 additions & 1 deletion messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"openSearch": "Ouvrir la recherche",
"closeSearch": "Fermer la recherche",
"myAccount": "Mon compte",
"wishlist": "Liste d'envies",
"wholesale": "Vente en gros"
},
"regionPreferences": {
Expand All @@ -67,6 +68,7 @@
"policies": "Politiques",
"poweredBy": "Propulse par",
"copyright": "© {year} {storeName}. Propulse par Spree Commerce.",
"wishlist": "Liste d'envies",
"wholesale": "Professionnels & vente en gros"
},
"home": {
Expand Down Expand Up @@ -332,7 +334,9 @@
"showPassword": "Afficher le mot de passe",
"hidePassword": "Masquer le mot de passe",
"forgotPassword": "Mot de passe oublie ?",
"policyConsentRequired": "Vous devez accepter les politiques du magasin pour creer un compte"
"policyConsentRequired": "Vous devez accepter les politiques du magasin pour creer un compte",
"wishlist": "Liste d'envies",
"wishlistDescription": "Gérez vos produits enregistrés"
},
"register": {
"createAccount": "Creer un compte",
Expand Down Expand Up @@ -546,6 +550,26 @@
"finalizingPayment": "Finalisation du paiement...",
"or": "ou"
},
"wishlist": {
"myWishlist": "Ma liste d'envies",
"addToWishlist": "Ajouter à la liste d'envies",
"addedToWishlist": "Ajouté à la liste d'envies",
"removeFromWishlist": "Retirer de la liste d'envies",
"emptyTitle": "Votre liste d'envies est vide",
"emptyDescription": "Enregistrez les produits que vous aimez et retrouvez-les ici plus tard.",
"signInRequired": "Connectez-vous pour voir et gérer votre liste d'envies.",
"signInToView": "Se connecter pour voir la liste d'envies",
"failedToLoad": "Impossible de charger votre liste d'envies. Veuillez réessayer.",
"failedToAdd": "Impossible d'ajouter cet article à votre liste d'envies. Veuillez réessayer.",
"failedToRemove": "Impossible de retirer cet article de votre liste d'envies. Veuillez réessayer.",
"addToCart": "Ajouter au panier",
"addingToCart": "Ajout en cours...",
"remove": "Retirer",
"quantity": "Qté : {quantity}",
"refresh": "Actualiser",
"outlineHeart": "♡",
"filledHeart": "♥"
},
"wholesale": {
"portalTitle": "Portail grossiste",
"portalName": "Portail grossiste",
Expand Down
26 changes: 25 additions & 1 deletion messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"openSearch": "Otwórz wyszukiwanie",
"closeSearch": "Zamknij wyszukiwanie",
"myAccount": "Moje konto",
"wishlist": "Lista życzeń",
"wholesale": "Hurt"
},
"regionPreferences": {
Expand All @@ -67,6 +68,7 @@
"policies": "Regulaminy",
"poweredBy": "Zasilany przez",
"copyright": "© {year} {storeName}. Zasilany przez Spree Commerce.",
"wishlist": "Lista życzeń",
"wholesale": "Handel i sprzedaż hurtowa"
},
"home": {
Expand Down Expand Up @@ -332,7 +334,9 @@
"showPassword": "Pokaż hasło",
"hidePassword": "Ukryj hasło",
"forgotPassword": "Zapomniałeś hasła?",
"policyConsentRequired": "Musisz zaakceptować regulamin sklepu, aby utworzyć konto"
"policyConsentRequired": "Musisz zaakceptować regulamin sklepu, aby utworzyć konto",
"wishlist": "Lista życzeń",
"wishlistDescription": "Zarządzaj zapisanymi produktami"
},
"register": {
"createAccount": "Utwórz konto",
Expand Down Expand Up @@ -546,6 +550,26 @@
"finalizingPayment": "Finalizowanie płatności...",
"or": "lub"
},
"wishlist": {
"myWishlist": "Moja lista życzeń",
"addToWishlist": "Dodaj do listy życzeń",
"addedToWishlist": "Dodano do listy życzeń",
"removeFromWishlist": "Usuń z listy życzeń",
"emptyTitle": "Twoja lista życzeń jest pusta",
"emptyDescription": "Zapisz produkty, które lubisz, i znajdź je tutaj później.",
"signInRequired": "Zaloguj się, aby przeglądać i zarządzać listą życzeń.",
"signInToView": "Zaloguj się, aby zobaczyć listę życzeń",
"failedToLoad": "Nie udało się załadować listy życzeń. Spróbuj ponownie.",
"failedToAdd": "Nie udało się dodać tego produktu do listy życzeń. Spróbuj ponownie.",
"failedToRemove": "Nie udało się usunąć tego produktu z listy życzeń. Spróbuj ponownie.",
"addToCart": "Dodaj do koszyka",
"addingToCart": "Dodawanie...",
"remove": "Usuń",
"quantity": "Ilość: {quantity}",
"refresh": "Odśwież",
"outlineHeart": "♡",
"filledHeart": "♥"
},
"wholesale": {
"portalTitle": "Portal hurtowy",
"portalName": "Portal hurtowy",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { WishlistPageContent } from "@/components/wishlist/WishlistPageContent";

export default function AccountWishlistPage() {
return <WishlistPageContent />;
}
Comment on lines +3 to +5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the guest wishlist flow.

This route is nested under the protected account layout, which redirects unauthenticated /account/* paths to /account. As a result, WishlistPageContent’s guest sign-in state and its redirect query are never reached; guests clicking /wishlist, the header link, or footer link land on the generic account page and lose their destination. Exempt this route from the layout redirect or preserve the wishlist URL when redirecting to sign-in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`[country]/[locale]/(storefront)/account/wishlist/page.tsx around
lines 3 - 5, Update the account route protection around AccountWishlistPage so
unauthenticated users can reach WishlistPageContent and its guest sign-in state,
preserving the wishlist path and redirect query for sign-in; exempt this
wishlist route from the generic account-layout redirect rather than sending
guests to /account.

19 changes: 19 additions & 0 deletions src/app/[country]/[locale]/(storefront)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CreditCard,
Eye,
EyeOff,
Heart,
MapPin,
ShoppingBag,
User,
Expand Down Expand Up @@ -212,6 +213,24 @@ export default function AccountPage() {
</Card>
</Link>

<Link href={`${basePath}/account/wishlist`}>
<Card className="hover:border-gray-300 transition-colors h-full">
<CardContent className="flex items-center gap-4 py-0">
<div className="p-3 bg-gray-100 rounded-xl">
<Heart className="w-6 h-6 text-primary" />
</div>
<div>
<h2 className="text-lg font-medium text-gray-900">
{t("wishlist")}
</h2>
<p className="mt-1 text-sm text-gray-500">
{t("wishlistDescription")}
</p>
</div>
</CardContent>
</Card>
</Link>

<Link href={`${basePath}/account/addresses`}>
<Card className="hover:border-gray-300 transition-colors h-full">
<CardContent className="flex items-center gap-4 py-0">
Expand Down
Loading
Loading