pediatric-ai-scribe-v3/e2e/tests/hospitalcourse-workflow.spec.js
Daniel 3e3b4866be test(e2e): +44 tests — sick visit, hospital course, auth screen,
session persistence, per-tab model selector

Brings detailed coverage to sections that were previously only smoke-
tested:

- sickvisit-workflow.spec.js: generate → mocked note, refine bar
  round-trip, load popover, New button clears demographics + transcript.
- hospitalcourse-workflow.spec.js: fill H&P → generate → mocked
  narrative renders via /api/generate-hospital-course, refine updates
  text, load popover open/close.
- auth-screen.spec.js: unauthenticated landing visible, login form
  structure, forgot-password swap + return, register link is
  intentionally display:none on this instance (pinned), register form
  DOM still wired correctly if the link is manually unhidden, reg-
  password has minlength=8 + type=password.
- session-persistence.spec.js: clear cookie simulates logout (UI login
  is gated by Turnstile which can't be completed in the e2e container);
  re-login via loginAs restores the last tab + sub-pill via localStorage.
- model-selector.spec.js: tab-model-select dropdowns render with >0
  options across 7 tabs.

Fixture corrections:
- /api/sick-visit/note and /api/well-visit/note were not being mocked
  at all — the wrong /api/generate-sick-visit pattern was intercepting
  nothing, so tests fell through to the real AI backend. Both now have
  correct patterns and response shapes (sick-visit returns `note`,
  well-visit note returns `note`).
- /api/generate-hospital-course response key updated from `narrative`
  to `hospitalCourse` to match what the frontend actually reads.

wellvisit-workflow: the Visit Note test now asserts a concrete
waitForResponse on /api/well-visit/note + text render, instead of the
previous "hit either endpoint" fallback.

Suite: 294 passed / 0 failed in 5m30s.
2026-04-23 18:58:38 +02:00

76 lines
3.4 KiB
JavaScript

// ============================================================
// HOSPITAL COURSE — full workflow: fill inputs → generate → verify
// mocked narrative renders → refine → verify rendered replaces.
// The older soap-hospital-workflow.spec.js only smokes the save bar;
// this one drives the actual AI generate path.
// ============================================================
const { test, expect, E2E_BASE, mockAI } = require('../fixtures');
async function openTab(page) {
await page.goto(E2E_BASE + '/');
await page.waitForSelector('button.tab-btn', { timeout: 15000 });
const vp = page.viewportSize();
if (vp && vp.width <= 768) {
await page.click('#btn-menu-toggle').catch(() => {});
}
await page.click('button.tab-btn[data-tab="hospital"]');
await page.waitForFunction(() => {
const el = document.getElementById('hospital-tab');
return el && el.classList.contains('active') && el.innerHTML.trim().length > 100;
}, { timeout: 15000 });
}
test.describe('Hospital Course — generate + refine', () => {
test('minimum inputs → generate → mocked narrative renders', async ({ authedPage: _, page }) => {
await mockAI(page);
await openTab(page);
await page.fill('#hc-age', '9 years');
await page.selectOption('#hc-gender', 'Male');
await page.fill('#hc-pmh', 'Asthma, mild intermittent');
// H&P is the minimum "some note" the route needs so it doesn't reject
await page.locator('#hc-hp-content').click();
await page.keyboard.type('Admitted for status asthmaticus. Started on continuous albuterol and steroids.');
const [resp] = await Promise.all([
page.waitForResponse('**/api/generate-hospital-course', { timeout: 15000 }),
page.click('#hc-generate-btn'),
]);
expect(resp.status()).toBe(200);
await expect(page.locator('#hc-output')).toBeVisible({ timeout: 10000 });
await expect(page.locator('#hc-course-text')).toContainText('MOCK hospital course narrative');
});
test('refine button on generated course fires /api/refine + updates text', async ({ authedPage: _, page }) => {
await mockAI(page, {
'**/api/refine': { success: true, refined: 'REFINED-HC course — emphasised hospital day 1 events.', model: 'mock-gpt' },
});
await openTab(page);
await page.fill('#hc-age', '5 years');
await page.locator('#hc-hp-content').click();
await page.keyboard.type('Admission for pneumonia, improving on IV cefriaxone.');
await page.click('#hc-generate-btn');
await expect(page.locator('#hc-course-text')).toContainText('MOCK hospital course', { timeout: 10000 });
// Refine with an instruction
const refineInput = page.locator('#hc-refine-input, [id*="hc-refine"]').first();
await refineInput.fill('Tighten the prose.');
const [resp] = await Promise.all([
page.waitForResponse('**/api/refine'),
page.click('#hc-refine-btn'),
]);
expect(resp.status()).toBe(200);
await expect(page.locator('#hc-course-text')).toContainText('REFINED-HC course', { timeout: 10000 });
});
test('load popover: opens and closes from both triggers', async ({ authedPage: _, page }) => {
await openTab(page);
await page.click('#btn-hosp-load');
await expect(page.locator('#hosp-load-popover')).not.toHaveClass(/hidden/, { timeout: 3000 });
await page.locator('#hosp-load-popover .enc-pop-close').click();
await expect(page.locator('#hosp-load-popover')).toHaveClass(/hidden/);
});
});