Files
cms-client/tests/e2e/onboarding.spec.ts
Noa Virellia c5784f80dc test(onboarding): add E2E tests for UUID-username onboarding dialog
Also fixes the `completeProfile` form action: SvelteKit only supports form
actions in `+page.server.ts`, not `+layout.server.ts`. Moved the action to a
dedicated `/(app)/onboarding/+page.server.ts` and updated the dialog form's
`action` attribute to the absolute path `/app/onboarding?/completeProfile`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:44:06 +08:00

91 lines
3.2 KiB
TypeScript

import { test, expect } from './helpers/fixtures';
import { mock } from './helpers/mock';
const UUID_USERNAME = '550e8400-e29b-41d4-a716-446655440000';
const emptyEventList = {
status: 200,
body: { status: 200, data: { items: [] } }
};
test('dialog appears for UUID username and is not dismissable', async ({ page, loggedInUser }) => {
void loggedInUser;
// Override user/info AFTER fixture runs (last writer wins)
await mock.override('GET', '/user/info', {
status: 200,
body: { status: 200, data: { ...loggedInUser, username: UUID_USERNAME } }
});
await mock.override('GET', '/event/list', emptyEventList);
await page.goto('/app/');
await page.waitForLoadState('networkidle');
// Dialog is visible
await expect(page.getByRole('dialog')).toBeVisible();
await expect(page.getByText('完善个人资料')).toBeVisible();
// Pressing Escape does not close it (non-dismissable)
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).toBeVisible();
});
test('successful submit closes the dialog', async ({ page, loggedInUser }) => {
void loggedInUser;
await mock.override('GET', '/user/info', {
status: 200,
body: { status: 200, data: { ...loggedInUser, username: UUID_USERNAME } }
});
await mock.override('GET', '/event/list', emptyEventList);
await page.goto('/app/');
await page.waitForLoadState('networkidle');
await expect(page.getByRole('dialog')).toBeVisible();
// Register success responses before submitting
await mock.override('PATCH', '/user/update', { status: 200, body: { status: 200 } });
await mock.override('GET', '/user/info', {
status: 200,
body: { status: 200, data: { ...loggedInUser, username: 'alice_real', nickname: 'Alice Real' } }
});
const dialog = page.getByRole('dialog');
await dialog.locator('input[name="username"]').fill('alice_real');
await dialog.locator('input[name="nickname"]').fill('Alice Real');
await page.getByRole('button', { name: /保存并继续/i }).click();
await expect(page.getByRole('dialog')).not.toBeVisible();
// Verify PATCH body
const patches = await mock.requests({ method: 'PATCH', path: '/user/update' });
expect(patches).toHaveLength(1);
expect(patches[0].body).toMatchObject({ username: 'alice_real', nickname: 'Alice Real' });
});
test('backend error shows alert and keeps dialog open', async ({ page, loggedInUser }) => {
void loggedInUser;
await mock.override('GET', '/user/info', {
status: 200,
body: { status: 200, data: { ...loggedInUser, username: UUID_USERNAME } }
});
await mock.override('GET', '/event/list', emptyEventList);
await page.goto('/app/');
await page.waitForLoadState('networkidle');
await expect(page.getByRole('dialog')).toBeVisible();
await mock.override('PATCH', '/user/update', {
status: 409,
body: { status: 409, msg: '用户名已被使用' }
});
const dialog = page.getByRole('dialog');
await dialog.locator('input[name="username"]').fill('taken_name');
await dialog.locator('input[name="nickname"]').fill('Alice');
await page.getByRole('button', { name: /保存并继续/i }).click();
// Error alert is shown
await expect(page.getByText('用户名已被使用')).toBeVisible();
// Dialog remains open
await expect(page.getByRole('dialog')).toBeVisible();
});