porchlight/tests/e2e/test_full_flow.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

84 lines
3.4 KiB
JavaScript

// tests/e2e/test_full_flow.js
// Full user journey: magic link registration -> set password -> logout -> login.
const { TARGET_URL, run } = require('./helpers');
run(async (page, assert) => {
const fixtures = JSON.parse(process.env.E2E_FIXTURES || '{}');
assert(fixtures.register_token, 'Test fixtures loaded (register_token present)');
// ---- Step 1: Register via magic link ----
console.log('\n--- Magic link registration ---');
await page.goto(`${TARGET_URL}/register/${fixtures.register_token}`);
// Should redirect to /manage/credentials?setup=1
await page.waitForURL('**/manage/credentials?setup=1', { timeout: 5000 });
assert(
page.url().includes('/manage/credentials'),
`Redirected to credentials page (url: ${page.url()})`
);
// Should show welcome message
const welcome = page.locator('[role="status"]');
assert(await welcome.isVisible(), 'Welcome/setup message is visible');
const welcomeText = await welcome.textContent();
assert(
welcomeText.includes('Welcome'),
`Welcome message shown (got: "${welcomeText}")`
);
// Page title should be Porchlight
const title = await page.title();
assert(title.includes('Porchlight'), 'Credentials page title contains Porchlight');
// ---- Step 2: Set password ----
console.log('\n--- Set password ---');
const passwordInput = page.locator('#password');
const confirmInput = page.locator('#confirm');
assert(await passwordInput.isVisible(), 'Password input is visible');
assert(await confirmInput.isVisible(), 'Confirm password input is visible');
await passwordInput.fill('mypassword123');
await confirmInput.fill('mypassword123');
await page.click('#password-section button[type="submit"]');
// Wait for HTMX response — password-section innerHTML gets replaced
// The success message uses role="status"
const successMsg = page.locator('#password-section [role="status"]');
await successMsg.waitFor({ timeout: 5000 });
const successText = await successMsg.textContent();
assert(
successText.includes('Password updated'),
`Password set successfully (got: "${successText}")`
);
// ---- Step 3: Logout ----
console.log('\n--- Logout ---');
// POST /logout returns an HX-Redirect header, not a standard redirect.
// Use page.request to call it, then navigate manually.
await page.request.post(`${TARGET_URL}/logout`);
// Navigate to credentials — should redirect to login since we're logged out
await page.goto(`${TARGET_URL}/manage/credentials`);
await page.waitForURL('**/login', { timeout: 5000 });
assert(page.url().includes('/login'), 'Redirected to login after logout');
// ---- Step 4: Login with the password we just set ----
console.log('\n--- Login with new password ---');
await page.fill('#username', fixtures.register_username);
await page.fill('#password', 'mypassword123');
// Submit via HTMX — on success, HX-Redirect header triggers redirect
await page.click('form[hx-post="/login/password"] button[type="submit"]');
// Wait for redirect to credentials page
await page.waitForURL('**/manage/credentials', { timeout: 5000 });
assert(
page.url().includes('/manage/credentials'),
`Login succeeded, redirected to credentials (url: ${page.url()})`
);
// Should NOT show setup message (no ?setup=1)
const setupMsgCount = await page.locator('[role="status"]:has-text("Welcome")').count();
assert(setupMsgCount === 0, 'No welcome/setup message on normal login');
});