The password setup/change form used hx-target="#password-section" with hx-swap="innerHTML", but that div wraps the form itself. On a validation error the route returns only an alert div, so the swap replaced the entire form — the password inputs disappeared. Most visible during registration's "set password" step. Retarget the form to a dedicated #password-error div outside the form (mirrors the working login form's #login-error pattern), so the form and its inputs survive errors while messages still render inside #password-section. Also fix pre-existing broken e2e tests: they omitted the required current_password fill and used passwords below the zxcvbn strength threshold (score 1 < MIN_PASSWORD_STRENGTH=2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
2.6 KiB
JavaScript
64 lines
2.6 KiB
JavaScript
// @ts-check
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
const fixtures = JSON.parse(process.env.E2E_FIXTURES || '{}');
|
|
|
|
test.describe('Full user journey', () => {
|
|
test('register via magic link, set password, logout, login', async ({ page }) => {
|
|
// Verify fixtures are loaded
|
|
expect(fixtures.register_token).toBeTruthy();
|
|
|
|
// ---- Step 1: Register via magic link ----
|
|
await page.goto(`/register/${fixtures.register_token}`);
|
|
|
|
// Should redirect to /manage/credentials?setup=1
|
|
await page.waitForURL('**/manage/credentials?setup=1', { timeout: 5000 });
|
|
expect(page.url()).toContain('/manage/credentials');
|
|
|
|
// Should show welcome message
|
|
const welcome = page.locator('[role="status"]');
|
|
await expect(welcome).toBeVisible();
|
|
const welcomeText = await welcome.textContent();
|
|
expect(welcomeText).toContain('Welcome');
|
|
|
|
// Page title should contain Porchlight
|
|
await expect(page).toHaveTitle(/Porchlight/);
|
|
|
|
// ---- Step 2: Set password ----
|
|
const passwordInput = page.locator('#password');
|
|
const confirmInput = page.locator('#confirm');
|
|
await expect(passwordInput).toBeVisible();
|
|
await expect(confirmInput).toBeVisible();
|
|
|
|
await passwordInput.fill('purple-tiger-mountain-42');
|
|
await confirmInput.fill('purple-tiger-mountain-42');
|
|
await page.click('#password-section button[type="submit"]');
|
|
|
|
// Wait for success message
|
|
const successMsg = page.locator('#password-section [role="status"]');
|
|
await expect(successMsg).toBeVisible({ timeout: 5000 });
|
|
await expect(successMsg).toContainText('Password updated');
|
|
|
|
// ---- Step 3: Logout ----
|
|
// Clear the session cookie via the browser context (the request fixture
|
|
// has a separate cookie jar and cannot clear the page's session).
|
|
await page.context().clearCookies();
|
|
|
|
// Navigate to credentials — should redirect to login since we're logged out
|
|
await page.goto('/manage/credentials');
|
|
await page.waitForURL('**/login', { timeout: 5000 });
|
|
expect(page.url()).toContain('/login');
|
|
|
|
// ---- Step 4: Login with the password we just set ----
|
|
await page.fill('#username', fixtures.register_username);
|
|
await page.fill('#password', 'purple-tiger-mountain-42');
|
|
await page.click('form[hx-post="/login/password"] button[type="submit"]');
|
|
|
|
// Wait for redirect to credentials page
|
|
await page.waitForURL('**/manage/credentials', { timeout: 5000 });
|
|
expect(page.url()).toContain('/manage/credentials');
|
|
|
|
// Should NOT show setup message (no ?setup=1)
|
|
await expect(page.locator('[role="status"]:has-text("Welcome")')).toHaveCount(0);
|
|
});
|
|
});
|