Covers BP / BMI / BSA / Weight-Based Dosing / Growth / Bilirubin / Vitals / Resus Meds / GCS / Equipment. Each tab gets: panel-loads test + one full input→calculate→result flow. Extras for BP (Clear), Dose (max cap), Growth (sub-pill), Bili (Bhutani sub-pill), GCS (motor change, infant switch), Equipment (empty re-select hides). All 53 e2e tests pass (26 Bedside + 27 top-calculators).
220 lines
8.9 KiB
JavaScript
220 lines
8.9 KiB
JavaScript
// Smoke tests for the 10 top-row calculator tabs (everything except Bedside).
|
||
// Each test navigates to its tab, fills inputs, clicks Calculate/Assess,
|
||
// and asserts a known string appears in the result. Catches the "tab loads
|
||
// but button does nothing" class of regression.
|
||
|
||
const { test, expect } = require('@playwright/test');
|
||
|
||
async function openCalculators(page) {
|
||
await page.goto('/e2e-harness.html');
|
||
await page.waitForFunction(() => window.__harnessReady === true);
|
||
await page.waitForSelector('button.calc-nav-pill[data-calc="bp"]');
|
||
}
|
||
|
||
async function selectTab(page, tabName) {
|
||
await page.click(`button.calc-nav-pill[data-calc="${tabName}"]`);
|
||
await expect(page.locator(`#calc-${tabName}`)).toBeVisible();
|
||
}
|
||
|
||
test.describe('Top-level calculators — panel loads', () => {
|
||
const tabs = ['bp', 'bmi', 'growth', 'bili', 'vitals', 'bsa', 'dose', 'resus', 'gcs', 'equipment'];
|
||
for (const tab of tabs) {
|
||
test(`${tab} panel becomes visible when pill clicked`, async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, tab);
|
||
});
|
||
}
|
||
});
|
||
|
||
test.describe('Blood Pressure percentile', () => {
|
||
test('5 yr, male, height 110, BP 105/65 → produces a result', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bp');
|
||
await page.fill('#bp-age', '5');
|
||
await page.selectOption('#bp-sex', 'male');
|
||
await page.fill('#bp-height', '110');
|
||
await page.fill('#bp-systolic', '105');
|
||
await page.fill('#bp-diastolic', '65');
|
||
await page.click('#btn-calc-bp');
|
||
await expect(page.locator('#bp-result')).not.toHaveClass(/hidden/);
|
||
// Result should mention a percentile or classification
|
||
await expect(page.locator('#bp-result')).toContainText(/percentile|Normal|Elevated|HTN|Stage/i);
|
||
});
|
||
|
||
test('Clear button hides result', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bp');
|
||
await page.fill('#bp-age', '5');
|
||
await page.selectOption('#bp-sex', 'male');
|
||
await page.fill('#bp-height', '110');
|
||
await page.fill('#bp-systolic', '105');
|
||
await page.fill('#bp-diastolic', '65');
|
||
await page.click('#btn-calc-bp');
|
||
await page.click('#btn-clear-bp');
|
||
await expect(page.locator('#bp-result')).toHaveClass(/hidden/);
|
||
await expect(page.locator('#bp-age')).toHaveValue('');
|
||
});
|
||
});
|
||
|
||
test.describe('BMI percentile', () => {
|
||
test('7 yr, male, 25 kg, 120 cm → BMI computed', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bmi');
|
||
await page.fill('#bmi-age-yr', '7');
|
||
await page.selectOption('#bmi-age-mo', '0');
|
||
await page.selectOption('#bmi-sex', 'male');
|
||
await page.fill('#bmi-weight', '25');
|
||
await page.fill('#bmi-height', '120');
|
||
await page.click('#btn-calc-bmi');
|
||
await expect(page.locator('#bmi-result')).not.toHaveClass(/hidden/);
|
||
// BMI = 25 / 1.20² = 17.36 — expect something recognizable
|
||
await expect(page.locator('#bmi-result')).toContainText(/17\.|BMI|percentile/i);
|
||
});
|
||
});
|
||
|
||
test.describe('Body Surface Area (Mosteller)', () => {
|
||
test('20 kg, 110 cm → BSA shown', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bsa');
|
||
await page.fill('#bsa-weight', '20');
|
||
await page.fill('#bsa-height', '110');
|
||
await page.click('#btn-calc-bsa');
|
||
await expect(page.locator('#bsa-result')).not.toHaveClass(/hidden/);
|
||
// sqrt(110*20/3600) = 0.782
|
||
await expect(page.locator('#bsa-result')).toContainText(/0\.78|BSA|m²|m2/i);
|
||
});
|
||
});
|
||
|
||
test.describe('Weight-based dosing', () => {
|
||
test('15 kg × 10 mg/kg → 150 mg shown', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'dose');
|
||
await page.fill('#dose-weight', '15');
|
||
await page.fill('#dose-per-kg', '10');
|
||
await page.click('#btn-calc-dose');
|
||
await expect(page.locator('#dose-result')).not.toHaveClass(/hidden/);
|
||
await expect(page.locator('#dose-result')).toContainText(/150/);
|
||
});
|
||
|
||
test('Max cap respected: 15 kg × 100 mg/kg capped at 500 mg', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'dose');
|
||
await page.fill('#dose-weight', '15');
|
||
await page.fill('#dose-per-kg', '100');
|
||
await page.fill('#dose-max', '500');
|
||
await page.click('#btn-calc-dose');
|
||
await expect(page.locator('#dose-result')).toContainText(/500/);
|
||
});
|
||
});
|
||
|
||
test.describe('Growth charts', () => {
|
||
test('3 yr male, 14 kg → Weight-for-Age result', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'growth');
|
||
await page.selectOption('#growth-sex', 'male');
|
||
await page.fill('#growth-age-yr', '3');
|
||
await page.fill('#growth-weight', '14');
|
||
await page.click('#btn-calc-growth');
|
||
await expect(page.locator('#growth-result')).not.toHaveClass(/hidden/);
|
||
await expect(page.locator('#growth-result')).toContainText(/percentile|z-score|%ile|z=/i);
|
||
});
|
||
|
||
test('Sub-pill switches to Length-for-Age', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'growth');
|
||
await page.click('button.calc-pill[data-growth="lfa"]');
|
||
await expect(page.locator('button.calc-pill[data-growth="lfa"]')).toHaveClass(/active/);
|
||
});
|
||
});
|
||
|
||
test.describe('Bilirubin (AAP 2022)', () => {
|
||
test('GA 38, age 48h, TSB 12.5, no risk factors → AAP assessment', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bili');
|
||
await page.selectOption('#bili-ga', '38');
|
||
await page.fill('#bili-age-hours', '48');
|
||
await page.fill('#bili-tsb', '12.5');
|
||
await page.selectOption('#bili-risk', 'none');
|
||
await page.click('#btn-calc-bili-aap');
|
||
await expect(page.locator('#bili-result')).not.toHaveClass(/hidden/);
|
||
await expect(page.locator('#bili-result')).toContainText(/phototherapy|threshold|AAP|bilirubin/i);
|
||
});
|
||
|
||
test('Bhutani sub-pill: age 48h, TSB 8.5 → risk zone', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'bili');
|
||
await page.click('button.calc-pill[data-bili="bhutani"]');
|
||
await page.fill('#bhutani-age', '48');
|
||
await page.fill('#bhutani-tsb', '8.5');
|
||
await page.click('#btn-calc-bhutani');
|
||
await expect(page.locator('#bili-result')).not.toHaveClass(/hidden/);
|
||
await expect(page.locator('#bili-result')).toContainText(/risk|zone|low|high|intermediate/i);
|
||
});
|
||
});
|
||
|
||
test.describe('Vital signs reference', () => {
|
||
test('Selecting 1-3 yr shows HR range', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'vitals');
|
||
await page.selectOption('#vitals-age-select', '1-3yr');
|
||
await expect(page.locator('#vitals-result')).not.toHaveClass(/hidden/);
|
||
// Expected: HR 70-110
|
||
await expect(page.locator('#vitals-result')).toContainText(/70|HR/i);
|
||
});
|
||
});
|
||
|
||
test.describe('Resus meds', () => {
|
||
test('15 kg → multiple weight-based drug doses rendered', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'resus');
|
||
await page.fill('#resus-weight', '15');
|
||
await page.click('#btn-calc-resus');
|
||
await expect(page.locator('#resus-result')).not.toHaveClass(/hidden/);
|
||
// At least epinephrine + atropine should appear for any resus dose table
|
||
await expect(page.locator('#resus-result')).toContainText(/epinephrine|epi/i);
|
||
await expect(page.locator('#resus-result')).toContainText(/atropine/i);
|
||
});
|
||
});
|
||
|
||
test.describe('Glasgow Coma Scale', () => {
|
||
test('Child defaults (4/5/6) → GCS 15 shown', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'gcs');
|
||
// selects default to max scores → 4+5+6 = 15
|
||
await expect(page.locator('#gcs-result')).toContainText(/15/);
|
||
});
|
||
|
||
test('Changing motor to "None" (1) → GCS drops', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'gcs');
|
||
await page.selectOption('#gcs-child-motor', '1');
|
||
await expect(page.locator('#gcs-result')).toContainText(/10/); // 4+5+1
|
||
});
|
||
|
||
test('Switch to infant panel', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'gcs');
|
||
await page.click('button.calc-pill[data-gcs="infant"]');
|
||
await expect(page.locator('#gcs-infant-panel')).toBeVisible();
|
||
await expect(page.locator('#gcs-child-panel')).toBeHidden();
|
||
});
|
||
});
|
||
|
||
test.describe('Equipment sizing', () => {
|
||
test('Selecting "1 year" renders sizes', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'equipment');
|
||
await page.selectOption('#equip-age-select', '1yr');
|
||
await expect(page.locator('#equip-result')).not.toHaveClass(/hidden/);
|
||
// Should mention ETT and at least one other item
|
||
await expect(page.locator('#equip-result')).toContainText(/ETT|Endotracheal/i);
|
||
});
|
||
|
||
test('Selecting empty option hides result', async ({ page }) => {
|
||
await openCalculators(page);
|
||
await selectTab(page, 'equipment');
|
||
await page.selectOption('#equip-age-select', '1yr');
|
||
await page.selectOption('#equip-age-select', '');
|
||
await expect(page.locator('#equip-result')).toHaveClass(/hidden/);
|
||
});
|
||
});
|