dev1/tests/e2e/41-profile-change-password-submit.spec.mjs

99 lines
4.3 KiB
JavaScript

// @ts-check
import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Profile — ChangePasswordForm submit', () => {
test.setTimeout(20000);
test('success path closes the form; 429 path shows error and stays open', async ({ page }) => {
const u = loadTestUser();
// First POST => 200; second => 429 (no endpoint guessing needed)
let calls = 0;
await page.route('**/api/**password**', async (route) => {
calls += 1;
if (calls === 1) return route.fulfill({ status: 200, contentType: 'application/json', body: '{}' });
if (calls === 2) return route.fulfill({ status: 429, contentType: 'application/json', body: JSON.stringify({ error: 'Too many attempts' }) });
return route.fallback();
});
// Sign in (your existing flow)
await page.context().clearCookies();
await page.goto('/signin', { waitUntil: 'networkidle' });
await page.getByPlaceholder('Username', { exact: true }).fill(u.username);
await page.getByPlaceholder('Password', { exact: true }).fill(u.password);
await page.getByRole('button', { name: /^Sign In$/ }).click();
await page.waitForURL('**/signin-landing**', { timeout: 15000 });
await page.goto('/profile', { waitUntil: 'networkidle' });
// If already open, close to make deterministic
const cancelIfOpen = page.getByRole('button', { name: /^Cancel password change$/i });
if (await cancelIfOpen.isVisible().catch(() => false)) {
await cancelIfOpen.click();
await expect(cancelIfOpen).toBeHidden({ timeout: 5000 });
}
// Open (use exact text; fresh locator)
await page.getByRole('button', { name: /^Change Password$/i }).click();
// 3 password inputs: current / new / confirm
const pwInputs = page.locator('input[type="password"]');
await expect(pwInputs.first()).toBeVisible({ timeout: 5000 });
// 1) Success submit — form should close
await pwInputs.nth(0).fill('OldPass!1');
await pwInputs.nth(1).fill('NewPass!1');
await pwInputs.nth(2).fill('NewPass!1');
const submit = page.getByRole('button', { name: /^Update Password$/i });
await submit.click();
// Form closes => Update button disappears
await expect(submit).toBeHidden({ timeout: 7000 });
// Reopen deterministically (works whether trigger is a button or link,
// or if the section needs a refresh)
async function reopenChangePassword(page) {
// If somehow still open, close first
const update = page.getByRole('button', { name: /^Update Password$/i }).first();
if (await update.isVisible().catch(() => false)) {
const cancel = page.getByRole('button', { name: /^Cancel password change$/i }).first();
if (await cancel.isVisible().catch(() => false)) {
await cancel.click();
await expect(update).toBeHidden({ timeout: 7000 });
}
}
// Try common triggers
const triggers = [
page.getByRole('button', { name: /^Change Password$/i }).first(),
page.getByRole('link', { name: /^Change Password$/i }).first(),
page.locator('text=Change Password').first(),
];
for (const t of triggers) {
if (await t.isVisible().catch(() => false)) { await t.click(); return; }
}
// Fallback: reload the page section and try once more
await page.goto('/profile', { waitUntil: 'networkidle' });
for (const t of triggers) {
if (await t.isVisible().catch(() => false)) { await t.click(); return; }
}
throw new Error('Could not find Change Password trigger after success submit');
}
await reopenChangePassword(page);
await pwInputs.nth(0).fill('OldPass!1');
await pwInputs.nth(1).fill('NewPass!2');
await pwInputs.nth(2).fill('NewPass!2');
await page.getByRole('button', { name: /^Update Password$/i }).click();
// For 429: either see an error, OR the form remains open (Update/Cancel still visible)
const errText = page.getByText(/too many attempts|please wait|try again/i);
const sawError = await errText.isVisible({ timeout: 3000 }).catch(() => false);
const stillOpen =
(await page.getByRole('button', { name: /^Update Password$/i }).isVisible().catch(() => false)) ||
(await page.getByRole('button', { name: /^Cancel password change$/i }).isVisible().catch(() => false));
expect(sawError || stillOpen).toBeTruthy();
});
});