Files
redefined-designs/frontend/tests/e2e/account-details.spec.ts
T
bermudalamb 4b0c01068f test(e2e): convert the account specs onto page objects (#137)
account-modal and account-details, taking two more copies of "register a customer" and two more of the `accountModal(page)` helper that every account-touching spec had rewritten.

Both files had grown their own vocabulary for the same dialog. account-details reached for `modal.getByLabel('Current password', { exact: true })` and its five siblings inline in each test, so a change to the account form meant editing six places in one file and more in the next. Those are named locators now, and the three multi-step operations — saving a name, changing a password, changing an email — are actions, because each is a disclosure to open and three fields to fill before the button does anything.

AccountModal.open() gains the 20s timeout the header already had, and for the same reason. Arriving at /account means booting the app and resolving the session against the server. The old specs never noticed because they registered through the form first, which loaded the app and confirmed the session before navigating; taking the API-registered `customer` fixture arrives cold, and the 5s default is comfortably beaten on an idle machine and missed on a loaded one.

Verified: 18/18 across three serial repeats, and 12/13 in parallel. The one failure is `changes the email address and marks it unverified again`, which fails identically on the unconverted file and passes whenever the suite is not saturated — the same load-related flakiness as the rest of the family, not something this commit introduced.

Refs #137
2026-08-23 17:46:45 -05:00

96 lines
3.6 KiB
TypeScript

import { test, expect, uniqueEmail } from './fixtures';
const NEW_PASSWORD = 'a-brand-new-password';
test.describe('Managing your own details', () => {
test('saves a new name, and it survives a reload', async ({ page, customer, accountModal }) => {
await accountModal.open();
await accountModal.saveName('Ada', 'Lovelace');
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.firstName).toHaveValue('Ada');
await expect(accountModal.lastName).toHaveValue('Lovelace');
});
test('refuses to save a blank name', async ({ customer, accountModal }) => {
await accountModal.open();
await accountModal.saveName('');
await expect(accountModal.dialog.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,
customer,
accountModal,
authModal,
header
}) => {
await accountModal.open();
await accountModal.changePassword(customer.password, NEW_PASSWORD);
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();
await accountModal.logOut();
await expect(page).toHaveURL(/\/$/, { timeout: 20000 });
await authModal.gotoLogIn();
await authModal.logIn(customer.email, customer.password);
await expect(page.getByText('invalid email or password')).toBeVisible();
await authModal.logIn(customer.email, NEW_PASSWORD);
await header.waitForSignedIn();
});
test('refuses a password change when the current password is wrong', async ({
customer,
accountModal
}) => {
await accountModal.open();
await accountModal.changePassword('not-the-password', 'another-password');
await expect(accountModal.dialog.getByText('current password is incorrect')).toBeVisible();
});
test('changes the email address and marks it unverified again', async ({
page,
customer,
accountModal
}) => {
const nextEmail = uniqueEmail();
await accountModal.open();
await accountModal.changeEmail(nextEmail, customer.password);
await expect(page.getByText('Check the new address for a verification link.')).toBeVisible();
await expect(accountModal.dialog).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(accountModal.notVerifiedNotice).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 ({
customer,
accountModal
}) => {
await accountModal.open();
await accountModal.changeEmail(uniqueEmail(), 'not-the-password');
await expect(accountModal.dialog.getByText('current password is incorrect')).toBeVisible();
await expect(accountModal.dialog).toContainText(customer.email);
});
});