porchlight/tests/e2e/test_credentials.js
Johan Lundberg c381896de4
test: add comprehensive e2e test suite with shared helpers and DB seeding
Extract shared test runner (helpers.js), add file-based SQLite with
setup_db.py for fixture seeding, and add tests for auth guard, credentials
management, full registration flow, health endpoint, password auth, and
magic link registration errors. 66 checks across 7 test files.
2026-02-16 14:41:14 +01:00

75 lines
3.4 KiB
JavaScript

// tests/e2e/test_credentials.js
// Tests credential management page: structure, password set/change, validation errors.
const { TARGET_URL, run } = require('./helpers');
run(async (page, assert) => {
const fixtures = JSON.parse(process.env.E2E_FIXTURES || '{}');
// ---- Setup: Log in with dedicated credentials user ----
console.log('\n--- Setup: login ---');
await page.goto(`${TARGET_URL}/login`);
await page.fill('#username', fixtures.cred_username);
await page.fill('#password', fixtures.cred_password);
await page.click('form[hx-post="/login/password"] button[type="submit"]');
await page.waitForURL('**/manage/credentials', { timeout: 5000 });
// ---- Page structure ----
console.log('\n--- Credentials page structure ---');
const title = await page.title();
assert(title.includes('Credentials'), `Title contains "Credentials" (got: "${title}")`);
assert(title.includes('Porchlight'), `Title contains "Porchlight" (got: "${title}")`);
const h1 = await page.locator('h1').textContent();
assert(h1 === 'Credentials', `H1 says "Credentials" (got: "${h1}")`);
// Security keys section
const securityKeysH2 = page.locator('h2:has-text("Security keys")');
assert(await securityKeysH2.isVisible(), 'Security keys heading visible');
const registerBtn = page.locator('#webauthn-register-btn');
assert(await registerBtn.isVisible(), 'Add security key button visible');
// Password section
const passwordH2 = page.locator('h2:has-text("Password")');
assert(await passwordH2.isVisible(), 'Password heading visible');
const passwordSection = page.locator('#password-section');
assert(await passwordSection.isVisible(), 'Password section visible');
// ---- Password validation: mismatch ----
console.log('\n--- Password validation: mismatch ---');
await page.fill('#password', 'newpassword1');
await page.fill('#confirm', 'newpassword2');
await page.click('#password-section button[type="submit"]');
await page.waitForSelector('#password-section [role="alert"]', { timeout: 5000 });
const mismatchErr = await page.locator('#password-section [role="alert"]').textContent();
assert(
mismatchErr.includes('do not match'),
`Shows mismatch error (got: "${mismatchErr}")`
);
// ---- Password validation: minlength enforced client-side ----
console.log('\n--- Password validation: minlength attribute ---');
// Reload page to clear HTMX state (the form was replaced by the error div)
await page.goto(`${TARGET_URL}/manage/credentials`);
const pwMinlength = await page.locator('#password').getAttribute('minlength');
assert(pwMinlength === '8', `Password input has minlength="8" (got: "${pwMinlength}")`);
const confirmMinlength = await page.locator('#confirm').getAttribute('minlength');
assert(confirmMinlength === '8', `Confirm input has minlength="8" (got: "${confirmMinlength}")`);
// ---- Password change: success ----
console.log('\n--- Password change: success ---');
await page.goto(`${TARGET_URL}/manage/credentials`);
await page.fill('#password', 'newpassword123');
await page.fill('#confirm', 'newpassword123');
await page.click('#password-section button[type="submit"]');
await page.waitForSelector('#password-section [role="status"]', { timeout: 5000 });
const successMsg = await page.locator('#password-section [role="status"]').textContent();
assert(
successMsg.includes('Password updated'),
`Shows success message (got: "${successMsg}")`
);
});