pediatric-ai-scribe-v3/test/calculators-data.test.js
2026-05-08 04:36:39 +02:00

438 lines
26 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
function loadVitals() {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'vitals.json'), 'utf8');
return JSON.parse(raw);
}
test('vitals.json exposes all calculator age groups', () => {
const data = loadVitals();
assert.equal(data.version, '1.0');
assert.deepEqual(Object.keys(data.ageGroups), [
'premie',
'0-3mo',
'3-6mo',
'6-12mo',
'1-3yr',
'3-6yr',
'6-12yr',
'>12yr'
]);
});
test('vitals.json preserves key Harriet Lane ranges', () => {
const groups = loadVitals().ageGroups;
assert.equal(groups.premie.hr.awake, '120-170');
assert.equal(groups.premie.spo2, '88-95% (target)');
assert.equal(groups['0-3mo'].sbp, '65-85');
assert.equal(groups['3-6mo'].rr, '30-45');
assert.equal(groups['1-3yr'].weight, '10-15 kg');
assert.equal(groups['6-12yr'].hr.sleeping, '50-85');
assert.equal(groups['>12yr'].dbp, '65-85');
});
test('vitals.json entries have complete renderable fields', () => {
const groups = loadVitals().ageGroups;
Object.entries(groups).forEach(([key, group]) => {
assert.ok(group.label, `${key}.label`);
assert.ok(group.hr && group.hr.awake && group.hr.sleeping, `${key}.hr`);
['rr', 'sbp', 'dbp', 'temp', 'weight', 'spo2'].forEach((field) => {
assert.ok(group[field], `${key}.${field}`);
});
assert.ok(Array.isArray(group.notes) && group.notes.length > 0, `${key}.notes`);
});
});
test('resus-meds.json exposes all PALS medication formulas', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'resus-meds.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(data.medications.map((m) => m.formula), [
'adenosine',
'amiodarone',
'atropine',
'calciumChloride',
'calciumGluconate',
'dextrose',
'epinephrine',
'hydrocortisone',
'insulin',
'lidocaine',
'magnesiumSulfate',
'naloxone',
'sodiumBicarbonate'
]);
});
test('resus dose formulas preserve representative outputs', async () => {
const { calculateResusDose } = await import('../public/js/calculators/resus.js');
assert.equal(calculateResusDose('adenosine', 20).dose, '2 mg (0.1 mg/kg)');
assert.equal(calculateResusDose('epinephrine', 10).dose, '0.1 mg IV/IO (0.01 mg/kg of 0.1 mg/mL = 1 mL) q3-5 min');
assert.equal(calculateResusDose('magnesiumSulfate', 50).dose, '2000 mg (50 mg/kg)');
assert.equal(calculateResusDose('sodiumBicarbonate', 8).extra, 'Dilute to 0.5 mEq/mL (use 4.2% solution) for neonates/small infants.');
});
test('fenton-lms.json preserves key LMS anchors', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'fenton-lms.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(data.weightLms.male['22'], { L: 0.21, M: 496, S: 0.17 });
assert.deepEqual(data.weightLms.male['40'], { L: 0.01, M: 3530, S: 0.12 });
assert.deepEqual(data.weightLms.female['22'], { L: 0.23, M: 474, S: 0.17 });
assert.deepEqual(data.weightLms.female['40'], { L: 0.01, M: 3340, S: 0.12 });
});
test('neonatal Fenton helpers preserve interpolation and classification', async () => {
const neonatal = await import('../public/js/calculators/neonatal.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'fenton-lms.json'), 'utf8');
const data = JSON.parse(raw).weightLms;
assert.deepEqual(neonatal.interpolateLMS(data.male, 30), { L: 0.18, M: 1430, S: 0.14 });
assert.equal(Math.round(neonatal.interpolateLMS(data.female, 30.5).M), 1415);
assert.equal(neonatal.classifyGA(36, 6).label, 'Late Preterm');
assert.equal(neonatal.classifyBirthWeight(900).label, 'Extremely Low Birth Weight (ELBW)');
assert.equal(neonatal.classifyWeight(50).label, 'AGA');
});
test('equipment.json preserves equipment age groups and anchors', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'equipment.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(Object.keys(data.ageGroups), [
'premie',
'newborn',
'6mo',
'1yr',
'2-3yr',
'4-6yr',
'7-10yr',
'11-15yr',
'16yr'
]);
assert.equal(data.ageGroups.newborn.ett, '3.0-3.5');
assert.equal(data.ageGroups['1yr'].lma, '2');
assert.equal(data.ageGroups['4-6yr'].blade, 'Miller 2 / MAC 2');
assert.equal(data.ageGroups['16yr'].foley, '12 Fr');
});
test('equipment.json entries have complete renderable fields', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'equipment.json'), 'utf8');
const groups = JSON.parse(raw).ageGroups;
Object.entries(groups).forEach(([key, group]) => {
['label', 'bvm', 'nasal', 'oral', 'blade', 'ett', 'lma', 'glidescope', 'iv', 'cvl', 'ngt', 'chest', 'foley'].forEach((field) => {
assert.ok(group[field], `${key}.${field}`);
});
});
});
test('bhutani-zones.json preserves risk-zone curves', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bhutani-zones.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(Object.keys(data.zones), ['p95', 'p75', 'p40']);
assert.equal(data.zones.p95['24'], 9.6);
assert.equal(data.zones.p75['48'], 12.6);
assert.equal(data.zones.p40['120'], 13.2);
});
test('bilirubin helpers preserve Bhutani interpolation and classification', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bhutani-zones.json'), 'utf8');
const zones = JSON.parse(raw).zones;
assert.equal(bilirubin.interpolateThreshold(zones.p95, 21), 9.05);
assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 10).zone, 'High-Risk Zone');
assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 8).zone, 'High-Intermediate Zone');
assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 7).zone, 'Low-Intermediate Zone');
assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 5).zone, 'Low-Risk Zone');
});
test('aap-bilirubin-thresholds.json preserves treatment threshold anchors', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(Object.keys(data.phototherapy.noRisk), ['35', '36', '37', '38', '39', '40']);
assert.deepEqual(Object.keys(data.phototherapy.risk), ['35', '36', '37', '38']);
assert.deepEqual(Object.keys(data.exchange.noRisk), ['35', '36', '37', '38']);
assert.deepEqual(Object.keys(data.exchange.risk), ['35', '36', '37', '38']);
assert.equal(data.phototherapy.noRisk['35']['24'], 10.6);
assert.equal(data.phototherapy.noRisk['40']['48'], 17.0);
assert.equal(data.phototherapy.risk['38']['72'], 16.6);
assert.equal(data.exchange.noRisk['38']['96'], 27.0);
assert.equal(data.exchange.risk['35']['24'], 16.1);
});
test('bilirubin helpers preserve AAP 2022 GA plateau selection and interpolation', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(bilirubin.interpolateThreshold(data.phototherapy.noRisk['35'], 12.5), 8.6);
assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 41, 'none').phototherapy['48'], 17.0);
assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 39, 'none').phototherapy['48'], 16.6);
assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 41, 'medium').phototherapy['48'], 14.0);
assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 40, 'none').exchange['48'], 24.0);
});
test('AAP bilirubin sample calculation below phototherapy threshold', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
const assessment = bilirubin.calculateAapBilirubinAssessment(data, 40, 48, 16.0, 'none');
assert.equal(assessment.phototherapyThreshold, 17.0);
assert.equal(assessment.exchangeThreshold, 24.0);
assert.equal(assessment.phototherapyMargin, -1.0);
assert.equal(assessment.exchangeMargin, -8.0);
assert.equal(assessment.abovePhototherapy, false);
assert.equal(assessment.aboveExchange, false);
assert.equal(assessment.status, 'below');
});
test('AAP bilirubin sample calculation above phototherapy threshold', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
const assessment = bilirubin.calculateAapBilirubinAssessment(data, 39, 48, 17.0, 'none');
assert.equal(assessment.phototherapyThreshold, 16.6);
assert.equal(assessment.exchangeThreshold, 24.0);
assert.equal(Math.round(assessment.phototherapyMargin * 10) / 10, 0.4);
assert.equal(assessment.abovePhototherapy, true);
assert.equal(assessment.aboveExchange, false);
assert.equal(assessment.status, 'phototherapy');
});
test('AAP bilirubin sample calculation above exchange threshold', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
const assessment = bilirubin.calculateAapBilirubinAssessment(data, 35, 24, 16.2, 'medium');
assert.equal(assessment.phototherapyThreshold, 8.9);
assert.equal(assessment.exchangeThreshold, 16.1);
assert.equal(Math.round(assessment.phototherapyMargin * 10) / 10, 7.3);
assert.equal(Math.round(assessment.exchangeMargin * 10) / 10, 0.1);
assert.equal(assessment.abovePhototherapy, true);
assert.equal(assessment.aboveExchange, true);
assert.equal(assessment.status, 'exchange');
});
test('AAP bilirubin sample calculation interpolates fractional age', async () => {
const bilirubin = await import('../public/js/calculators/bilirubin.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
const data = JSON.parse(raw);
const assessment = bilirubin.calculateAapBilirubinAssessment(data, 35, 12.5, 8.55, 'none');
assert.equal(assessment.phototherapyThreshold, 8.6);
assert.equal(assessment.abovePhototherapy, false);
assert.equal(assessment.status, 'below');
});
test('bmi-lms.json exposes complete CDC BMI-for-age tables', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
['male', 'female'].forEach((sex) => {
const ages = Object.keys(data.lms[sex]).map(Number).sort((a, b) => a - b);
assert.equal(ages[0], 24);
assert.equal(ages[ages.length - 1], 240);
assert.equal(ages.length, 37);
ages.forEach((age) => {
const row = data.lms[sex][age];
assert.equal(typeof row.L, 'number', `${sex}.${age}.L`);
assert.equal(typeof row.M, 'number', `${sex}.${age}.M`);
assert.equal(typeof row.S, 'number', `${sex}.${age}.S`);
});
});
});
test('BMI sample calculation preserves healthy-weight result', async () => {
const bmi = await import('../public/js/calculators/bmi.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
const data = JSON.parse(raw).lms;
const assessment = bmi.calculateBMIAssessment(data, 'male', 10, 32, 140);
assert.equal(assessment.ageMonths, 120);
assert.equal(Number(assessment.bmi.toFixed(1)), 16.3);
assert.equal(Number(assessment.z.toFixed(2)), -0.17);
assert.equal(assessment.percentile, 43.42);
assert.equal(assessment.classification.text, 'Healthy Weight');
});
test('BMI sample calculation preserves obesity classification', async () => {
const bmi = await import('../public/js/calculators/bmi.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
const data = JSON.parse(raw).lms;
const assessment = bmi.calculateBMIAssessment(data, 'male', 12, 70, 150);
assert.equal(Number(assessment.bmi.toFixed(1)), 31.1);
assert.equal(assessment.percentile, 98.97);
assert.equal(assessment.classification.text, 'Class 2 Severe Obesity');
assert.equal(Number(assessment.classification.pctOf95.toFixed(0)), 128);
assert.equal(Number(assessment.classification.bmi95.toFixed(1)), 24.2);
});
test('BMI sample calculation preserves fractional-age interpolation', async () => {
const bmi = await import('../public/js/calculators/bmi.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
const data = JSON.parse(raw).lms;
const lms = bmi.interpolateLMS(data.male, 123);
assert.equal(Number(lms.M.toFixed(4)), 16.7806);
const assessment = bmi.calculateBMIAssessment(data, 'male', 10.5, 40, 145);
assert.equal(assessment.ageMonths, 126);
assert.equal(assessment.percentile, 79.26);
assert.equal(assessment.classification.text, 'Healthy Weight');
});
test('BMI calculator preserves legacy outputs across sex/age/body-size combinations', async () => {
const bmi = await import('../public/js/calculators/bmi.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
const data = JSON.parse(raw).lms;
const cases = [
{ name: 'male healthy 10y', sex: 'male', ageYears: 10, weightKg: 32, heightCm: 140, ageMonths: 120, bmi: 16.3, z: -0.17, percentile: 43.42, classification: 'Healthy Weight', pctOf95: 74, bmi95: 22.2 },
{ name: 'female obese 8y', sex: 'female', ageYears: 8, weightKg: 35, heightCm: 130, ageMonths: 96, bmi: 20.7, z: 1.65, percentile: 95.03, classification: 'Obese (Class 1)', pctOf95: 100, bmi95: 20.7 },
{ name: 'male class 2 severe 12y', sex: 'male', ageYears: 12, weightKg: 70, heightCm: 150, ageMonths: 144, bmi: 31.1, z: 2.32, percentile: 98.97, classification: 'Class 2 Severe Obesity', pctOf95: 128, bmi95: 24.2 },
{ name: 'female underweight 5y', sex: 'female', ageYears: 5, weightKg: 13, heightCm: 110, ageMonths: 60, bmi: 10.7, z: -7.66, percentile: 0, classification: 'Underweight', pctOf95: 59, bmi95: 18.3 },
{ name: 'male fractional age 10.5y', sex: 'male', ageYears: 10.5, weightKg: 40, heightCm: 145, ageMonths: 126, bmi: 19.0, z: 0.82, percentile: 79.26, classification: 'Healthy Weight', pctOf95: 84, bmi95: 22.7 },
{ name: 'female healthy 15y', sex: 'female', ageYears: 15, weightKg: 52, heightCm: 162, ageMonths: 180, bmi: 19.8, z: -0.04, percentile: 48.44, classification: 'Healthy Weight', pctOf95: 70, bmi95: 28.1 },
{ name: 'male overweight 6y', sex: 'male', ageYears: 6, weightKg: 26, heightCm: 120, ageMonths: 72, bmi: 18.1, z: 1.51, percentile: 93.42, classification: 'Overweight', pctOf95: 98, bmi95: 18.4 },
{ name: 'female class 3 severe 14y', sex: 'female', ageYears: 14, weightKg: 100, heightCm: 155, ageMonths: 168, bmi: 41.6, z: 2.61, percentile: 99.55, classification: 'Class 3 Severe Obesity', pctOf95: 153, bmi95: 27.3 }
];
cases.forEach((expected) => {
const actual = bmi.calculateBMIAssessment(data, expected.sex, expected.ageYears, expected.weightKg, expected.heightCm);
assert.equal(actual.ageMonths, expected.ageMonths, expected.name + ' ageMonths');
assert.equal(Number(actual.bmi.toFixed(1)), expected.bmi, expected.name + ' bmi');
assert.equal(Number(actual.z.toFixed(2)), expected.z, expected.name + ' z');
assert.equal(actual.percentile, expected.percentile, expected.name + ' percentile');
assert.equal(actual.classification.text, expected.classification, expected.name + ' classification');
assert.equal(Number(actual.classification.pctOf95.toFixed(0)), expected.pctOf95, expected.name + ' pctOf95');
assert.equal(Number(actual.classification.bmi95.toFixed(1)), expected.bmi95, expected.name + ' bmi95');
});
});
test('growth-lms.json exposes complete WHO/CDC/Fenton datasets', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'growth-lms.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
assert.deepEqual(Object.keys(data.lms), ['wfaLMS', 'cdcWfaLMS', 'lfaLMS', 'cdcLfaLMS', 'hcfaLMS', 'wflLMS', 'fentonLMS']);
Object.entries(data.lms).forEach(([tableName, bySex]) => {
['male', 'female'].forEach((sex) => {
const rows = bySex[sex];
assert.ok(rows, `${tableName}.${sex}`);
const keys = Object.keys(rows).map(Number).sort((a, b) => a - b);
assert.ok(keys.length > 0, `${tableName}.${sex}.rows`);
keys.forEach((key) => {
assert.equal(typeof rows[key].L, 'number', `${tableName}.${sex}.${key}.L`);
assert.equal(typeof rows[key].M, 'number', `${tableName}.${sex}.${key}.M`);
assert.equal(typeof rows[key].S, 'number', `${tableName}.${sex}.${key}.S`);
});
});
});
});
test('growth calculator preserves legacy outputs across chart combinations', async () => {
const growth = await import('../public/js/calculators/growth.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'growth-lms.json'), 'utf8');
const data = JSON.parse(raw).lms;
const cases = [
{ name: 'who wfa male 6mo 8kg', table: data.wfaLMS.male, x: 6, value: 8, z: 0.07, percentile: 52.79, lms: { L: 0.1256, M: 7.9389, S: 0.10957 } },
{ name: 'cdc wfa female 72mo 20kg', table: data.cdcWfaLMS.female, x: 72, value: 20, z: -0.11, percentile: 45.51, lms: { L: -1.326764, M: 20.3364, S: 0.14959 } },
{ name: 'who lfa male 12mo 75cm', table: data.lfaLMS.male, x: 12, value: 75, z: -0.31, percentile: 37.79, lms: { L: 1, M: 75.7391, S: 0.03137 } },
{ name: 'cdc lfa female 120mo 140cm', table: data.cdcLfaLMS.female, x: 120, value: 140, z: 0.26, percentile: 60.43, lms: { L: 0.284749, M: 138.2112, S: 0.048705 } },
{ name: 'who hcfa male 12mo 46cm', table: data.hcfaLMS.male, x: 12, value: 46, z: -0.05, percentile: 48.02, lms: { L: 1, M: 46.0637, S: 0.02789 } },
{ name: 'who wfl female 70cm 8kg', table: data.wflLMS.female, x: 70, value: 8, z: -0.22, percentile: 41.17, lms: { L: -0.3833, M: 8.163, S: 0.09068 } },
{ name: 'fenton male 30w 1.5kg', table: data.fentonLMS.male, x: 30, value: 1500, z: 0.34, percentile: 63.41, lms: { L: 0.18, M: 1430, S: 0.14 } }
];
cases.forEach((expected) => {
const actual = growth.calculateLmsPercentile(expected.table, expected.x, expected.value);
assert.equal(Number(actual.z.toFixed(2)), expected.z, expected.name + ' z');
assert.equal(Number(actual.percentile.toFixed(2)), expected.percentile, expected.name + ' percentile');
assert.equal(Number(actual.lms.L.toFixed(6)), expected.lms.L, expected.name + ' L');
assert.equal(Number(actual.lms.M.toFixed(4)), expected.lms.M, expected.name + ' M');
assert.equal(Number(actual.lms.S.toFixed(6)), expected.lms.S, expected.name + ' S');
});
});
test('growth helper exports preserve mid-parental height and result rendering', async () => {
const growth = await import('../public/js/calculators/growth.js');
assert.deepEqual(growth.calcMidParentalHeight(160, 180, 'male'), { target: 176.5, low: 168, high: 185 });
assert.deepEqual(growth.calcMidParentalHeight(160, 180, 'female'), { target: 163.5, low: 155, high: 172 });
assert.equal(Number(growth.valueFromLMS(1, 75, 0.03, 0).toFixed(2)), 75);
const html = growth.renderGrowthResult('Weight-for-Age', 8, 'kg', 0.07, 52.79);
assert.match(html, /52\.79<span style="font-size:12px;">th/);
assert.match(html, /Weight-for-Age: 8 kg/);
});
test('bp-reference.json exposes complete height LMS and coefficient tables', () => {
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bp-reference.json'), 'utf8');
const data = JSON.parse(raw);
assert.equal(data.version, '1.0');
['female', 'male'].forEach((sex) => {
['L', 'M', 'S'].forEach((field) => {
assert.equal(data.heightLms[sex][field].length, 218, `${sex}.${field}`);
data.heightLms[sex][field].forEach((value, index) => {
assert.equal(typeof value, 'number', `${sex}.${field}.${index}`);
});
});
['sys', 'dia'].forEach((type) => {
assert.equal(data.coefficients[sex][type].length, 99, `${sex}.${type}`);
data.coefficients[sex][type].forEach((row, index) => {
assert.equal(row.length, 13, `${sex}.${type}.${index}`);
row.forEach((value, col) => assert.equal(typeof value, 'number', `${sex}.${type}.${index}.${col}`));
});
});
});
});
test('BP calculator preserves legacy outputs across sex/age/height/BP combinations', async () => {
const bp = await import('../public/js/calculators/bp.js');
const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bp-reference.json'), 'utf8');
const data = JSON.parse(raw);
const cases = [
{ name: 'male child normal', age: 5, sex: 'male', heightCm: 110, sys: 95, dia: 55, heightPctile: 61.69, heightZ: 0.3, sysPctile: 56, diaPctile: 56, sys50: 93, sys90: 106, sys95: 109, dia50: 53, dia90: 65, dia95: 69, sysClass: 'normal', diaClass: 'normal', classification: 'normal' },
{ name: 'female child stage1 systolic', age: 8, sex: 'female', heightCm: 130, sys: 116, dia: 70, heightPctile: 67.28, heightZ: 0.45, sysPctile: 97, diaPctile: 86, sys50: 97, sys90: 110, sys95: 113, dia50: 59, dia90: 72, dia95: 75, sysClass: 'stage1', diaClass: 'normal', classification: 'stage1' },
{ name: 'male child stage1 both', age: 10, sex: 'male', heightCm: 140, sys: 120, dia: 80, heightPctile: 59.5, heightZ: 0.24, sysPctile: 98, diaPctile: 97, sys50: 100, sys90: 112, sys95: 116, dia50: 62, dia90: 75, dia95: 78, sysClass: 'stage1', diaClass: 'stage1', classification: 'stage1' },
{ name: 'female child stage2 absolute', age: 11, sex: 'female', heightCm: 145, sys: 142, dia: 92, heightPctile: 57.13, heightZ: 0.18, sysPctile: 99, diaPctile: 99, sys50: 102, sys90: 114, sys95: 118, dia50: 61, dia90: 74, dia95: 77, sysClass: 'stage2', diaClass: 'stage2', classification: 'stage2' },
{ name: 'male teen elevated', age: 14, sex: 'male', heightCm: 165, sys: 124, dia: 76, heightPctile: 57.25, heightZ: 0.18, sysPctile: 88, diaPctile: 89, sys50: 111, sys90: 125, sys95: 129, dia50: 63, dia90: 77, dia95: 80, sysClass: 'elevated', diaClass: 'normal', classification: 'elevated' },
{ name: 'female teen stage1 diastolic', age: 16, sex: 'female', heightCm: 162, sys: 118, dia: 82, heightPctile: 46.75, heightZ: -0.08, sysPctile: 79, diaPctile: 95, sys50: 109, sys90: 123, sys95: 127, dia50: 66, dia90: 78, dia95: 82, sysClass: 'normal', diaClass: 'stage1', classification: 'stage1' },
{ name: 'male teen stage2 systolic', age: 17, sex: 'male', heightCm: 175, sys: 142, dia: 78, heightPctile: 48.69, heightZ: -0.03, sysPctile: 98, diaPctile: 83, sys50: 117, sys90: 131, sys95: 136, dia50: 68, dia90: 81, dia95: 85, sysClass: 'stage2', diaClass: 'normal', classification: 'stage2' }
];
cases.forEach((expected) => {
const actual = bp.calculateBPAssessment(data, expected.age, expected.sex, expected.heightCm, expected.sys, expected.dia);
assert.equal(Number(actual.height.percentile.toFixed(2)), expected.heightPctile, expected.name + ' height percentile');
assert.equal(Number(actual.height.z.toFixed(2)), expected.heightZ, expected.name + ' height z');
assert.equal(actual.systolic.percentile, expected.sysPctile, expected.name + ' systolic percentile');
assert.equal(actual.diastolic.percentile, expected.diaPctile, expected.name + ' diastolic percentile');
assert.equal(Number(actual.systolic.predicted[49].toFixed(0)), expected.sys50, expected.name + ' sys50');
assert.equal(Number(actual.systolic.predicted[89].toFixed(0)), expected.sys90, expected.name + ' sys90');
assert.equal(Number(actual.systolic.predicted[94].toFixed(0)), expected.sys95, expected.name + ' sys95');
assert.equal(Number(actual.diastolic.predicted[49].toFixed(0)), expected.dia50, expected.name + ' dia50');
assert.equal(Number(actual.diastolic.predicted[89].toFixed(0)), expected.dia90, expected.name + ' dia90');
assert.equal(Number(actual.diastolic.predicted[94].toFixed(0)), expected.dia95, expected.name + ' dia95');
assert.equal(actual.classification.sysClass, expected.sysClass, expected.name + ' sys class');
assert.equal(actual.classification.diaClass, expected.diaClass, expected.name + ' dia class');
assert.equal(actual.classification.classification, expected.classification, expected.name + ' classification');
});
});
test('dosing helpers preserve BSA and weight-based dose outputs', async () => {
const dosing = await import('../public/js/calculators/dosing.js');
assert.equal(Number(dosing.calculateBsa(20, 110).toFixed(3)), 0.782);
const uncapped = dosing.calculateWeightBasedDose(12, 15, 3, 0, 40);
assert.equal(uncapped.singleDose, 180);
assert.equal(uncapped.dailyDose, 540);
assert.equal(uncapped.volumeMl, 4.5);
assert.equal(uncapped.capped, false);
const capped = dosing.calculateWeightBasedDose(40, 15, 4, 500, 50);
assert.equal(capped.singleDose, 500);
assert.equal(capped.dailyDose, 2000);
assert.equal(capped.volumeMl, 10);
assert.equal(capped.capped, true);
});
test('GCS helper preserves severity thresholds', async () => {
const gcs = await import('../public/js/calculators/gcs.js');
assert.deepEqual(gcs.calculateGcs(1, 1, 1), { total: 3, severity: 'Severe (Coma)', color: '#ef4444', bg: '#fee2e2' });
assert.equal(gcs.calculateGcs(2, 3, 4).severity, 'Moderate');
assert.equal(gcs.calculateGcs(4, 5, 6).severity, 'Mild');
});