86 lines
3.6 KiB
JavaScript
86 lines
3.6 KiB
JavaScript
// @ts-check
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('@p0 Reset Password — exact UI flow', () => {
|
|
test.setTimeout(15000);
|
|
|
|
test('mismatch passwords → inline error, no request', async ({ page }) => {
|
|
// Route with a token param (the component checks presence only)
|
|
await page.goto('/reset-password/abcd1234', { waitUntil: 'networkidle' });
|
|
|
|
const newPw = page.getByLabel(/^New password$/i).or(page.locator('input[type="password"]').first());
|
|
const confirm = page.getByLabel(/^Confirm password$/i).or(page.locator('input[type="password"]').nth(1));
|
|
const submit = page.getByRole('button', { name: /^Update password$/i });
|
|
|
|
await expect(newPw).toBeVisible();
|
|
await newPw.fill('Str0ng!Passw0rd');
|
|
await confirm.fill('Different!Pass');
|
|
|
|
// Ensure no network call is made when mismatch
|
|
let sawConfirmCall = false;
|
|
page.on('request', (req) => {
|
|
if (req.method() === 'POST' && /\/api\/auth\/password-reset\/confirm$/.test(req.url())) {
|
|
sawConfirmCall = true;
|
|
}
|
|
});
|
|
|
|
await submit.click();
|
|
await expect(page.getByText(/^Passwords do not match\.$/i)).toBeVisible();
|
|
expect(sawConfirmCall).toBeFalsy();
|
|
});
|
|
|
|
test('success path → “Password updated” → Go to Sign In navigates', async ({ page }) => {
|
|
await page.goto('/reset-password/successToken123', { waitUntil: 'networkidle' });
|
|
|
|
// Intercept confirm endpoint with 200 OK
|
|
await page.route('**/api/auth/password-reset/confirm', async (route) => {
|
|
const req = route.request();
|
|
expect(req.method()).toBe('POST');
|
|
const body = JSON.parse(req.postData() || '{}');
|
|
// basic shape check
|
|
expect(body).toHaveProperty('token');
|
|
expect(body).toHaveProperty('password');
|
|
await route.fulfill({ status: 200, contentType: 'application/json', body: '{}' });
|
|
});
|
|
|
|
const newPw = page.getByLabel(/^New password$/i).or(page.locator('input[type="password"]').first());
|
|
const confirm = page.getByLabel(/^Confirm password$/i).or(page.locator('input[type="password"]').nth(1));
|
|
const submit = page.getByRole('button', { name: /^Update password$/i });
|
|
|
|
await newPw.fill('Str0ng!Passw0rd$');
|
|
await confirm.fill('Str0ng!Passw0rd$');
|
|
await submit.click();
|
|
|
|
await expect(page.getByRole('heading', { name: /^Password updated$/i })).toBeVisible({ timeout: 10000 });
|
|
|
|
const goSignin = page.getByRole('button', { name: /^Go to Sign In$/i });
|
|
await expect(goSignin).toBeVisible();
|
|
await goSignin.click();
|
|
await expect(page).toHaveURL(/\/signin(\?|$)/, { timeout: 10000 });
|
|
});
|
|
|
|
test('rate limited (429) → shows friendly error and stays on form', async ({ page }) => {
|
|
await page.goto('/reset-password/ratelimitToken', { waitUntil: 'networkidle' });
|
|
|
|
// Intercept with 429 (Too Many Requests)
|
|
await page.route('**/api/auth/password-reset/confirm', async (route) => {
|
|
await route.fulfill({ status: 429, contentType: 'application/json', body: '{}' });
|
|
});
|
|
|
|
const newPw = page.getByLabel(/^New password$/i).or(page.locator('input[type="password"]').first());
|
|
const confirm = page.getByLabel(/^Confirm password$/i).or(page.locator('input[type="password"]').nth(1));
|
|
const submit = page.getByRole('button', { name: /^Update password$/i });
|
|
|
|
await newPw.fill('Str0ng!Passw0rd$');
|
|
await confirm.fill('Str0ng!Passw0rd$');
|
|
await submit.click();
|
|
|
|
await expect(
|
|
page.getByText(/Too many attempts\. Please wait ~30 seconds and try again\./i)
|
|
).toBeVisible({ timeout: 5000 });
|
|
|
|
// Still on form (not the success screen)
|
|
await expect(page.getByRole('heading', { name: /^Set a new password$/i })).toBeVisible();
|
|
});
|
|
});
|