8 new spec files covering sections previously only smoke-tested:
- ai-endpoints-contract.spec.js — hits 8 real AI endpoints via request
context and fails if the response leaks TypeError / ReferenceError /
'Cannot read properties of undefined' / 'is not defined' / 'is not a
function'. This is the class of bug that shipped the PE-narrative
regression to prod because every page-level mock prevented the real
handler from running.
- encounter-workflow.spec.js — generate HPI, refine, clear transcript.
- encounter-save-load.spec.js — save draft, load popover, repopulate.
- wellvisit-workflow.spec.js — byvisit, milestones, SSHADESS (12+
reveal), visit note.
- vaxschedule-content.spec.js — schedule + catch-up panels populate
beyond "Loading".
- chart-review-workflow.spec.js — generate + load popover.
- learning-tab.spec.js — search filter, category pills, feed.
- settings-faq-dictation.spec.js — voice/password/2FA/Nextcloud
sections, FAQ expand/collapse, dictation generate flow.
Baseline fixes:
- Added CORS_ORIGINS + API_RATE_LIMIT_MAX env overrides so the e2e
container accepts the browser's Origin header and can absorb the
full suite's API traffic without tripping the 200/min guard.
- Server's /api/ rate limit is now configurable via
API_RATE_LIMIT_MAX (default stays 200).
- extensions-crud: replaced native page.on('dialog') listeners with
#confirm-modal-ok clicks (we moved off native confirm()).
- pe-guide-smoke + extensions-crud: mobile viewport opens the hamburger
before clicking sidebar tabs.
- fixtures.js: /api/refine mock uses 'refined' (real API shape), not
'content'. /api/chart-review replaced with /api/generate-chart-review.
Suite: 230 passed / 0 failed in 4m36s.
80 lines
3.4 KiB
JavaScript
80 lines
3.4 KiB
JavaScript
// ============================================================
|
|
// ENCOUNTER TAB — detailed workflow: transcript → generate HPI →
|
|
// refine → shorten. Uses mocked AI so tests don't burn API credits.
|
|
// ============================================================
|
|
|
|
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="encounter"]');
|
|
await page.waitForFunction(() => {
|
|
const el = document.getElementById('encounter-tab');
|
|
return el && el.classList.contains('active') && el.innerHTML.trim().length > 100;
|
|
}, { timeout: 15000 });
|
|
}
|
|
|
|
test.describe('Encounter — HPI generation workflow', () => {
|
|
|
|
test('fill transcript + generate → mocked HPI renders in output pane', async ({ authedPage: _, page }) => {
|
|
await mockAI(page);
|
|
await openTab(page);
|
|
|
|
// Fill age + setting; transcript is a contenteditable div, not a textarea
|
|
await page.fill('#enc-age', '8 years');
|
|
await page.selectOption('#enc-gender', 'Male');
|
|
await page.locator('#enc-transcript').click();
|
|
await page.keyboard.type('Chief complaint: 3-day history of fever and cough.');
|
|
|
|
// Click generate — wait for the mocked /api/generate-hpi-encounter response
|
|
const [hpiResp] = await Promise.all([
|
|
page.waitForResponse('**/api/generate-hpi-encounter'),
|
|
page.click('#enc-generate-btn'),
|
|
]);
|
|
expect(hpiResp.status()).toBe(200);
|
|
|
|
// Output container un-hides and shows the mocked text
|
|
await expect(page.locator('#enc-output')).toBeVisible();
|
|
await expect(page.locator('#enc-hpi-text')).toContainText('MOCK HPI from encounter');
|
|
});
|
|
|
|
test('refine action → fetches /api/refine and updates the rendered HPI', async ({ authedPage: _, page }) => {
|
|
// Override /api/refine to return a recognisable marker so we can tell the refined
|
|
// content replaced the original.
|
|
await mockAI(page, {
|
|
'**/api/refine': { success: true, refined: 'REFINED-MARKER: now a longer narrative.', model: 'mock-gpt' },
|
|
});
|
|
await openTab(page);
|
|
|
|
await page.fill('#enc-age', '8 years');
|
|
await page.locator('#enc-transcript').click();
|
|
await page.keyboard.type('Fever and cough 3 days.');
|
|
await page.click('#enc-generate-btn');
|
|
await expect(page.locator('#enc-hpi-text')).toContainText('MOCK HPI', { timeout: 10000 });
|
|
|
|
// Type an instruction and hit refine
|
|
await page.fill('#enc-refine-input', 'Make it longer.');
|
|
const [refineResp] = await Promise.all([
|
|
page.waitForResponse('**/api/refine'),
|
|
page.click('#enc-refine-btn'),
|
|
]);
|
|
expect(refineResp.status()).toBe(200);
|
|
await expect(page.locator('#enc-hpi-text')).toContainText('REFINED-MARKER', { timeout: 10000 });
|
|
});
|
|
|
|
test('clear transcript button empties the contenteditable', async ({ authedPage: _, page }) => {
|
|
await mockAI(page);
|
|
await openTab(page);
|
|
await page.locator('#enc-transcript').click();
|
|
await page.keyboard.type('Some content.');
|
|
await expect(page.locator('#enc-transcript')).toContainText('Some content.');
|
|
await page.click('#enc-clear');
|
|
const text = await page.locator('#enc-transcript').innerText();
|
|
expect(text.trim()).toBe('');
|
|
});
|
|
});
|