import { test, expect, Page } from './fixtures'; const PASSWORD = 'supersecret123'; function uniqueEmail(): string { return `playwright-${Date.now()}-${Math.floor(Math.random() * 10000)}@example.com`; } // Registering closes the auth modal and returns the customer to the page behind // it — the storefront, for a direct visit to /register — rather than navigating // to /account. So the header, not the URL, is what proves the session exists. // // The wait is generous because this is a bcrypt round-trip rather than a render, // and the suite's workers all register at once. async function register(page: Page, email: string) { await page.goto('/register'); await page.getByRole('textbox', { name: 'Email' }).fill(email); await page.getByRole('textbox', { name: 'First name' }).fill('Test'); await page.getByRole('textbox', { name: 'Last name' }).fill('Customer'); await page.getByLabel('Password').fill(PASSWORD); await page.getByRole('button', { name: 'Create account' }).click(); await expect(page.getByRole('button', { name: 'My Account' })).toBeVisible({ timeout: 20000 }); } // Log out lives inside the account view, which is a modal over the storefront. async function openAccount(page: Page) { await page.goto('/account'); await expect(page.getByRole('dialog', { name: 'My Account' })).toBeVisible(); } test.describe('Customer accounts', () => { // Both names are required from anyone new so every email has a first name to // greet with (#106). The server refuses without them; this is the form // refusing first, so nobody gets a round trip to find out. test('will not submit a registration without both names', async ({ page }) => { await page.goto('/register'); await page.getByRole('textbox', { name: 'Email' }).fill(uniqueEmail()); await page.getByLabel('Password').fill(PASSWORD); await page.getByRole('button', { name: 'Create account' }).click(); await expect(page.getByText('First name is required')).toBeVisible(); await expect(page.getByText('Last name is required')).toBeVisible(); // Still on the form rather than signed in. await expect(page.getByRole('button', { name: 'My Account' })).toHaveCount(0); }); test('marketing consent checkbox is unchecked by default', async ({ page }) => { await page.goto('/register'); await expect(page.getByRole('checkbox')).not.toBeChecked(); }); test('the consent label is the exact wording the server records', async ({ page }) => { await page.goto('/register'); // The stored consent text is kept verbatim so the record says what the // customer actually saw. Three different wordings were in circulation // before the sign-in form was shared between the routes and the cart // prompt, and none of them matched what was stored. const consent = 'I want to receive occasional emails about new one-of-a-kind items from Redefined Designs. I can unsubscribe at any time.'; await expect(page.getByRole('dialog', { name: 'Create an account' })).toContainText(consent); }); test('registering signs the customer in and returns them where they were', async ({ page }) => { const email = uniqueEmail(); await register(page, email); // Back on the storefront, signed in — not moved to the account page. await expect(page).toHaveURL(/\/$/); await openAccount(page); await expect(page.getByText(email)).toBeVisible(); }); test('rejects login with the wrong password', async ({ page }) => { const email = uniqueEmail(); await register(page, email); await openAccount(page); await page.getByRole('button', { name: 'Log out' }).click(); await page.goto('/login'); await page.getByRole('textbox', { name: 'Email' }).fill(email); await page.getByLabel('Password').fill('wrong-password'); // Scoped to the modal: the storefront rendered behind it has a "Log in" // button of its own, which is what opened this one. await page.getByRole('dialog', { name: 'Log in' }).getByRole('button', { name: 'Log in' }).click(); await expect(page.getByText('invalid email or password')).toBeVisible(); }); test('logging out returns to the home page and resets the header', async ({ page }) => { await register(page, uniqueEmail()); await openAccount(page); await page.getByRole('button', { name: 'Log out' }).click(); // A server round-trip followed by re-rendering the storefront behind the // modal, so the 5s default is too tight when workers run concurrently. await expect(page).toHaveURL(/\/$/, { timeout: 20000 }); await expect(page.getByRole('button', { name: 'Log in' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Sign up' })).toBeVisible(); await expect(page.getByRole('button', { name: 'My Account' })).toBeHidden(); }); test('the logged-out header survives a reload', async ({ page }) => { await register(page, uniqueEmail()); await openAccount(page); await page.getByRole('button', { name: 'Log out' }).click(); await expect(page.getByRole('button', { name: 'Log in' })).toBeVisible(); // Proves the server session was actually destroyed, rather than the header // merely being repainted from stale client state. await page.reload(); await expect(page.getByRole('button', { name: 'Log in' })).toBeVisible(); await expect(page.getByRole('button', { name: 'My Account' })).toBeHidden(); }); test('logging out does not leave the account page on the back stack', async ({ page }) => { await register(page, uniqueEmail()); await openAccount(page); await page.getByRole('button', { name: 'Log out' }).click(); await expect(page).toHaveURL(/\/$/, { timeout: 20000 }); await page.goBack(); await expect(page).not.toHaveURL(/\/account/); }); test('a failed logout says so instead of appearing to succeed', async ({ page }) => { await register(page, uniqueEmail()); await openAccount(page); await page.route('**/api/customers/logout', (route) => route.fulfill({ status: 500, contentType: 'application/json', body: '{"error":"internal error"}' }) ); await page.getByRole('button', { name: 'Log out' }).click(); // The session cookie is still valid, so pretending to be logged out would // silently log the customer back in on their next reload. await expect(page.getByText(/couldn't log out/i)).toBeVisible(); await expect(page).toHaveURL(/\/account/); }); }); test.describe('Auth routes are not dead ends', () => { test('opening Log in from the header closes back to where browsing left off', async ({ page }) => { await page.goto('/?max_price=50000'); await page.getByRole('button', { name: 'Log in' }).click(); const modal = page.getByRole('dialog', { name: 'Log in' }); await expect(modal).toBeVisible(); await expect(page).toHaveURL(/\/login/); await modal.getByRole('button', { name: 'Close' }).click(); await expect(modal).toBeHidden(); await expect(page).toHaveURL(/max_price=50000/); }); test('a direct visit opens over the storefront rather than a blank page', async ({ page }) => { await page.goto('/login'); await expect(page.getByRole('dialog', { name: 'Log in' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeVisible(); }); test('switching between sign in and sign up keeps one history entry', async ({ page }) => { await page.goto('/?max_price=50000'); await page.getByRole('button', { name: 'Log in' }).click(); await page.getByRole('tab', { name: 'Create Account' }).click(); await expect(page).toHaveURL(/\/register/); await page.getByRole('tab', { name: 'Log In' }).click(); await expect(page).toHaveURL(/\/login/); // Back returns to browsing rather than walking through each tab visited. await page.goBack(); await expect(page).toHaveURL(/max_price=50000/); await expect(page.getByRole('dialog', { name: 'Log in' })).toBeHidden(); }); test('signing in from the header returns to the page behind, signed in', async ({ page }) => { const email = uniqueEmail(); await register(page, email); await page.goto('/account'); await page.getByRole('button', { name: 'Log out' }).click(); await expect(page.getByRole('button', { name: 'Log in' })).toBeVisible(); await page.goto('/?max_price=50000'); await page.getByRole('button', { name: 'Log in' }).click(); const modal = page.getByRole('dialog', { name: 'Log in' }); await modal.getByRole('textbox', { name: 'Email' }).fill(email); await modal.getByLabel('Password').fill(PASSWORD); await modal.getByRole('button', { name: 'Log in' }).click(); await expect(page.getByRole('button', { name: 'My Account' })).toBeVisible({ timeout: 20000 }); await expect(page).toHaveURL(/max_price=50000/); }); test('reaching password recovery from the login form keeps a way back', async ({ page }) => { await page.goto('/login'); await page.getByRole('button', { name: 'Forgot password?' }).click(); const modal = page.getByRole('dialog', { name: 'Reset your password' }); await expect(modal).toBeVisible(); await expect(page).toHaveURL(/\/forgot-password/); await modal.getByRole('button', { name: 'Sign in' }).click(); await expect(page.getByRole('dialog', { name: 'Log in' })).toBeVisible(); }); });