import { test, expect, Page } from './fixtures'; const PASSWORD = 'supersecret123'; const uniqueEmail = () => `details-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}@example.com`; // Same generous wait as the other account specs: registration is a bcrypt // round-trip, not a render, and runs past Playwright's 5s default when the // suite's workers all register at once. async function registerCustomer(page: Page): Promise { const email = uniqueEmail(); 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 }); return email; } const accountModal = (page: Page) => page.getByRole('dialog', { name: 'My Account' }); async function signIn(page: Page, email: string, password: string) { await page.goto('/login'); await page.getByRole('textbox', { name: 'Email' }).fill(email); await page.getByLabel('Password').fill(password); await page.getByRole('dialog', { name: 'Log in' }).getByRole('button', { name: 'Log in' }).click(); } test.describe('Managing your own details', () => { test('saves a new name, and it survives a reload', async ({ page }) => { await registerCustomer(page); await page.goto('/account'); const modal = accountModal(page); await modal.getByLabel('First name', { exact: true }).fill('Ada'); await modal.getByLabel('Last name', { exact: true }).fill('Lovelace'); await modal.getByRole('button', { name: 'Save name' }).click(); await expect(page.getByText('Name updated')).toBeVisible(); // Reloaded rather than re-read from component state, which would pass even // if nothing had been persisted. await page.reload(); await expect(accountModal(page).getByLabel('First name', { exact: true })).toHaveValue('Ada'); await expect(accountModal(page).getByLabel('Last name', { exact: true })).toHaveValue('Lovelace'); }); test('refuses to save a blank name', async ({ page }) => { await registerCustomer(page); await page.goto('/account'); const modal = accountModal(page); await modal.getByLabel('First name', { exact: true }).fill(''); await modal.getByRole('button', { name: 'Save name' }).click(); await expect(modal.getByText('First name is required')).toBeVisible(); }); // The assertion that matters for a password change: not that the form said // something reassuring, but that the old credential has actually stopped // opening the account. test('changes the password, leaving the old one dead and the new one working', async ({ page }) => { const email = await registerCustomer(page); const newPassword = 'a-brand-new-password'; await page.goto('/account'); const modal = accountModal(page); await modal.getByRole('button', { name: 'Change your password' }).click(); await modal.getByLabel('Current password', { exact: true }).fill(PASSWORD); await modal.getByLabel('New password', { exact: true }).fill(newPassword); await modal.getByLabel('Confirm new password', { exact: true }).fill(newPassword); await modal.getByRole('button', { name: 'Change password' }).click(); await expect(page.getByText('Password changed. Other devices have been signed out.')).toBeVisible(); // Still signed in here: the session making the change is deliberately the // one session spared. Exact, because "Delete my account" further down this // same modal is otherwise also a match. await expect(page.getByRole('button', { name: 'My Account', exact: true })).toBeVisible(); // Logging out from inside the open modal, which is where the control lives. await page.getByRole('button', { name: 'Log out' }).click(); await expect(page).toHaveURL(/\/$/, { timeout: 20000 }); await signIn(page, email, PASSWORD); await expect(page.getByText('invalid email or password')).toBeVisible(); await signIn(page, email, newPassword); await expect(page.getByRole('button', { name: 'My Account', exact: true })) .toBeVisible({ timeout: 20000 }); }); test('refuses a password change when the current password is wrong', async ({ page }) => { await registerCustomer(page); await page.goto('/account'); const modal = accountModal(page); await modal.getByRole('button', { name: 'Change your password' }).click(); await modal.getByLabel('Current password', { exact: true }).fill('not-the-password'); await modal.getByLabel('New password', { exact: true }).fill('another-password'); await modal.getByLabel('Confirm new password', { exact: true }).fill('another-password'); await modal.getByRole('button', { name: 'Change password' }).click(); await expect(modal.getByText('current password is incorrect')).toBeVisible(); }); test('changes the email address and marks it unverified again', async ({ page }) => { await registerCustomer(page); const nextEmail = uniqueEmail(); await page.goto('/account'); const modal = accountModal(page); await modal.getByRole('button', { name: 'Change your email address' }).click(); await modal.getByLabel('New email address', { exact: true }).fill(nextEmail); await modal.getByLabel('Your password', { exact: true }).fill(PASSWORD); await modal.getByRole('button', { name: 'Change email' }).click(); await expect(page.getByText('Check the new address for a verification link.')).toBeVisible(); await expect(modal).toContainText(nextEmail); // A changed address is unverified by definition, and the account view has // to say so or the customer has no way to know a link is waiting. await expect(modal.getByText('Email not verified')).toBeVisible(); }); // A live session is not enough to move the address a password reset goes to, // which is the whole reason the field is there. test('refuses an email change when the password is wrong, and keeps the old address', async ({ page }) => { const email = await registerCustomer(page); await page.goto('/account'); const modal = accountModal(page); await modal.getByRole('button', { name: 'Change your email address' }).click(); await modal.getByLabel('New email address', { exact: true }).fill(uniqueEmail()); await modal.getByLabel('Your password', { exact: true }).fill('not-the-password'); await modal.getByRole('button', { name: 'Change email' }).click(); await expect(modal.getByText('current password is incorrect')).toBeVisible(); await expect(modal).toContainText(email); }); });