import { test, expect, Page } from '@playwright/test'; const PASSWORD = 'supersecret123'; const uniqueEmail = () => `account-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}@example.com`; // Registering lands on /account, which is now the modal over the storefront. // Returns the address so a test can assert the right account is shown. async function registerAndCloseAccount(page: Page): Promise { const email = uniqueEmail(); await page.goto('/register'); await page.getByRole('textbox', { name: 'Email' }).fill(email); await page.getByLabel('Password').fill(PASSWORD); await page.getByRole('button', { name: 'Create account' }).click(); // Registration is a bcrypt round-trip, not a render. Unloaded it takes about // half a second; with the suite's workers all registering at once it can pass // Playwright's 5s default, which shows up as a failure on whichever test lost // the race rather than as the load problem it is. await expect(page).toHaveURL(/\/account/, { timeout: 20000 }); await closeAccount(page); return email; } const accountModal = (page: Page) => page.getByRole('dialog', { name: 'My Account' }); async function closeAccount(page: Page) { await accountModal(page).getByRole('button', { name: 'Close' }).click(); await expect(accountModal(page)).toBeHidden(); } test.describe('My Account opens as a modal', () => { test('opens over the storefront and closes back to it, filters and all', async ({ page }) => { await registerAndCloseAccount(page); // A filtered view, to prove closing restores where the customer actually // was rather than a bare storefront. await page.goto('/?max_price=50000'); await expect(page.getByRole('button', { name: 'My Account' })).toBeVisible(); await page.getByRole('button', { name: 'My Account' }).click(); await expect(accountModal(page)).toBeVisible(); // Still a real, linkable URL rather than hidden view state. await expect(page).toHaveURL(/\/account/); await closeAccount(page); await expect(page).toHaveURL(/max_price=50000/); }); test('the browser back button closes it, the same as the close control', async ({ page }) => { await registerAndCloseAccount(page); await page.goto('/?max_price=50000'); await page.getByRole('button', { name: 'My Account' }).click(); await expect(accountModal(page)).toBeVisible(); await page.goBack(); await expect(accountModal(page)).toBeHidden(); await expect(page).toHaveURL(/max_price=50000/); }); test('a direct visit renders the storefront behind it, so closing lands somewhere real', async ({ page }) => { await registerAndCloseAccount(page); // A bookmark, or the link in a verification email. There is no page behind // in this case, which is what used to make /account a dead end. await page.goto('/account'); await expect(accountModal(page)).toBeVisible(); await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeVisible(); await closeAccount(page); await expect(page).toHaveURL(/\/$/); await expect(page.getByRole('button', { name: 'My Account' })).toBeVisible(); }); test('survives a reload, since it is a route rather than view state', async ({ page }) => { const email = await registerAndCloseAccount(page); await page.goto('/?max_price=50000'); await page.getByRole('button', { name: 'My Account' }).click(); await expect(accountModal(page)).toBeVisible(); await page.reload(); await expect(accountModal(page)).toBeVisible(); await expect(accountModal(page)).toContainText(email); }); test('shows the signed-in account and its settings', async ({ page }) => { const email = await registerAndCloseAccount(page); await page.goto('/account'); const modal = accountModal(page); await expect(modal).toContainText(email); await expect(modal).toContainText('Order History'); // Scoped to the modal: the storefront behind it has a theme switch of its // own, so an unscoped switch locator would be ambiguous. await expect(modal.getByRole('switch')).toHaveCount(2); }); test('deleting the account does not leave the page behind it looking signed in', async ({ page }) => { await registerAndCloseAccount(page); await page.goto('/account'); await accountModal(page).getByRole('button', { name: 'Delete my account' }).click(); await page.getByRole('dialog', { name: 'Delete your account?' }) .getByRole('button', { name: 'Delete my account' }).click(); // The storefront is rendered behind the modal, so a session left in place // would visibly go on offering My Account for an account that is gone. await expect(page.getByRole('button', { name: 'Sign up' })).toBeVisible(); await expect(page.getByRole('button', { name: 'My Account' })).toBeHidden(); await expect(page).toHaveURL(/\/$/); }); test('stays usable on a phone, with the close control in reach', async ({ page }) => { await registerAndCloseAccount(page); await page.setViewportSize({ width: 390, height: 664 }); await page.goto('/account'); const modal = accountModal(page); await expect(modal).toBeVisible(); // The view is taller than the viewport, so the body scrolls rather than // pushing the title and close control off-screen. await expect(modal.getByRole('button', { name: 'Close' })).toBeInViewport(); await expect(modal.getByText('Order History')).toBeVisible(); await closeAccount(page); await expect(page).toHaveURL(/\/$/); }); });