// ============================================================ // WELL VISIT — detailed workflow across the 4 sub-tabs: // 1. By Visit Age (reference display) // 2. Milestones — generate narrative from toggles // 3. SSHADESS — generate psychosocial assessment // 4. Visit Note — end-to-end generate a well-child note // All AI calls are mocked. // ============================================================ 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="wellvisit"]'); await page.waitForFunction(() => { const el = document.getElementById('wellvisit-tab'); return el && el.classList.contains('active') && el.innerHTML.trim().length > 100; }, { timeout: 15000 }); } async function openSubtab(page, key) { await page.click(`button.wv-subtab-btn[data-subtab="${key}"]`); await page.waitForFunction( (k) => !document.getElementById('wv-panel-' + k).classList.contains('hidden'), key, { timeout: 5000 } ); } test.describe('Well Visit — detailed workflows', () => { test('By Visit Age — selecting a visit age renders content detail', async ({ authedPage: _, page }) => { await openTab(page); await openSubtab(page, 'byvisit'); // Dropdown must exist and be non-empty const options = await page.locator('#wv-visit-select option').count(); expect(options).toBeGreaterThan(1); // Pick the 2nd option (skip placeholder). Detail container should populate. await page.selectOption('#wv-visit-select', { index: 1 }); await expect.poll(async () => (await page.locator('#wv-visit-detail').innerText()).trim().length, { timeout: 5000 }).toBeGreaterThan(0); }); test('Milestones — toggle "All yes" then generate → narrative renders', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page); await openSubtab(page, 'milestones'); await page.fill('#ms-age', '12 months'); await page.selectOption('#ms-gender', 'Male'); // Age group picker — pick the first real option if there is a placeholder const optCount = await page.locator('#ms-age-group option').count(); if (optCount > 1) await page.selectOption('#ms-age-group', { index: 1 }); // Mark everything achieved (the "All yes" action), wait for checklist to have at least one item await page.waitForFunction(() => document.querySelectorAll('#milestone-checklist .milestone-row').length > 0, null, { timeout: 5000 }).catch(() => {}); await page.click('#ms-all-yes').catch(() => {}); const [resp] = await Promise.all([ page.waitForResponse('**/api/generate-milestone-narrative'), page.click('#ms-generate-btn'), ]); expect(resp.status()).toBe(200); await expect(page.locator('#ms-narrative-text')).toContainText('MOCK developmental narrative', { timeout: 10000 }); }); test('SSHADESS — domain list renders + generate fires /api/well-visit/shadess', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page); // The SSHADESS subtab button is display:none until the user picks a 12+ // visit in byvisit. Iterate the visit dropdown options and pick one whose // value looks like a 12+ year visit. await openSubtab(page, 'byvisit'); await page.waitForFunction(() => document.querySelectorAll('#wv-visit-select option').length > 2, null, { timeout: 5000 }); const adolescentOpt = await page.locator('#wv-visit-select option').evaluateAll((opts) => { const match = opts.find(o => /1[2-8].*(year|yr)/i.test(o.textContent || '') || /1[2-8].*year/i.test(o.value || '')); return match ? match.value : null; }); if (!adolescentOpt) test.skip(true, 'No 12+ visit option in dropdown — cannot expose SSHADESS subtab'); await page.selectOption('#wv-visit-select', adolescentOpt); // Now the shadess subtab button becomes visible await expect(page.locator('button.wv-subtab-btn[data-subtab="shadess"]')).toBeVisible({ timeout: 5000 }); await openSubtab(page, 'shadess'); // Age 14 — should surface SSHADESS (12+ only) await page.fill('#shadess-age', '14 years'); await page.selectOption('#shadess-gender', 'Female'); // Wait for domains to render — JS populates after age/gender are set await expect.poll(async () => (await page.locator('#shadess-domains').innerText()).trim().length, { timeout: 5000 }).toBeGreaterThan(0); // Generate refuses with a toast if no domain has data. Fill the first // domain's free-text comment so hasData is true. await page.locator('.shadess-comment').first().fill('Lives at home with parents, supportive environment.'); const [resp] = await Promise.all([ page.waitForResponse('**/api/well-visit/shadess', { timeout: 10000 }), page.click('#btn-shadess-generate'), ]); expect(resp.status()).toBe(200); await expect(page.locator('#shadess-result-text')).toContainText('MOCK SSHADESS', { timeout: 10000 }); }); test('Visit Note — minimal generate → mocked well-visit note in output', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page); await openSubtab(page, 'note'); await page.fill('#wv-note-age', '5 years'); await page.selectOption('#wv-note-gender', 'Male'); // Provide a vitals string so the request body has something await page.fill('#wv-vitals', 'T 37.0, HR 95, RR 22, BP 95/60, SpO2 99% RA'); // Use the transcript area for content await page.locator('#wv-transcript').click(); await page.keyboard.type('Parent reports child is doing well; no concerns.'); const [resp] = await Promise.all([ page.waitForResponse('**/api/well-visit/note', { timeout: 15000 }), page.click('#btn-wv-generate'), ]); expect(resp.status()).toBe(200); await expect(page.locator('#wv-note-output')).toBeVisible({ timeout: 10000 }); await expect(page.locator('#wv-note-text')).toContainText('MOCK well visit note', { timeout: 10000 }); }); });