pediatric-ai-scribe-v3/e2e/tests/auth-screen.spec.js
Daniel 6b039c4d87 test: bring the e2e specs up to date with the app they test
Three specs were asserting screens that no longer exist. They passed for
as long as they did only because the e2e stack shared production's
database and its configuration; against a clean one they failed honestly.

Signing in is a stepped flow now — email, then a choice between a
password and an emailed code — so #login-password is in the DOM but
hidden until that choice is made. The spec asserted it visible on the
landing screen. Replaced with one test for the landing step and a new
one that walks the transition, which nothing covered before.

The register link is hidden only when registration is disabled. This
install has it enabled and invite-gated, so the link shows and the
invite field is required; the spec asserted display:none.

Connecting Nextcloud by signing in to Nextcloud is now the offered path,
with the username and app-password fields folded behind "Use an app
password instead". The spec asserted all three visible at once; it now
checks the primary path and then opens the fallback.

learning-tab.spec.js is deleted and `learning` is out of the smoke tab
list — that feature was removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 00:49:37 +02:00

99 lines
5 KiB
JavaScript

// ============================================================
// AUTH SCREEN — unauthenticated landing page structure.
// These tests do NOT use the `authedPage` fixture; they visit the
// app with a fresh context (no cookie) and assert the sign-in
// form + register + forgot-password transitions render correctly.
// ============================================================
const { test, expect, E2E_BASE } = require('../fixtures');
test.describe('Unauthenticated auth screen', () => {
// Use the base test that doesn't auto-login.
//
// Signing in is a stepped flow, not one form: email first, then a choice
// between a password and an emailed code. The password field exists in the
// DOM from the start but stays hidden until that choice is made, so asserting
// it visible on the landing screen tests a page that no longer exists.
test('landing asks for the email only, and hides the rest of the flow', async ({ page }) => {
await page.goto(E2E_BASE + '/');
await expect(page.locator('#auth-screen')).toBeVisible({ timeout: 10000 });
await expect(page.locator('#login-email')).toBeVisible();
await expect(page.locator('#btn-login-continue')).toBeVisible();
// Later steps are present but not yet offered.
await expect(page.locator('#login-password')).toBeHidden();
await expect(page.locator('#login-code')).toBeHidden();
await expect(page.locator('#btn-local-login')).toBeHidden();
// main app body must be hidden while unauthenticated
await expect(page.locator('#main-app')).toBeHidden();
});
test('an email leads to the choice, and choosing a password reveals it', async ({ page }) => {
await page.goto(E2E_BASE + '/');
await page.waitForSelector('#auth-screen', { timeout: 10000 });
await page.fill('#login-email', 'someone@ped-ai.test');
await page.click('#btn-login-continue');
await expect(page.locator('#login-choice')).toBeVisible();
// The address is fixed once the flow has moved past it; "use a different
// email" is how you go back, and it only appears after the first step.
await expect(page.locator('#login-email')).toHaveJSProperty('readOnly', true);
await expect(page.locator('#login-change-email')).toBeVisible();
await page.click('#btn-login-use-password');
await expect(page.locator('#login-password')).toBeVisible();
await expect(page.locator('#btn-local-login')).toBeVisible();
});
test('the register link follows the registration setting', async ({ page }) => {
// Hidden by default and shown only when registration is enabled. The seed
// sets registration_enabled true and invite_only true, which is what
// production runs: registration is open, but a code is required — so the
// link shows and the invite field is required.
await page.goto(E2E_BASE + '/');
await page.waitForSelector('#auth-screen', { timeout: 10000 });
await expect(page.locator('#show-register')).toBeVisible();
await expect(page.locator('#register-form')).toHaveCount(1);
await page.click('#show-register');
await expect(page.locator('#reg-invite')).toBeVisible();
await expect(page.locator('#reg-invite')).toHaveJSProperty('required', true);
});
test('register form DOM is wired correctly if manually unhidden', async ({ page }) => {
await page.goto(E2E_BASE + '/');
await page.waitForSelector('#auth-screen', { timeout: 10000 });
// Force the link visible so we can exercise the swap path — useful for
// future tests that want to validate the full register flow.
await page.locator('#show-register').evaluate(el => { el.style.display = ''; });
await page.click('#show-register');
await expect(page.locator('#register-form')).toBeVisible();
await expect(page.locator('#reg-name')).toBeVisible();
await expect(page.locator('#reg-email')).toBeVisible();
await expect(page.locator('#reg-password')).toBeVisible();
await page.click('#show-login');
await expect(page.locator('#login-form')).toBeVisible();
await expect(page.locator('#register-form')).toBeHidden();
});
test('clicking "Forgot password?" swaps to forgot form', async ({ page }) => {
await page.goto(E2E_BASE + '/');
await page.waitForSelector('#auth-screen', { timeout: 10000 });
await page.click('#show-forgot');
await expect(page.locator('#forgot-form')).toBeVisible();
await expect(page.locator('#forgot-email')).toBeVisible();
await expect(page.locator('#login-form')).toBeHidden();
// Back link returns to login
await page.click('#show-login-2');
await expect(page.locator('#login-form')).toBeVisible();
await expect(page.locator('#forgot-form')).toBeHidden();
});
test('password minlength enforces 8 chars in register form', async ({ page }) => {
await page.goto(E2E_BASE + '/');
await page.waitForSelector('#auth-screen', { timeout: 10000 });
// Attribute check doesn't require the element to be visible.
const pw = page.locator('#reg-password');
await expect(pw).toHaveAttribute('minlength', '8');
await expect(pw).toHaveAttribute('type', 'password');
});
});