- FIX #70 : generateForecastDoses garde !isActive — un traitement archivé à Posologie ne projette plus de créneaux ; l'horizon du chip Prévision (chart.js) ne considère que les Posologies ACTIVES. §6.bis inchangé : l'historique reste simulé, seul le FUTUR s'arrête. 3 tests miroirs. - Page Doses : routeGlyph (💊💉💧🩹🧴, miroir routeIcon) + encarts « prise de sang le même jour » 🧪↑/🧪↓ (labMarkersForDose, miroir) ; i18n FR/EN ; 10 tests doses-extras.test.js (⚠️ makeDose(ts, amount, overrides)). 194 tests + E2E verts (check.sh). WEB_VERSION alignée sur 1.12.0.
117 lines
4.8 KiB
JavaScript
117 lines
4.8 KiB
JavaScript
/**
|
|
* Tests des ajouts v1.12.0 à la page Doses (miroir de DosesExtrasTest.kt +
|
|
* ForecastDosesGuardTest.kt) :
|
|
* - FIX #70 : un traitement INACTIF ne projette PLUS de créneaux
|
|
* (generateForecastDoses) — l'historique reste simulé (§6.bis) ;
|
|
* - routeGlyph : un émoji PAR forme de prise (IM/SC partagent la seringue
|
|
* par design — différenciées par le texte de la ligne côté web) ;
|
|
* - labMarkersForDose : marqueurs « prise de sang le même jour » —
|
|
* avant/après la dose, jour calendaire LOCAL, frontière minuit.
|
|
*
|
|
* Déterministes (instants construits via Date locale, hors changement
|
|
* d'heure — même discipline que misc.test.js).
|
|
*/
|
|
|
|
import { test, describe } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { generateForecastDoses } from '../js/pk/pk-engine.js';
|
|
import { routeGlyph, treatmentsForDisplay } from '../js/data/models.js';
|
|
import { labMarkersForDose } from '../js/util/format.js';
|
|
import { makeTreatment, makeDose, DAY_MS, HOUR_MS } from './helpers.js';
|
|
|
|
const NOW = 1_790_000_000_000;
|
|
|
|
describe('forecast guard for inactive treatments (fix #70, v1.12.0)', () => {
|
|
test('un inactif à Posologie + historique ne projette plus rien', () => {
|
|
const archived = makeTreatment({ id: 1, isActive: false, forecastIntervalDays: 7 });
|
|
const doses = [
|
|
makeDose(NOW - 8 * DAY_MS, 5, { treatmentId: 1 }),
|
|
makeDose(NOW - DAY_MS, 5, { treatmentId: 1 }),
|
|
];
|
|
assert.equal(generateForecastDoses(archived, doses, NOW + 30 * DAY_MS, NOW).length, 0);
|
|
});
|
|
|
|
test('un actif jumeau projette toujours (comportement inchangé)', () => {
|
|
const active = makeTreatment({ id: 2, isActive: true, forecastIntervalDays: 7 });
|
|
const doses = [
|
|
makeDose(NOW - 8 * DAY_MS, 5, { treatmentId: 2 }),
|
|
makeDose(NOW - DAY_MS, 5, { treatmentId: 2 }),
|
|
];
|
|
const slots = generateForecastDoses(active, doses, NOW + 30 * DAY_MS, NOW);
|
|
assert.ok(slots.length > 0, 'un actif à Posologie projette');
|
|
assert.equal(slots[0].timestamp, NOW - DAY_MS + 7 * DAY_MS);
|
|
});
|
|
|
|
test('archiver un traitement actif arrête ses créneaux futurs', () => {
|
|
const treatment = makeTreatment({ id: 3, isActive: true, forecastIntervalDays: 7 });
|
|
const doses = [makeDose(NOW - DAY_MS, 5, { treatmentId: 3 })];
|
|
const before = generateForecastDoses(treatment, doses, NOW + 30 * DAY_MS, NOW);
|
|
const after = generateForecastDoses(
|
|
{ ...treatment, isActive: false }, doses, NOW + 30 * DAY_MS, NOW,
|
|
);
|
|
assert.ok(before.length > 0);
|
|
assert.equal(after.length, 0);
|
|
});
|
|
});
|
|
|
|
describe('route glyphs (v1.12.0)', () => {
|
|
test('un glyphe par forme (IM/SC partagent la seringue par design)', () => {
|
|
const oral = routeGlyph('ORAL');
|
|
const im = routeGlyph('INJECTION_IM');
|
|
const sc = routeGlyph('INJECTION_SUBCUT');
|
|
const gel = routeGlyph('TRANSDERMAL_GEL');
|
|
const patch = routeGlyph('TRANSDERMAL_PATCH');
|
|
const other = routeGlyph('OTHER');
|
|
assert.notEqual(oral, im);
|
|
assert.notEqual(oral, gel);
|
|
assert.notEqual(im, gel);
|
|
assert.notEqual(gel, patch);
|
|
assert.notEqual(patch, other);
|
|
assert.equal(im, sc, 'IM/SC : même seringue (texte de ligne distinct côté web)');
|
|
});
|
|
|
|
test('route inconnue/null → glyphe OTHER', () => {
|
|
assert.equal(routeGlyph(null), routeGlyph('OTHER'));
|
|
assert.equal(routeGlyph(undefined), routeGlyph('OTHER'));
|
|
});
|
|
});
|
|
|
|
describe('lab markers per dose (v1.12.0)', () => {
|
|
// Jour calendaire LOCAL de node (hors changement d'heure)
|
|
const noon = new Date(2026, 8, 27, 12, 0).getTime(); // 27 sept 2026 12:00 local
|
|
|
|
const lab = (atMs) => ({ marker: 'E2', value: 100, unit: 'pg/mL', timestamp: atMs });
|
|
|
|
test('labs avant ET après la dose le même jour → les deux marqueurs', () => {
|
|
const m = labMarkersForDose([lab(noon - HOUR_MS), lab(noon + HOUR_MS)], noon);
|
|
assert.equal(m.before, true);
|
|
assert.equal(m.after, true);
|
|
});
|
|
|
|
test('lab d\'un autre jour ne marque jamais', () => {
|
|
const m = labMarkersForDose([lab(noon - 24 * HOUR_MS)], noon);
|
|
assert.equal(m.before, false);
|
|
assert.equal(m.after, false);
|
|
});
|
|
|
|
test('frontière minuit : 23:50 la veille ≠ jour suivant', () => {
|
|
const lateEvening = new Date(2026, 8, 26, 23, 50).getTime();
|
|
const earlyMorning = new Date(2026, 8, 27, 0, 10).getTime();
|
|
const m = labMarkersForDose([lab(lateEvening)], earlyMorning);
|
|
assert.equal(m.before, false);
|
|
assert.equal(m.after, false);
|
|
});
|
|
|
|
test('lab exactement à l\'heure de la dose compte comme APRÈS (convention)', () => {
|
|
const m = labMarkersForDose([lab(noon)], noon);
|
|
assert.equal(m.before, false);
|
|
assert.equal(m.after, true);
|
|
});
|
|
|
|
test('aucun lab → aucun marqueur', () => {
|
|
const m = labMarkersForDose([], noon);
|
|
assert.equal(m.before, false);
|
|
assert.equal(m.after, false);
|
|
});
|
|
});
|