gestion_table_tournante_libre/src/interface/libelles.test.js
Mathieu Benoit 71076afe4d [ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.

--- FR ---

[ADD] application : horloge, traduction, mise en forme, nom affiché

Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.

Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00

107 lines
4.9 KiB
JavaScript

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// Chaque mode que pose un script npm (« --mode … » dans package.json) a son
// libellé de provenance, comme le mode test que pose Vitest ; un mode sans
// libellé s'affiche sous son propre nom plutôt que de laisser le bandeau muet
// sur la provenance.
//
// Les textes des écrans se partagent en tables, une par écran, sous
// libelles/ : chacune exporte TABLE, une Map de libellés, et libelles.js les
// assemble par ordre alphabétique des fichiers.
import assert from 'node:assert/strict';
import { readdirSync, readFileSync } from 'node:fs';
import { TABLES_APPLICATION } from '../application/libelles.js';
import { creerTraduction } from '../application/traduction.js';
import { describe, test } from '../../test/lanceur.js';
import { TABLES_INTERFACE, aUnLibelle, libelleProvenance, titreAvecVersion } from './libelles.js';
// Modes nommés par les scripts de package.json, triés, sans doublon.
function modesDesScripts() {
const paquet = JSON.parse(
readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
);
const modes = new Set();
for (const commande of Object.values(paquet.scripts)) {
for (const [, mode] of commande.matchAll(/--mode[= ](\S+)/g)) {
modes.add(mode);
}
}
return [...modes].sort();
}
describe('libellés', () => {
test('libelleProvenance nomme les quatre modes de construction', () => {
assert.equal(libelleProvenance('developpement'), 'développement');
assert.equal(libelleProvenance('livraison'), 'livraison');
assert.equal(libelleProvenance('documentation'), 'documentation');
assert.equal(libelleProvenance('test'), 'épreuve');
});
test('chaque mode que pose un script npm a un libellé de provenance', () => {
const modes = modesDesScripts();
assert.ok(modes.length > 0, 'aucun « --mode » dans les scripts de package.json');
assert.deepEqual(modes.filter((mode) => !aUnLibelle(mode)), []);
});
test('libelleProvenance rend un mode sans libellé sous son propre nom', () => {
assert.equal(libelleProvenance('production'), 'production');
assert.equal(libelleProvenance('constructor'), 'constructor');
assert.equal(aUnLibelle('constructor'), false);
});
test('titreAvecVersion joint le nom et la version par un tiret cadratin', () => {
assert.equal(titreAvecVersion('X'), 'Gestion table tournante Libre — X');
});
});
// Les modules de table de libelles/, épreuves exceptées, par ordre
// alphabétique, et ce que chacun exporte.
async function tablesDuDossier() {
const noms = readdirSync(new URL('./libelles/', import.meta.url))
.filter((nom) => nom.endsWith('.js') && !nom.endsWith('.test.js'))
.sort();
return Promise.all(noms.map(async (nom) => ({ table: nom.slice(0, -3), module: await import(`./libelles/${nom}`) })));
}
describe('libellés : les tables des écrans (§ 14.6)', () => {
test('chaque module de libelles/ exporte TABLE, une Map ; TABLES_INTERFACE les assemble, onze, par ordre alphabétique', async () => {
const tables = await tablesDuDossier();
assert.equal(tables.length, 11);
assert.deepEqual(tables.map(({ table }) => table), [
'accueil', 'communs', 'coquille', 'diagnostic', 'evenement', 'generation',
'import', 'participants', 'plan', 'propositions', 'tables',
]);
for (const { table, module } of tables) assert.ok(module.TABLE instanceof Map, table);
assert.equal(TABLES_INTERFACE.length, tables.length);
tables.forEach(({ table, module }, rang) => assert.equal(TABLES_INTERFACE[rang], module.TABLE, table));
});
test("les tables d'écran ne portent que des libellés, chacun préfixé du nom de sa table", async () => {
const tables = await tablesDuDossier();
let comptees = 0;
for (const { table, module } of tables) {
const forme = new RegExp(`^${table}(?:\\.[a-z][A-Za-z0-9]*)+$`);
for (const cle of module.TABLE.keys()) {
comptees += 1;
assert.match(cle, forme, table);
}
}
assert.ok(comptees > 0, 'aucune clé');
});
test("les tables de l'application et des écrans se réunissent sans clé partagée", () => {
assert.ok(TABLES_APPLICATION.length > 0 && TABLES_INTERFACE.length > 0);
const { cles } = creerTraduction([...TABLES_APPLICATION, ...TABLES_INTERFACE]);
assert.ok(cles().length > 0);
});
test("la coquille se lit en français dans coquille.js : son titre est celui de titreAvecVersion, ses provenances celles de libelleProvenance", () => {
const { t } = creerTraduction(TABLES_INTERFACE);
assert.equal(t('coquille.titre', { version: 'X' }), titreAvecVersion('X'));
assert.equal(t('coquille.nom'), 'Gestion table tournante Libre');
for (const mode of ['developpement', 'livraison', 'documentation', 'test']) {
assert.equal(t(`coquille.provenance.${mode}`), libelleProvenance(mode), mode);
}
});
});