// Bedside module smoke tests. // The harness renders the Calculators + Bedside components side-by-side, so // each test selects the target sub-pill (if any) and asserts a known string // is rendered. Bedside was promoted to a top-level tab, so there is no longer // a calc-nav-pill[data-calc="bedside"] — the panel is always visible in the // harness and always the full tab in the live app. const { test, expect } = require('@playwright/test'); async function openCalculators(page) { await page.goto('/e2e-harness.html'); await page.waitForFunction(() => window.__harnessReady === true); // Wait for the bedside component to finish injecting — #bedside-age is the // first input in the shared age→weight estimator at the top of the tab. await page.waitForSelector('#bedside-age'); } async function openBedside(page, subPill) { await openCalculators(page); if (subPill) { await page.click(`button.calc-pill[data-em="${subPill}"]`); } } test.describe('Bedside — top-level', () => { test('Calculators tab shows age-weight estimator', async ({ page }) => { await openCalculators(page); await expect(page.locator('#bedside-age')).toBeVisible(); await expect(page.locator('#bedside-formula')).toBeVisible(); await expect(page.locator('#bedside-weight')).toBeVisible(); }); test('Age → Weight: typing 3y auto-fills weight (APLS)', async ({ page }) => { await openCalculators(page); await page.fill('#bedside-age', '3y'); await expect(page.locator('#bedside-weight')).toHaveValue('14'); await expect(page.locator('#bedside-estimate-note')).toContainText('APLS'); }); test('Formula switch to Best Guess updates weight', async ({ page }) => { await openCalculators(page); await page.fill('#bedside-age', '3y'); await page.selectOption('#bedside-formula', 'bestguess'); await expect(page.locator('#bedside-weight')).toHaveValue('16'); // 2 * (3+5) = 16 await expect(page.locator('#bedside-estimate-note')).toContainText('Best Guess'); }); test('Clear button resets the estimator', async ({ page }) => { await openCalculators(page); await page.fill('#bedside-age', '5y'); await page.click('#btn-bedside-clear'); await expect(page.locator('#bedside-age')).toHaveValue(''); await expect(page.locator('#bedside-weight')).toHaveValue(''); }); }); test.describe('Bedside — every sub-pill renders', () => { const subPills = [ { key: 'neonatal', expected: /Neonatal Assessment/i }, { key: 'airway', expected: /Airway Management/i }, { key: 'cardiac', expected: /Cardiac Arrest/i }, { key: 'respiratory', expected: /Respiratory Management/i }, { key: 'ventilation', expected: /Oxygen & Ventilation/i }, { key: 'seizure', expected: /Status Epilepticus/i }, { key: 'sepsis', expected: /Sepsis & Fever/i }, { key: 'anaphylaxis', expected: /Anaphylaxis/i }, { key: 'sedation', expected: /Procedural Sedation/i }, { key: 'agitation', expected: /Acute Agitation/i }, { key: 'antiemetics', expected: /Antiemetics/i }, { key: 'antimicrobials', expected: /Empiric Antimicrobials/i }, { key: 'burns', expected: /Burn Management/i }, { key: 'toxicology', expected: /Toxicology/i }, { key: 'trauma', expected: /Trauma/i }, ]; for (const { key, expected } of subPills) { test(`${key} sub-pill shows header`, async ({ page }) => { await openBedside(page, key); await expect(page.locator(`#em-${key}`)).toContainText(expected); }); } }); test.describe('Bedside — dose calculators fire', () => { test('Status Epilepticus: Show Pathway renders timeline with weight', async ({ page }) => { await openBedside(page, 'seizure'); await page.fill('#seizure-weight', '20'); await page.click('#btn-seizure-calc'); await expect(page.locator('#seizure-result')).toContainText(/20 kg/); await expect(page.locator('#seizure-result')).toContainText(/Lorazepam/); await expect(page.locator('#seizure-result')).toContainText(/0\.1 mg\/kg/); // per-kg visible }); test('Sepsis: Show Approach renders Phoenix criteria + first-hour bundle', async ({ page }) => { await openBedside(page, 'sepsis'); await page.fill('#sepsis-weight', '25'); await page.click('#btn-sepsis-show'); await expect(page.locator('#sepsis-result')).toContainText(/Phoenix/); await expect(page.locator('#sepsis-result')).toContainText(/first-hour/i); }); test('Anaphylaxis: Calculate Doses shows weight-based epinephrine', async ({ page }) => { await openBedside(page, 'anaphylaxis'); await page.fill('#anaph-weight', '25'); await page.click('#btn-anaph-calc'); await expect(page.locator('#anaph-result')).toContainText(/Epinephrine/); await expect(page.locator('#anaph-result')).toContainText(/0\.25 mg/); // 25*0.01 }); test('Burns: body-parts calculator + Parkland', async ({ page }) => { await openBedside(page, 'burns'); await page.fill('#burn-weight', '20'); await page.fill('input[data-burn-region="head"]', '50'); // 50% of 13 (young) = 6.5 await page.fill('input[data-burn-region="ant_trunk"]', '100'); // 100% of 13 = 13 await page.click('#btn-burn-calc'); await expect(page.locator('#burn-result')).toContainText(/Parkland/); await expect(page.locator('#burn-result')).toContainText(/20 kg/); }); test('Airway: Calculate renders RSI drugs with per-kg', async ({ page }) => { await openBedside(page, 'airway'); await page.fill('#airway-weight', '20'); await page.fill('#airway-age', '5'); await page.click('#btn-airway-calc'); await expect(page.locator('#airway-result')).toContainText(/Ketamine/); await expect(page.locator('#airway-result')).toContainText(/mg\/kg/); // per-kg visible await expect(page.locator('#airway-result')).toContainText(/ETT/); }); }); test.describe('Bedside — interactive widgets', () => { test('Lightbox: seizure pathway image opens and closes', async ({ page }) => { await openBedside(page, 'seizure'); await page.click('button[data-img-src="/img/epilepsy_eiic_pathway.png"]'); await expect(page.locator('#img-lightbox')).toBeVisible(); await expect(page.locator('#img-lightbox-img')).toHaveAttribute('src', /epilepsy_eiic_pathway/); await page.click('#img-lightbox-close'); await expect(page.locator('#img-lightbox')).toBeHidden(); }); test('Lightbox: NRP pathway image opens on neonatal sub-pill', async ({ page }) => { await openBedside(page, 'neonatal'); // The "View pathway image" button sits inside a collapsed
// titled "NRP Resuscitation Pathway" — expand it first. await page.getByText('NRP Resuscitation Pathway').click(); await page.click('button[data-img-src="/img/nrp_pathway.png"]'); await expect(page.locator('#img-lightbox')).toBeVisible(); await expect(page.locator('#img-lightbox-img')).toHaveAttribute('src', /nrp_pathway/); await page.click('#img-lightbox-close'); await expect(page.locator('#img-lightbox')).toBeHidden(); }); test('Ventilation: Show Reference renders pressure-time SVG', async ({ page }) => { await openBedside(page, 'ventilation'); await page.fill('#vent-weight', '20'); await page.click('#btn-vent-show'); await expect(page.locator('#vent-result svg')).toBeVisible(); await expect(page.locator('#vent-result')).toContainText(/Target SpO2/); await expect(page.locator('#vent-result')).toContainText(/PEEP/); }); });