[ADD] event list: retained placement's chosen repeated meetings
The event list summed up the retained placement by its collisions alone; the chosen repeated meetings, the other figure the proposals table ranks on, was only visible after opening the event. Both now sit under one « Placement retenu » group header, read from a single measurement of the retained placement, with the same « — » cases and lazy loading. The column group span is tested, so assistive technology keeps both columns under that header. The user guide drops the stale redundancy target. Checked: node 5152, browser 1154, node-long 129; no capture changes. --- FR --- [ADD] liste des événements : rencontres répétées choisies du retenu La liste des événements résumait le retenu par ses seules collisions ; les rencontres répétées choisies, l'autre nombre que classe le tableau des propositions, ne se voyaient qu'en ouvrant l'événement. Les deux tiennent sous un même en-tête « Placement retenu », lues d'une seule mesure du retenu, avec les mêmes cas « — » et le même chargement différé. L'étendue du groupe de colonnes est éprouvée : les aides techniques gardent les deux colonnes sous cet en-tête. Le guide d'usage quitte la cible périmée. Vérifié : node 5152, navigateur 1154, node-long 129 ; aucune capture. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
4f9c550e44
commit
ffce294cec
12 changed files with 254 additions and 117 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -166,12 +166,13 @@
|
||||||
page de qualité.
|
page de qualité.
|
||||||
- Le guide d'usage montre la page de qualité, ses vues et ses cas limites,
|
- Le guide d'usage montre la page de qualité, ses vues et ses cas limites,
|
||||||
captures à l'appui.
|
captures à l'appui.
|
||||||
- Dans la liste des événements, la colonne du placement retenu montre ses
|
- Dans la liste des événements, le placement retenu montre ses « Collisions
|
||||||
« Collisions cumulées (paire et tour) », le même nombre que la colonne du
|
cumulées (paire et tour) » et ses « Rencontres répétées choisies (paires) »,
|
||||||
tableau des propositions, au lieu de « Diff somme d'appartenance ». Le réglage
|
les mêmes nombres que les colonnes du tableau des propositions, au lieu de
|
||||||
« Cible de la somme de redondance », qui ne servait qu'à cette colonne, quitte
|
« Diff somme d'appartenance ». Le réglage « Cible de la somme de redondance »,
|
||||||
la section des propositions : un fichier qui le porte s'ouvre sans message, et
|
qui ne servait qu'à cette ancienne colonne, quitte la section des
|
||||||
le perd au premier enregistrement.
|
propositions : un fichier qui le porte s'ouvre sans message, et le perd au
|
||||||
|
premier enregistrement.
|
||||||
- La section « Propositions » offre « Garder les 10 meilleures » : les dix
|
- La section « Propositions » offre « Garder les 10 meilleures » : les dix
|
||||||
premières du classement restent, et celle d'où vient le placement retenu
|
premières du classement restent, et celle d'où vient le placement retenu
|
||||||
aussi, les autres s'effacent. Le nombre se règle dans le champ voisin. Une
|
aussi, les autres s'effacent. Le nombre se règle dans le champ voisin. Une
|
||||||
|
|
|
||||||
|
|
@ -378,8 +378,9 @@ réservations imposent de ce que la recherche a choisi.
|
||||||
proposition alors que le retenu a été ajusté à la main détruit ces ajustements :
|
proposition alors que le retenu a été ajusté à la main détruit ces ajustements :
|
||||||
une fenêtre le dit avant.
|
une fenêtre le dit avant.
|
||||||
|
|
||||||
La liste des événements résume ensuite le retenu par l’écart de sa somme de
|
La liste des événements résume ensuite le retenu par ses collisions cumulées
|
||||||
redondance d’appartenance à la cible de la soirée.
|
(paire et tour) et ses rencontres répétées choisies (paires), les nombres du
|
||||||
|
tableau des propositions.
|
||||||
|
|
||||||
Retenir n’est pas obligatoire, mais tout ce qui s’imprime sans placement retenu
|
Retenir n’est pas obligatoire, mais tout ce qui s’imprime sans placement retenu
|
||||||
porte la mention BROUILLON (étape 12).
|
porte la mention BROUILLON (étape 12).
|
||||||
|
|
|
||||||
18
spec.md
18
spec.md
|
|
@ -201,7 +201,7 @@ du calcul qui a produit la proposition. Le logiciel sépare ce que le moteur a
|
||||||
décidé de ce que les réservations imposent (§ 5.4). Retenir fait passer le plan
|
décidé de ce que les réservations imposent (§ 5.4). Retenir fait passer le plan
|
||||||
à l'état *Retenu* (§ 9) ; les propositions non retenues restent disponibles. Le
|
à l'état *Retenu* (§ 9) ; les propositions non retenues restent disponibles. Le
|
||||||
retenu se résume ensuite dans la liste des événements, par ses collisions
|
retenu se résume ensuite dans la liste des événements, par ses collisions
|
||||||
cumulées (§ 5.4, § 8.1).
|
cumulées et ses rencontres répétées choisies (§ 5.4, § 8.1).
|
||||||
|
|
||||||
**11. Ajuster à la main.** *Facultative, reprenable.* Le même geste qu'à
|
**11. Ajuster à la main.** *Facultative, reprenable.* Le même geste qu'à
|
||||||
l'étape 7 déplace maintenant une personne réelle dans le placement retenu. **Le
|
l'étape 7 déplace maintenant une personne réelle dans le placement retenu. **Le
|
||||||
|
|
@ -1950,12 +1950,16 @@ pastille de l'état et son compte —, ou ne les groupe pas, au choix ; ses
|
||||||
en-têtes de colonnes se groupent en « Événement », « Placement retenu » et
|
en-têtes de colonnes se groupent en « Événement », « Placement retenu » et
|
||||||
« Meilleure proposition du classement », et le nom de chaque ligne en est
|
« Meilleure proposition du classement », et le nom de chaque ligne en est
|
||||||
l'en-tête, la date et le chemin en petite ligne dessous. Sous le tableau, des
|
l'en-tête, la date et le chemin en petite ligne dessous. Sous le tableau, des
|
||||||
cartes offrent la création, le dossier de travail et les démonstrations. Une
|
cartes offrent la création, le dossier de travail et les démonstrations. Deux
|
||||||
colonne résume en plus le **placement retenu** : « Collisions cumulées (paire et
|
colonnes résument en plus le **placement retenu**, sous l'en-tête du groupe qui
|
||||||
tour) », les collisions cumulées du retenu (§ 5.4) — le nombre que la colonne du
|
les couvre toutes deux : « Collisions cumulées (paire et tour) », les collisions
|
||||||
même intitulé du tableau des propositions porte sur la ligne du retenu. Elle se
|
cumulées du retenu, puis « Rencontres répétées choisies (paires) », les paires
|
||||||
lit dans la même mesure que le tableau, sans rien écrire ; un événement sans
|
réunies plus d'une fois que le placement a choisies, sans celles que les
|
||||||
retenu, ou dont le retenu est fautif ou en dérive (§ 8.9, § 9), y porte « — ».
|
réservations imposent (§ 5.4) — dans chacune, le nombre que la colonne du même
|
||||||
|
intitulé du tableau des propositions porte sur la ligne du retenu. Les deux se
|
||||||
|
lisent dans une seule mesure du retenu, celle du tableau, sans rien écrire ; un
|
||||||
|
événement sans retenu, ou dont le retenu est fautif ou en dérive (§ 8.9, § 9),
|
||||||
|
porte « — » dans l'une et l'autre.
|
||||||
Chaque ligne porte aussi la **« Meilleure proposition du classement »**, sous
|
Chaque ligne porte aussi la **« Meilleure proposition du classement »**, sous
|
||||||
les libellés du bloc que la section de la génération montre en tête :
|
les libellés du bloc que la section de la génération montre en tête :
|
||||||
l'identifiant de la proposition au rang 1 du classement (§ 5.7) — la même
|
l'identifiant de la proposition au rang 1 du classement (§ 5.7) — la même
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,10 @@
|
||||||
// dans ses mesures, à part des choisies.
|
// dans ses mesures, à part des choisies.
|
||||||
//
|
//
|
||||||
// resumeDuRetenu donne à la liste des événements les collisions cumulées
|
// resumeDuRetenu donne à la liste des événements les collisions cumulées
|
||||||
// du retenu, lues dans la même mesure que sa ligne du tableau, sans le
|
// et les rencontres répétées choisies du retenu, lues dans une seule
|
||||||
// diagnostic ni les plafonds : la liste en résume chaque événement du
|
// mesure, celle de sa ligne du tableau, sans le diagnostic ni les
|
||||||
// dossier. premiereDuClassement est la seule définition de la proposition
|
// plafonds : la liste en résume chaque événement du dossier.
|
||||||
|
// premiereDuClassement est la seule définition de la proposition
|
||||||
// au rang 1 : la section de la génération la lit dans sa table,
|
// au rang 1 : la section de la génération la lit dans sa table,
|
||||||
// resumeDuMeilleur en donne l'identifiant à la liste, sur la même table
|
// resumeDuMeilleur en donne l'identifiant à la liste, sur la même table
|
||||||
// d'évaluation, diagnostic compris — le premier critère du classement lit
|
// d'évaluation, diagnostic compris — le premier critère du classement lit
|
||||||
|
|
@ -217,17 +218,23 @@ export function evaluer(charge, { plafondsAPriori = 'calcul' } = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Le résumé du retenu que montre la liste des événements (§ 5.4, § 8.1) :
|
* Le résumé du retenu que montre la liste des événements (§ 5.4, § 8.1),
|
||||||
* collisionsCumulees, les occurrences (paire, tour) où deux personnes d'une
|
* lu dans un seul appel à mesurer, la mesure que la ligne du retenu
|
||||||
* même appartenance partagent une table, celles de mesurer — la valeur que
|
* d'evaluer porte :
|
||||||
* la ligne du retenu d'evaluer porte, et que la colonne « Collisions
|
* - collisionsCumulees, les occurrences (paire, tour) où deux personnes
|
||||||
* cumulées (paire et tour) » du tableau des propositions écrit. null sans
|
* d'une même appartenance partagent une table — la valeur que la colonne
|
||||||
* retenu, pour un retenu fautif ou en dérive, et quand la configuration ne
|
* « Collisions cumulées (paire et tour) » du tableau des propositions
|
||||||
* se normalise pas : rien ne s'y mesure, et la liste écrit « — ». Ne
|
* écrit ;
|
||||||
* modifie pas la charge.
|
* - rencontresRepetees, les paires réunies à plus d'un tour que le placement
|
||||||
|
* a choisies, sans celles que les réservations imposent
|
||||||
|
* (mesures.rencontresRepetees.choisies) — la valeur que la colonne
|
||||||
|
* « Rencontres répétées choisies (paires) » écrit.
|
||||||
|
* null sans retenu, pour un retenu fautif ou en dérive, et quand la
|
||||||
|
* configuration ne se normalise pas : rien ne s'y mesure, et la liste écrit
|
||||||
|
* « — » dans les deux colonnes. Ne modifie pas la charge.
|
||||||
*
|
*
|
||||||
* @param {import('../stockage/types.js').Charge} charge
|
* @param {import('../stockage/types.js').Charge} charge
|
||||||
* @returns {{collisionsCumulees: number}|null}
|
* @returns {{collisionsCumulees: number, rencontresRepetees: number}|null}
|
||||||
*/
|
*/
|
||||||
export function resumeDuRetenu(charge) {
|
export function resumeDuRetenu(charge) {
|
||||||
const { retenu } = charge;
|
const { retenu } = charge;
|
||||||
|
|
@ -239,8 +246,8 @@ export function resumeDuRetenu(charge) {
|
||||||
if (erreur instanceof ErreurConfiguration) return null;
|
if (erreur instanceof ErreurConfiguration) return null;
|
||||||
throw erreur;
|
throw erreur;
|
||||||
}
|
}
|
||||||
const { collisionsCumulees } = mesurer(instance, indexerPlan(instance, planDepuisFichier(retenu)));
|
const { collisionsCumulees, rencontresRepetees } = mesurer(instance, indexerPlan(instance, planDepuisFichier(retenu)));
|
||||||
return { collisionsCumulees };
|
return { collisionsCumulees, rencontresRepetees: rencontresRepetees.choisies };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -484,23 +484,52 @@ describe("évaluation : une configuration qui ne se tient pas", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('resumeDuRetenu : les collisions cumulées du retenu que lit la liste des événements (§ 5.4, § 8.1)', () => {
|
describe('resumeDuRetenu : les collisions cumulées et les rencontres répétées choisies du retenu que lit la liste des événements (§ 5.4, § 8.1)', () => {
|
||||||
// La petite démonstration, trois propositions générées, la deuxième retenue.
|
// La petite démonstration, trois propositions générées, la deuxième retenue.
|
||||||
function retenue() {
|
function retenue() {
|
||||||
const generees = generee(chargeDemo('petite-conflit'), { graine: 3, nombre: 3, arret: 300 });
|
const generees = generee(chargeDemo('petite-conflit'), { graine: 3, nombre: 3, arret: 300 });
|
||||||
return executer(generees, 'retenirProposition', { id: generees.propositions[1].id });
|
return executer(generees, 'retenirProposition', { id: generees.propositions[1].id });
|
||||||
}
|
}
|
||||||
|
|
||||||
test("les seules collisions cumulées (paire et tour), celles de la ligne du retenu d'evaluer et de mesurer", () => {
|
test("les collisions cumulées (paire et tour) et les rencontres répétées choisies, celles de la ligne du retenu d'evaluer et de mesurer, et rien d'autre", () => {
|
||||||
const charge = figer(retenue());
|
const charge = figer(retenue());
|
||||||
const resume = resumeDuRetenu(charge);
|
const resume = resumeDuRetenu(charge);
|
||||||
const { mesures } = evaluer(charge).retenu;
|
const { mesures } = evaluer(charge).retenu;
|
||||||
assert.ok(mesures.collisionsCumulees > 0, 'le retenu porte des collisions');
|
assert.ok(mesures.collisionsCumulees > 0, 'le retenu porte des collisions');
|
||||||
|
assert.ok(mesures.rencontresRepetees.choisies > 0, 'le retenu porte des rencontres répétées choisies');
|
||||||
assert.notEqual(mesures.collisionsCumulees, mesures.totalRedondance);
|
assert.notEqual(mesures.collisionsCumulees, mesures.totalRedondance);
|
||||||
assert.deepEqual(resume, { collisionsCumulees: mesures.collisionsCumulees });
|
assert.notEqual(mesures.rencontresRepetees.choisies, mesures.collisionsCumulees);
|
||||||
|
assert.deepEqual(resume, {
|
||||||
|
collisionsCumulees: mesures.collisionsCumulees,
|
||||||
|
rencontresRepetees: mesures.rencontresRepetees.choisies,
|
||||||
|
});
|
||||||
const instance = normaliser(configurationDe(charge));
|
const instance = normaliser(configurationDe(charge));
|
||||||
const attendues = mesurer(instance, indexerPlan(instance, planDepuisFichier(charge.retenu)));
|
const attendues = mesurer(instance, indexerPlan(instance, planDepuisFichier(charge.retenu)));
|
||||||
assert.equal(attendues.collisionsCumulees, mesures.collisionsCumulees);
|
assert.equal(attendues.collisionsCumulees, mesures.collisionsCumulees);
|
||||||
|
assert.deepEqual(attendues.rencontresRepetees, mesures.rencontresRepetees);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('les rencontres répétées du résumé sont les seules choisies : une paire réservée ensemble, imposée, n’y entre pas', () => {
|
||||||
|
const charge = structuredClone(chargeDemo('petite'));
|
||||||
|
charge.reservations = [1, 2].map((participant) => ({ participant, table: 1, siege: null, portee: 'tous', tour: null }));
|
||||||
|
const avec = generee(charge, { graine: 6, nombre: 3, arret: 500 });
|
||||||
|
for (const proposition of avec.propositions) {
|
||||||
|
const retenue = figer(executer(avec, 'retenirProposition', { id: proposition.id }));
|
||||||
|
const { mesures } = evaluer(retenue).retenu;
|
||||||
|
assert.equal(mesures.rencontresRepetees.imposees, 1);
|
||||||
|
assert.deepEqual(resumeDuRetenu(retenue), {
|
||||||
|
collisionsCumulees: mesures.collisionsCumulees,
|
||||||
|
rencontresRepetees: mesures.rencontresRepetees.choisies,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('les deux valeurs sortent d’une seule mesure du retenu : un seul appel à mesurer, dont le résumé lit les deux', () => {
|
||||||
|
const source = readFileSync(new URL('./evaluation.js', import.meta.url), 'utf8');
|
||||||
|
const debut = source.indexOf('export function resumeDuRetenu(');
|
||||||
|
assert.ok(debut >= 0);
|
||||||
|
const corps = source.slice(debut, source.indexOf('\n}\n', debut));
|
||||||
|
assert.equal(corps.match(/\bmesurer\(/g)?.length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('aucune cible : le résumé ne dépend pas d’un champ cibleRedondance resté dans les réglages, et le module n’en exporte plus', () => {
|
test('aucune cible : le résumé ne dépend pas d’un champ cibleRedondance resté dans les réglages, et le module n’en exporte plus', () => {
|
||||||
|
|
|
||||||
|
|
@ -639,7 +639,7 @@ describe('lister (§ 8.1, § 19.4)', () => {
|
||||||
return serialiser(modifier(charge), { revision: 3, produitVersion: V1 });
|
return serialiser(modifier(charge), { revision: 3, produitVersion: V1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
test('les mesures du retenu : ses seules collisions cumulées, celles de resumeDuRetenu ; null sans retenu ; rien d’écrit', async () => {
|
test('les mesures du retenu : ses collisions cumulées et ses rencontres répétées choisies, celles de resumeDuRetenu ; null sans retenu ; rien d’écrit', async () => {
|
||||||
const { fs, facade } = await deuxEvenements();
|
const { fs, facade } = await deuxEvenements();
|
||||||
fs.deposer('portable', `retenue${SUFFIXES.etat}`, texteRetenu());
|
fs.deposer('portable', `retenue${SUFFIXES.etat}`, texteRetenu());
|
||||||
const avant = ecritures(fs);
|
const avant = ecritures(fs);
|
||||||
|
|
@ -649,7 +649,11 @@ describe('lister (§ 8.1, § 19.4)', () => {
|
||||||
const charge = analyser(texteRetenu()).charge;
|
const charge = analyser(texteRetenu()).charge;
|
||||||
const { mesures } = evaluer(charge).retenu;
|
const { mesures } = evaluer(charge).retenu;
|
||||||
assert.ok(mesures.collisionsCumulees > 0, 'le retenu porte des collisions');
|
assert.ok(mesures.collisionsCumulees > 0, 'le retenu porte des collisions');
|
||||||
assert.deepEqual(par.retenue.mesuresDuRetenu, { collisionsCumulees: mesures.collisionsCumulees });
|
assert.ok(mesures.rencontresRepetees.choisies > 0, 'le retenu porte des rencontres répétées choisies');
|
||||||
|
assert.deepEqual(par.retenue.mesuresDuRetenu, {
|
||||||
|
collisionsCumulees: mesures.collisionsCumulees,
|
||||||
|
rencontresRepetees: mesures.rencontresRepetees.choisies,
|
||||||
|
});
|
||||||
assert.deepEqual(par.retenue.mesuresDuRetenu, resumeDuRetenu(charge));
|
assert.deepEqual(par.retenue.mesuresDuRetenu, resumeDuRetenu(charge));
|
||||||
assert.equal(par['soiree-a'].mesuresDuRetenu, null);
|
assert.equal(par['soiree-a'].mesuresDuRetenu, null);
|
||||||
assert.equal(par.petite.mesuresDuRetenu, null);
|
assert.equal(par.petite.mesuresDuRetenu, null);
|
||||||
|
|
|
||||||
|
|
@ -1238,10 +1238,12 @@ describe('les mesures du retenu dans la liste (§ 5.4, § 8.1)', () => {
|
||||||
return mesurer(instance, indexerPlan(instance, planDepuisFichier(charge.retenu)));
|
return mesurer(instance, indexerPlan(instance, planDepuisFichier(charge.retenu)));
|
||||||
}
|
}
|
||||||
|
|
||||||
test('un événement retenu : ses collisions cumulées (paire et tour), sous l’intitulé du tableau des propositions ; sans retenu : « — » ; rien d’écrit', async () => {
|
test('un retenu : ses collisions cumulées et ses rencontres répétées choisies, sous les intitulés du tableau des propositions ; sans retenu, « — »', async () => {
|
||||||
const charge = chargeRetenue();
|
const charge = chargeRetenue();
|
||||||
const mesures = mesuresDe(charge);
|
const mesures = mesuresDe(charge);
|
||||||
expect(mesures.collisionsCumulees).toBeGreaterThan(0);
|
expect(mesures.collisionsCumulees).toBeGreaterThan(0);
|
||||||
|
expect(mesures.rencontresRepetees.choisies).toBeGreaterThan(0);
|
||||||
|
expect(mesures.rencontresRepetees.choisies).not.toBe(mesures.collisionsCumulees);
|
||||||
expect(mesures.collisionsCumulees).not.toBe(Math.abs(1000 - mesures.totalRedondance));
|
expect(mesures.collisionsCumulees).not.toBe(Math.abs(1000 - mesures.totalRedondance));
|
||||||
const m = await monter({
|
const m = await monter({
|
||||||
fichiers: [fichier(`retenue${SUFFIXES.etat}`, etat(charge)), fichier(`club${SUFFIXES.etat}`, texteRenomme('Club des Merles'))],
|
fichiers: [fichier(`retenue${SUFFIXES.etat}`, etat(charge)), fichier(`club${SUFFIXES.etat}`, texteRenomme('Club des Merles'))],
|
||||||
|
|
@ -1252,27 +1254,39 @@ describe('les mesures du retenu dans la liste (§ 5.4, § 8.1)', () => {
|
||||||
const retenue = ligneDe(m, 'retenue');
|
const retenue = ligneDe(m, 'retenue');
|
||||||
expect(colonne(retenue, 'collisionsCumulees')).toBe(format.nombre(mesures.collisionsCumulees));
|
expect(colonne(retenue, 'collisionsCumulees')).toBe(format.nombre(mesures.collisionsCumulees));
|
||||||
expect(colonne(retenue, 'collisionsCumulees')).toBe(format.nombre(evaluer(charge).retenu.mesures.collisionsCumulees));
|
expect(colonne(retenue, 'collisionsCumulees')).toBe(format.nombre(evaluer(charge).retenu.mesures.collisionsCumulees));
|
||||||
|
expect(colonne(retenue, 'rencontresRepetees')).toBe(format.nombre(mesures.rencontresRepetees.choisies));
|
||||||
|
expect(colonne(retenue, 'rencontresRepetees')).toBe(format.nombre(evaluer(charge).retenu.mesures.rencontresRepetees.choisies));
|
||||||
const club = ligneDe(m, 'club');
|
const club = ligneDe(m, 'club');
|
||||||
expect(colonne(club, 'collisionsCumulees')).toBe(TIRET);
|
expect(colonne(club, 'collisionsCumulees')).toBe(TIRET);
|
||||||
// L'intitulé est un en-tête de colonne, une seule fois pour toute la
|
expect(colonne(club, 'rencontresRepetees')).toBe(TIRET);
|
||||||
// liste, au texte de la colonne du tableau des propositions ; plus rien
|
// Chaque intitulé est un en-tête de colonne, une seule fois pour toute la
|
||||||
// ne nomme la différence à la cible.
|
// liste, au texte de la colonne du tableau des propositions, dans l'ordre
|
||||||
expect(tous(m, '[data-terme="collisionsCumulees"]').length).toBe(1);
|
// des valeurs ; plus rien ne nomme la différence à la cible.
|
||||||
expect(terme(accueil(m), 'collisionsCumulees').closest('thead')).not.toBeNull();
|
for (const nom of ['collisionsCumulees', 'rencontresRepetees']) {
|
||||||
expect(terme(accueil(m), 'collisionsCumulees').textContent).toBe(t('propositions.colonne.collisionsCumulees'));
|
expect(tous(m, `[data-terme="${nom}"]`).length, nom).toBe(1);
|
||||||
|
expect(terme(accueil(m), nom).closest('thead'), nom).not.toBeNull();
|
||||||
|
expect(terme(accueil(m), nom).textContent, nom).toBe(t(`propositions.colonne.${nom}`));
|
||||||
|
expect(visible(retenue.querySelector(`[data-colonne="${nom}"]`)), nom).toBe(true);
|
||||||
|
}
|
||||||
|
expect([...retenue.querySelectorAll('[data-colonne]')].map((cellule) => cellule.dataset.colonne).slice(-2)).toEqual([
|
||||||
|
'collisionsCumulees',
|
||||||
|
'rencontresRepetees',
|
||||||
|
]);
|
||||||
expect(tous(m, '[data-colonne="diffRedondance"], [data-terme="diffRedondance"]')).toEqual([]);
|
expect(tous(m, '[data-colonne="diffRedondance"], [data-terme="diffRedondance"]')).toEqual([]);
|
||||||
expect(accueil(m).textContent).not.toContain("Diff somme d'appartenance");
|
expect(accueil(m).textContent).not.toContain("Diff somme d'appartenance");
|
||||||
expect(visible(retenue.querySelector('[data-colonne="collisionsCumulees"]'))).toBe(true);
|
|
||||||
expect(ecritures(m)).toBe(avant);
|
expect(ecritures(m)).toBe(avant);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('chaque intitulé s’explique : le survol puis le focus ouvrent son infobulle, Échap et la sortie la ferment', async () => {
|
test('chaque intitulé s’explique : le survol puis le focus ouvrent son infobulle, Échap et la sortie la ferment', async () => {
|
||||||
const m = await monter({ fichiers: [fichier(`retenue${SUFFIXES.etat}`, etat(chargeRetenue()))] });
|
const m = await monter({ fichiers: [fichier(`retenue${SUFFIXES.etat}`, etat(chargeRetenue()))] });
|
||||||
await vi.waitFor(() => expect(lignes(m).length).toBe(1));
|
await vi.waitFor(() => expect(lignes(m).length).toBe(1));
|
||||||
await eprouverTermes(accueil(m), [['collisionsCumulees', 'accueil.explication.collisionsCumulees']]);
|
await eprouverTermes(accueil(m), [
|
||||||
|
['collisionsCumulees', 'accueil.explication.collisionsCumulees'],
|
||||||
|
['rencontresRepetees', 'accueil.explication.rencontresRepetees'],
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('un état écrit avec la cible de redondance d’un code antérieur : la ligne se lit sans signalement, ses collisions comme sans elle ; l’ouvrir n’écrit rien', async () => {
|
test('un état écrit avec la cible de redondance d’un code antérieur : la ligne se lit sans signalement, ses mesures du retenu comme sans elle ; l’ouvrir n’écrit rien', async () => {
|
||||||
const charge = chargeRetenue();
|
const charge = chargeRetenue();
|
||||||
const lu = JSON.parse(etat(charge));
|
const lu = JSON.parse(etat(charge));
|
||||||
lu.charge.reglages.cibleRedondance = 25;
|
lu.charge.reglages.cibleRedondance = 25;
|
||||||
|
|
@ -1282,6 +1296,7 @@ describe('les mesures du retenu dans la liste (§ 5.4, § 8.1)', () => {
|
||||||
const ligne = ligneDe(m, 'retenue');
|
const ligne = ligneDe(m, 'retenue');
|
||||||
expect(ligne.querySelector('[data-signalement]')).toBeNull();
|
expect(ligne.querySelector('[data-signalement]')).toBeNull();
|
||||||
expect(colonne(ligne, 'collisionsCumulees')).toBe(format.nombre(mesuresDe(charge).collisionsCumulees));
|
expect(colonne(ligne, 'collisionsCumulees')).toBe(format.nombre(mesuresDe(charge).collisionsCumulees));
|
||||||
|
expect(colonne(ligne, 'rencontresRepetees')).toBe(format.nombre(mesuresDe(charge).rencontresRepetees.choisies));
|
||||||
const avant = ecritures(m);
|
const avant = ecritures(m);
|
||||||
await ouvrirLigne(m, 'retenue');
|
await ouvrirLigne(m, 'retenue');
|
||||||
await attendreEvenement(m);
|
await attendreEvenement(m);
|
||||||
|
|
@ -1418,8 +1433,15 @@ describe('la meilleure proposition du classement dans la liste (§ 5.4, § 5.7,
|
||||||
expect(tableau.parentElement.scrollWidth).toBeLessThanOrEqual(tableau.parentElement.clientWidth);
|
expect(tableau.parentElement.scrollWidth).toBeLessThanOrEqual(tableau.parentElement.clientWidth);
|
||||||
expect(tableau.getBoundingClientRect().right).toBeLessThanOrEqual(carte.getBoundingClientRect().right);
|
expect(tableau.getBoundingClientRect().right).toBeLessThanOrEqual(carte.getBoundingClientRect().right);
|
||||||
expect(carte.getBoundingClientRect().right).toBeLessThanOrEqual(innerWidth);
|
expect(carte.getBoundingClientRect().right).toBeLessThanOrEqual(innerWidth);
|
||||||
|
// Chaque intitulé de colonne tient dans sa cellule, ceux du placement
|
||||||
|
// retenu compris.
|
||||||
|
for (const entete of tous(m, '[data-ecran="accueil"] thead th')) {
|
||||||
|
expect(entete.scrollWidth, entete.textContent).toBeLessThanOrEqual(entete.clientWidth);
|
||||||
|
expect(entete.getBoundingClientRect().right).toBeLessThanOrEqual(carte.getBoundingClientRect().right);
|
||||||
|
}
|
||||||
for (const ligne of lignes(m)) {
|
for (const ligne of lignes(m)) {
|
||||||
expect(lu(ligne).id).toMatch(/\d/);
|
expect(lu(ligne).id).toMatch(/\d/);
|
||||||
|
for (const nom of ['collisionsCumulees', 'rencontresRepetees']) expect(colonne(ligne, nom), nom).toMatch(/\d/);
|
||||||
for (const cellule of ligne.cells) {
|
for (const cellule of ligne.cells) {
|
||||||
expect(cellule.scrollWidth, cellule.textContent).toBeLessThanOrEqual(cellule.clientWidth);
|
expect(cellule.scrollWidth, cellule.textContent).toBeLessThanOrEqual(cellule.clientWidth);
|
||||||
expect(cellule.getBoundingClientRect().right).toBeLessThanOrEqual(innerWidth);
|
expect(cellule.getBoundingClientRect().right).toBeLessThanOrEqual(innerWidth);
|
||||||
|
|
@ -1446,15 +1468,16 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
return appliquerCommande('retenirProposition', charge, { id: charge.propositions[1].id }).charge;
|
return appliquerCommande('retenirProposition', charge, { id: charge.propositions[1].id }).charge;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Les deux valeurs de résumé d'une ligne, dans l'ordre du rendu.
|
// Les trois valeurs de résumé d'une ligne, dans l'ordre du rendu : les
|
||||||
const CELLULES = ['[data-colonne="collisionsCumulees"]', '[data-meilleure-id]'];
|
// deux mesures du retenu, puis la meilleure proposition.
|
||||||
|
const CELLULES = ['[data-colonne="collisionsCumulees"]', '[data-colonne="rencontresRepetees"]', '[data-meilleure-id]'];
|
||||||
const cellules = (ligne) => CELLULES.map((selecteur) => ligne.querySelector(selecteur));
|
const cellules = (ligne) => CELLULES.map((selecteur) => ligne.querySelector(selecteur));
|
||||||
// Ce que la ligne d'une charge écrit, par la table d'évaluation sollicitée
|
// Ce que la ligne d'une charge écrit, par la table d'évaluation sollicitée
|
||||||
// une seconde fois (§ 19.1).
|
// une seconde fois (§ 19.1).
|
||||||
function attendues(charge) {
|
function attendues(charge) {
|
||||||
const table = evaluer(charge);
|
const table = evaluer(charge);
|
||||||
const [premiere] = table.propositions;
|
const [premiere] = table.propositions;
|
||||||
return [table.retenu.mesures.collisionsCumulees, premiere.id].map((n) => format.nombre(n));
|
return [table.retenu.mesures.collisionsCumulees, table.retenu.mesures.rencontresRepetees.choisies, premiere.id].map((n) => format.nombre(n));
|
||||||
}
|
}
|
||||||
// Chaque valeur de la ligne attend : la cellule se dit occupée, porte un
|
// Chaque valeur de la ligne attend : la cellule se dit occupée, porte un
|
||||||
// seul signe, muet pour l'accessibilité (aria-hidden, sans rôle ni nom),
|
// seul signe, muet pour l'accessibilité (aria-hidden, sans rôle ni nom),
|
||||||
|
|
@ -1500,12 +1523,12 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
expect(colonne(ligne, 'participants')).toBe(format.nombre(charges[ligne.dataset.evenement].participants.length));
|
expect(colonne(ligne, 'participants')).toBe(format.nombre(charges[ligne.dataset.evenement].participants.length));
|
||||||
enAttente(ligne);
|
enAttente(ligne);
|
||||||
}
|
}
|
||||||
expect(signes(m).length).toBe(4);
|
expect(signes(m).length).toBe(6);
|
||||||
const region = regionEtat(m);
|
const region = regionEtat(m);
|
||||||
expect(region.textContent).toBe(t('accueil.chargement'));
|
expect(region.textContent).toBe(t('accueil.chargement'));
|
||||||
for (let image = 0; image < 3; image += 1) await uneImage();
|
for (let image = 0; image < 3; image += 1) await uneImage();
|
||||||
flushSync();
|
flushSync();
|
||||||
expect(signes(m).length).toBe(4);
|
expect(signes(m).length).toBe(6);
|
||||||
m.horloge.avancer(0);
|
m.horloge.avancer(0);
|
||||||
await vi.waitFor(() => resolue(ligneDe(m, 'alpha'), attendues(charges.alpha)));
|
await vi.waitFor(() => resolue(ligneDe(m, 'alpha'), attendues(charges.alpha)));
|
||||||
enAttente(ligneDe(m, 'beta'));
|
enAttente(ligneDe(m, 'beta'));
|
||||||
|
|
@ -1523,7 +1546,7 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
test('démontée avant le premier pas, l’application arrête le flux des résumés : aucun état ne se relit ensuite', async () => {
|
test('démontée avant le premier pas, l’application arrête le flux des résumés : aucun état ne se relit ensuite', async () => {
|
||||||
const charges = { alpha: chargeResumee('evt-alpha'), beta: chargeResumee('evt-beta') };
|
const charges = { alpha: chargeResumee('evt-alpha'), beta: chargeResumee('evt-beta') };
|
||||||
const m = await monter({ fichiers: Object.entries(charges).map(([base, charge]) => fichier(`${base}${SUFFIXES.etat}`, etat(charge))) });
|
const m = await monter({ fichiers: Object.entries(charges).map(([base, charge]) => fichier(`${base}${SUFFIXES.etat}`, etat(charge))) });
|
||||||
expect(signes(m).length).toBe(4);
|
expect(signes(m).length).toBe(6);
|
||||||
const relus = [];
|
const relus = [];
|
||||||
const lireTexte = m.fs.lireTexte;
|
const lireTexte = m.fs.lireTexte;
|
||||||
m.fs.lireTexte = (racine, chemin) => {
|
m.fs.lireTexte = (racine, chemin) => {
|
||||||
|
|
@ -1540,7 +1563,7 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
expect(relus).toEqual([]);
|
expect(relus).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('un état qui ne se relit pas pendant le chargement : « — » dans ses deux valeurs, et l’autre ligne résolue', async () => {
|
test('un état qui ne se relit pas pendant le chargement : « — » dans ses trois valeurs, et l’autre ligne résolue', async () => {
|
||||||
const charges = { alpha: chargeResumee('evt-alpha'), beta: chargeResumee('evt-beta') };
|
const charges = { alpha: chargeResumee('evt-alpha'), beta: chargeResumee('evt-beta') };
|
||||||
const m = await monter({ fichiers: Object.entries(charges).map(([base, charge]) => fichier(`${base}${SUFFIXES.etat}`, etat(charge))) });
|
const m = await monter({ fichiers: Object.entries(charges).map(([base, charge]) => fichier(`${base}${SUFFIXES.etat}`, etat(charge))) });
|
||||||
const lireTexte = m.fs.lireTexte;
|
const lireTexte = m.fs.lireTexte;
|
||||||
|
|
@ -1549,7 +1572,7 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
? Promise.reject(new ErreurStockage('LECTURE', { chemin, cause: 'EIO' }))
|
? Promise.reject(new ErreurStockage('LECTURE', { chemin, cause: 'EIO' }))
|
||||||
: lireTexte.call(m.fs, racine, chemin);
|
: lireTexte.call(m.fs, racine, chemin);
|
||||||
await chargerResumes(m);
|
await chargerResumes(m);
|
||||||
resolue(ligneDe(m, 'alpha'), [TIRET, TIRET]);
|
resolue(ligneDe(m, 'alpha'), [TIRET, TIRET, TIRET]);
|
||||||
resolue(ligneDe(m, 'beta'), attendues(charges.beta));
|
resolue(ligneDe(m, 'beta'), attendues(charges.beta));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1582,8 +1605,10 @@ describe('le chargement des résumés de la liste (§ 8.1, § 14.5, § 19.2)', (
|
||||||
|
|
||||||
test('le signe se voit : il tient 3:1 sur ce qui l’entoure dans les deux thèmes, sans atténuation', async () => {
|
test('le signe se voit : il tient 3:1 sur ce qui l’entoure dans les deux thèmes, sans atténuation', async () => {
|
||||||
const m = await monter({ fichiers: [fichier(`alpha${SUFFIXES.etat}`, etat(chargeResumee('evt-alpha')))] });
|
const m = await monter({ fichiers: [fichier(`alpha${SUFFIXES.etat}`, etat(chargeResumee('evt-alpha')))] });
|
||||||
|
// Une ligne qui attend : un signe par valeur de résumé, un tracé par
|
||||||
|
// signe.
|
||||||
const traces = tous(m, '[data-chargement] path');
|
const traces = tous(m, '[data-chargement] path');
|
||||||
expect(traces.length).toBe(2);
|
expect(traces.length).toBe(3);
|
||||||
for (const theme of ['clair', 'sombre']) {
|
for (const theme of ['clair', 'sombre']) {
|
||||||
await sousTheme(theme, async () => {
|
await sousTheme(theme, async () => {
|
||||||
for (const trace of traces) {
|
for (const trace of traces) {
|
||||||
|
|
@ -1794,7 +1819,17 @@ describe('la liste en tableau : en-tête, tuiles, groupes et cartes (§ 8.1, §
|
||||||
await chargerResumes(m);
|
await chargerResumes(m);
|
||||||
const entetes = tous(m, '[data-ecran="accueil"] thead th[scope="colgroup"]');
|
const entetes = tous(m, '[data-ecran="accueil"] thead th[scope="colgroup"]');
|
||||||
expect(entetes.map(texte)).toEqual([t('accueil.entete.evenement'), t('accueil.entete.retenu'), t('generation.meilleure.titre')]);
|
expect(entetes.map(texte)).toEqual([t('accueil.entete.evenement'), t('accueil.entete.retenu'), t('generation.meilleure.titre')]);
|
||||||
expect(entetes.map((th) => th.colSpan)).toEqual([4, 1, 1]);
|
expect(entetes.map((th) => th.colSpan)).toEqual([4, 2, 1]);
|
||||||
|
// Les groupes de colonnes, que lisent les techniques d'assistance pour
|
||||||
|
// rattacher une cellule à son en-tête de groupe : un par en-tête, chacun
|
||||||
|
// de la largeur de son en-tête, plus la colonne « Ouvrir », et leur
|
||||||
|
// somme fait une ligne entière.
|
||||||
|
const table = entetes[0].closest('table');
|
||||||
|
const groupesDeColonnes = [...table.querySelectorAll(':scope > colgroup')].map((groupe) => groupe.span);
|
||||||
|
expect(groupesDeColonnes).toEqual([4, 2, 1, 1]);
|
||||||
|
expect(groupesDeColonnes.slice(0, 3)).toEqual(entetes.map((th) => th.colSpan));
|
||||||
|
const largeurDeLigne = [...ligneDe(m, 'gala').cells].reduce((somme, cellule) => somme + cellule.colSpan, 0);
|
||||||
|
expect(groupesDeColonnes.reduce((somme, span) => somme + span, 0)).toBe(largeurDeLigne);
|
||||||
expect(fond(entetes[1])).toEqual(jeton('--couleur-etat-retenu-fond'));
|
expect(fond(entetes[1])).toEqual(jeton('--couleur-etat-retenu-fond'));
|
||||||
expect(fond(entetes[2])).toEqual(jeton('--couleur-etat-propose-fond'));
|
expect(fond(entetes[2])).toEqual(jeton('--couleur-etat-propose-fond'));
|
||||||
// Le groupe porte le signe de son état, de la marque de l'état.
|
// Le groupe porte le signe de son état, de la marque de l'état.
|
||||||
|
|
@ -1807,7 +1842,11 @@ describe('la liste en tableau : en-tête, tuiles, groupes et cartes (§ 8.1, §
|
||||||
};
|
};
|
||||||
const ligne = ligneDe(m, 'gala');
|
const ligne = ligneDe(m, 'gala');
|
||||||
for (const nom of ['nom', 'etat', 'participants', 'modification']) expect(dans2(ligne.querySelector(`[data-colonne="${nom}"]`), entetes[0]), nom).toBe(true);
|
for (const nom of ['nom', 'etat', 'participants', 'modification']) expect(dans2(ligne.querySelector(`[data-colonne="${nom}"]`), entetes[0]), nom).toBe(true);
|
||||||
expect(dans2(ligne.querySelector('[data-colonne="collisionsCumulees"]'), entetes[1])).toBe(true);
|
for (const nom of ['collisionsCumulees', 'rencontresRepetees']) expect(dans2(ligne.querySelector(`[data-colonne="${nom}"]`), entetes[1]), nom).toBe(true);
|
||||||
|
// Le groupe du retenu couvre ses deux colonnes, d'un bord à l'autre.
|
||||||
|
const bornes = ['collisionsCumulees', 'rencontresRepetees'].map((nom) => ligne.querySelector(`[data-colonne="${nom}"]`).getBoundingClientRect());
|
||||||
|
expect(Math.abs(bornes[0].left - entetes[1].getBoundingClientRect().left)).toBeLessThanOrEqual(0.5);
|
||||||
|
expect(Math.abs(bornes[1].right - entetes[1].getBoundingClientRect().right)).toBeLessThanOrEqual(0.5);
|
||||||
expect(dans2(ligne.querySelector('[data-meilleure-id]'), entetes[2])).toBe(true);
|
expect(dans2(ligne.querySelector('[data-meilleure-id]'), entetes[2])).toBe(true);
|
||||||
// Le nom est l'en-tête de sa ligne ; la date et le chemin le suivent,
|
// Le nom est l'en-tête de sa ligne ; la date et le chemin le suivent,
|
||||||
// dessous, en plus petit.
|
// dessous, en plus petit.
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@
|
||||||
//
|
//
|
||||||
// Les en-têtes de colonnes sont groupés : l'événement — nom, état,
|
// Les en-têtes de colonnes sont groupés : l'événement — nom, état,
|
||||||
// participants, dernière modification —, le placement retenu, à la teinte
|
// participants, dernière modification —, le placement retenu, à la teinte
|
||||||
// de Retenu — ses collisions cumulées (COLONNES_DU_RETENU) —, la
|
// de Retenu — ses collisions cumulées et ses rencontres répétées choisies,
|
||||||
// meilleure proposition du classement, à la teinte de Proposé, sous le
|
// une colonne chacune (COLONNES_DU_RETENU), que l'en-tête du groupe
|
||||||
// titre et le libellé du bloc de la génération (MEILLEURE) — son
|
// couvre —, la meilleure proposition du classement, à la teinte de
|
||||||
// identifiant —, puis les actions. Chaque valeur est dans son propre
|
// Proposé, sous le titre et le libellé du bloc de la génération
|
||||||
// nœud : data-colonne, data-meilleure-id, « — » sans valeur. Le nom est
|
// (MEILLEURE) — son identifiant —, puis les actions. Chaque valeur est
|
||||||
// l'en-tête de sa ligne ; la date et le chemin le suivent en petite
|
// dans son propre nœud : data-colonne, data-meilleure-id, « — » sans
|
||||||
// ligne, chacun précédé de son intitulé, que
|
// valeur. Le nom est l'en-tête de sa ligne ; la date et le chemin le
|
||||||
|
// suivent en petite ligne, chacun précédé de son intitulé, que
|
||||||
// lisent les technologies d'assistance et que l'œil ne voit pas, puis ses
|
// lisent les technologies d'assistance et que l'œil ne voit pas, puis ses
|
||||||
// signalements traduits — les bases qu'un doublon nomme suivent son
|
// signalements traduits — les bases qu'un doublon nomme suivent son
|
||||||
// texte, chacune dans son propre nœud (data-doublon). L'état est une
|
// texte, chacune dans son propre nœud (data-doublon). L'état est une
|
||||||
|
|
@ -33,7 +34,7 @@
|
||||||
//
|
//
|
||||||
// lignes : LigneListe[] de la façade ; resumes : les résumés reçus de
|
// lignes : LigneListe[] de la façade ; resumes : les résumés reçus de
|
||||||
// facade.resumer, par base — une ligne sans le sien attend, et chacune de
|
// facade.resumer, par base — une ligne sans le sien attend, et chacune de
|
||||||
// ses deux valeurs de résumé porte le signe du chargement (Chargement),
|
// ses trois valeurs de résumé porte le signe du chargement (Chargement),
|
||||||
// décoratif, dans une cellule qui se dit occupée (aria-busy), jusqu'à ce
|
// décoratif, dans une cellule qui se dit occupée (aria-busy), jusqu'à ce
|
||||||
// qu'il arrive. La région d'état qui suit le tableau
|
// qu'il arrive. La région d'état qui suit le tableau
|
||||||
// (data-chargement-liste) existe tant que la liste a des lignes, et écrit
|
// (data-chargement-liste) existe tant que la liste a des lignes, et écrit
|
||||||
|
|
@ -44,7 +45,7 @@
|
||||||
// du dernier échec, ou null ; apresOperation() : à appeler quand une
|
// du dernier échec, ou null ; apresOperation() : à appeler quand une
|
||||||
// opération a changé le dossier.
|
// opération a changé le dossier.
|
||||||
//
|
//
|
||||||
// L'intitulé de la mesure du retenu et le titre de la meilleure
|
// L'intitulé de chaque mesure du retenu et le titre de la meilleure
|
||||||
// proposition s'expliquent dans l'infobulle de la liste, une seule
|
// proposition s'expliquent dans l'infobulle de la liste, une seule
|
||||||
// (Infobulle, primitive) : chaque terme (data-terme, que pose le fragment termeExplique), un en-tête de colonne,
|
// (Infobulle, primitive) : chaque terme (data-terme, que pose le fragment termeExplique), un en-tête de colonne,
|
||||||
// se focalise au clavier, son survol ou son focus ouvre l'explication, le
|
// se focalise au clavier, son survol ou son focus ouvre l'explication, le
|
||||||
|
|
@ -72,9 +73,9 @@
|
||||||
const enChargement = $derived(lignes.some((ligne) => ligneAffichee(ligne, format, resumes.get(ligne.base)).enAttente));
|
const enChargement = $derived(lignes.some((ligne) => ligneAffichee(ligne, format, resumes.get(ligne.base)).enAttente));
|
||||||
const groupes = $derived(groupesDeLaListe(lignes, vue.filtre, vue.groupement));
|
const groupes = $derived(groupesDeLaListe(lignes, vue.filtre, vue.groupement));
|
||||||
// Le nombre de colonnes du tableau, que l'intitulé d'un groupe couvre :
|
// Le nombre de colonnes du tableau, que l'intitulé d'un groupe couvre :
|
||||||
// les quatre de l'événement, celle du retenu, celle de la meilleure
|
// les quatre de l'événement, les deux du retenu, celle de la meilleure
|
||||||
// proposition, les actions.
|
// proposition, les actions.
|
||||||
const COLONNES = 7;
|
const COLONNES = 8;
|
||||||
|
|
||||||
// Le terme expliqué : {ancre, terme, cle, details} — son élément, son nom
|
// Le terme expliqué : {ancre, terme, cle, details} — son élément, son nom
|
||||||
// et la clé de son explication — ; null sans infobulle.
|
// et la clé de son explication — ; null sans infobulle.
|
||||||
|
|
@ -136,13 +137,13 @@
|
||||||
<div class="defilement">
|
<div class="defilement">
|
||||||
<table>
|
<table>
|
||||||
<colgroup span="4"></colgroup>
|
<colgroup span="4"></colgroup>
|
||||||
<colgroup></colgroup>
|
<colgroup span={COLONNES_DU_RETENU.length}></colgroup>
|
||||||
<colgroup></colgroup>
|
<colgroup></colgroup>
|
||||||
<colgroup></colgroup>
|
<colgroup></colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="colgroup" colspan="4" class="groupe">{t('accueil.entete.evenement')}</th>
|
<th scope="colgroup" colspan="4" class="groupe">{t('accueil.entete.evenement')}</th>
|
||||||
<th scope="colgroup" class="groupe retenu"
|
<th scope="colgroup" colspan={COLONNES_DU_RETENU.length} class="groupe retenu"
|
||||||
><span class="entete-groupe"
|
><span class="entete-groupe"
|
||||||
><span class="signe"><Icone nom="coche" taille={14} /></span><span>{t('accueil.entete.retenu')}</span></span
|
><span class="signe"><Icone nom="coche" taille={14} /></span><span>{t('accueil.entete.retenu')}</span></span
|
||||||
></th
|
></th
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
// de la liste des événements affichent, sans arithmétique ni texte. Une
|
// de la liste des événements affichent, sans arithmétique ni texte. Une
|
||||||
// ligne de la liste (LigneListe de la façade, evenements.js) devient ses six
|
// ligne de la liste (LigneListe de la façade, evenements.js) devient ses six
|
||||||
// informations et ses signalements, mis en forme, puis, son résumé reçu, les
|
// informations et ses signalements, mis en forme, puis, son résumé reçu, les
|
||||||
// collisions cumulées de son retenu et l'identifiant de la meilleure
|
// collisions cumulées et les rencontres répétées choisies de son retenu et
|
||||||
// proposition de son classement ; les lignes de la liste, les tuiles de ses
|
// l'identifiant de la meilleure proposition de son classement ; les lignes
|
||||||
// filtres par état du plan, le résumé de son en-tête et les groupes de son
|
// de la liste, les tuiles de ses filtres par état du plan, le résumé de son
|
||||||
// tableau ; une question du dossier de travail, ses réponses ; un état
|
// en-tête et les groupes de son tableau ; une question du dossier de
|
||||||
// illisible, ses secours ; un refus de la façade, le refus affiché sur la
|
// travail, ses réponses ; un état illisible, ses secours ; un refus de la
|
||||||
// liste. Chaque clé rendue est écrite ici en entier : aucun composant ne
|
// façade, le refus affiché sur la liste. Chaque clé rendue est écrite ici
|
||||||
// compose une clé.
|
// en entier : aucun composant ne compose une clé.
|
||||||
|
|
||||||
// Les états du plan (§ 9) et la clé de leur nom, dans l'ordre du § 9.
|
// Les états du plan (§ 9) et la clé de leur nom, dans l'ordre du § 9.
|
||||||
const CLES_ETATS = new Map([
|
const CLES_ETATS = new Map([
|
||||||
|
|
@ -25,12 +25,15 @@ const CLES_ETATS = new Map([
|
||||||
export const ETATS = Object.freeze([...CLES_ETATS.keys()]);
|
export const ETATS = Object.freeze([...CLES_ETATS.keys()]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* La mesure du placement retenu qu'une ligne montre (§ 5.4, § 8.1) : ses
|
* Les mesures du placement retenu qu'une ligne montre (§ 5.4, § 8.1), dans
|
||||||
* collisions cumulées, { colonne, cle, explication } — le nom de sa valeur
|
* l'ordre de leurs colonnes : ses collisions cumulées, puis ses rencontres
|
||||||
* dans ligneAffichee et son data-colonne, la clé de son intitulé, celle de
|
* répétées choisies. Chacune est { colonne, cle, explication } — le nom de
|
||||||
* la colonne du tableau des propositions qui porte la même mesure, et celle
|
* sa valeur dans ligneAffichee et dans le résumé du retenu, qui est aussi son
|
||||||
* de son explication, propre à la liste, que l'infobulle de l'intitulé
|
* data-colonne et son data-terme, la clé de son intitulé, celle de la
|
||||||
* montre.
|
* colonne du tableau des propositions qui porte la même mesure, et celle de
|
||||||
|
* son explication, propre à la liste, que l'infobulle de l'intitulé montre.
|
||||||
|
* L'en-tête du groupe « Placement retenu » couvre autant de colonnes que la
|
||||||
|
* liste en compte.
|
||||||
*/
|
*/
|
||||||
export const COLONNES_DU_RETENU = Object.freeze([
|
export const COLONNES_DU_RETENU = Object.freeze([
|
||||||
Object.freeze({
|
Object.freeze({
|
||||||
|
|
@ -38,6 +41,11 @@ export const COLONNES_DU_RETENU = Object.freeze([
|
||||||
cle: 'propositions.colonne.collisionsCumulees',
|
cle: 'propositions.colonne.collisionsCumulees',
|
||||||
explication: 'accueil.explication.collisionsCumulees',
|
explication: 'accueil.explication.collisionsCumulees',
|
||||||
}),
|
}),
|
||||||
|
Object.freeze({
|
||||||
|
colonne: 'rencontresRepetees',
|
||||||
|
cle: 'propositions.colonne.rencontresRepetees',
|
||||||
|
explication: 'accueil.explication.rencontresRepetees',
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -130,8 +138,9 @@ export function signalementsDe(ligne) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Les six informations d'une ligne (§ 8.1), les collisions cumulées de son
|
* Les six informations d'une ligne (§ 8.1), les collisions cumulées et les
|
||||||
* retenu et l'identifiant de la meilleure proposition de son classement,
|
* rencontres répétées choisies de son retenu et l'identifiant de la
|
||||||
|
* meilleure proposition de son classement,
|
||||||
* mis en forme, et ses signalements. nom : celui inscrit dans le fichier, ou la base quand l'état
|
* mis en forme, et ses signalements. nom : celui inscrit dans le fichier, ou la base quand l'état
|
||||||
* ne se lit pas ; date, participants et dernière modification par format,
|
* ne se lit pas ; date, participants et dernière modification par format,
|
||||||
* la valeur absente quand ils manquent — la dernière modification est
|
* la valeur absente quand ils manquent — la dernière modification est
|
||||||
|
|
@ -140,21 +149,23 @@ export function signalementsDe(ligne) {
|
||||||
* clé de son nom, que le composant traduit ; chemin : tel que la façade
|
* clé de son nom, que le composant traduit ; chemin : tel que la façade
|
||||||
* l'affiche, en entier.
|
* l'affiche, en entier.
|
||||||
*
|
*
|
||||||
* Les deux valeurs de résumé lisent resume, ce que facade.resumer a rendu
|
* Les trois valeurs de résumé lisent resume, ce que facade.resumer a rendu
|
||||||
* pour la ligne : collisionsCumulees, celles de mesuresDuRetenu, par format,
|
* pour la ligne : collisionsCumulees et rencontresRepetees, celles de
|
||||||
* la valeur absente sans retenu mesurable — elles se lisent telles quelles,
|
* mesuresDuRetenu, par format, la valeur absente sans retenu mesurable —
|
||||||
* jamais refaites ; meilleure.id, l'identifiant de meilleureDuClassement, par
|
* elles se lisent telles quelles, jamais refaites ; meilleure.id,
|
||||||
* format, la valeur absente sans proposition classée. Sans resume, la ligne
|
* l'identifiant de meilleureDuClassement, par format, la valeur absente sans
|
||||||
* est enAttente : son résumé se calcule encore, et les deux valeurs valent la
|
* proposition classée. Sans resume, la ligne est enAttente : son résumé se
|
||||||
* valeur absente, que le composant remplace par le signe du chargement.
|
* calcule encore, et les trois valeurs valent la valeur absente, que le
|
||||||
|
* composant remplace par le signe du chargement.
|
||||||
*
|
*
|
||||||
* @param {Object} ligne LigneListe (evenements.js)
|
* @param {Object} ligne LigneListe (evenements.js)
|
||||||
* @param {ReturnType<import('../../application/format.js').creerFormat>} format
|
* @param {ReturnType<import('../../application/format.js').creerFormat>} format
|
||||||
* @param {{mesuresDuRetenu: {collisionsCumulees: number}|null,
|
* @param {{mesuresDuRetenu: {collisionsCumulees: number, rencontresRepetees: number}|null,
|
||||||
* meilleureDuClassement: {id: number}|null}} [resume]
|
* meilleureDuClassement: {id: number}|null}} [resume]
|
||||||
* @returns {{nom: string, date: string, etat: string, participants: string,
|
* @returns {{nom: string, date: string, etat: string, participants: string,
|
||||||
* modification: string, chemin: string, enAttente: boolean,
|
* modification: string, chemin: string, enAttente: boolean,
|
||||||
* collisionsCumulees: string, meilleure: {id: string},
|
* collisionsCumulees: string, rencontresRepetees: string,
|
||||||
|
* meilleure: {id: string},
|
||||||
* signalements: Array<{cle: string, details: Object, noms: string[]}>}}
|
* signalements: Array<{cle: string, details: Object, noms: string[]}>}}
|
||||||
*/
|
*/
|
||||||
export function ligneAffichee(ligne, format, resume) {
|
export function ligneAffichee(ligne, format, resume) {
|
||||||
|
|
@ -167,6 +178,7 @@ export function ligneAffichee(ligne, format, resume) {
|
||||||
chemin: ligne.chemin,
|
chemin: ligne.chemin,
|
||||||
enAttente: resume === undefined,
|
enAttente: resume === undefined,
|
||||||
collisionsCumulees: format.nombre(resume?.mesuresDuRetenu?.collisionsCumulees ?? null),
|
collisionsCumulees: format.nombre(resume?.mesuresDuRetenu?.collisionsCumulees ?? null),
|
||||||
|
rencontresRepetees: format.nombre(resume?.mesuresDuRetenu?.rencontresRepetees ?? null),
|
||||||
meilleure: { id: format.nombre(resume?.meilleureDuClassement?.id ?? null) },
|
meilleure: { id: format.nombre(resume?.meilleureDuClassement?.id ?? null) },
|
||||||
signalements: signalementsDe(ligne),
|
signalements: signalementsDe(ligne),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ describe('ligneAffichee : les six informations (§ 8.1)', () => {
|
||||||
'modification',
|
'modification',
|
||||||
'nom',
|
'nom',
|
||||||
'participants',
|
'participants',
|
||||||
|
'rencontresRepetees',
|
||||||
'signalements',
|
'signalements',
|
||||||
]);
|
]);
|
||||||
assert.equal(affichee.nom, 'Bal des Grisailles');
|
assert.equal(affichee.nom, 'Bal des Grisailles');
|
||||||
|
|
@ -335,59 +336,85 @@ describe('saisieDe : la saisie d’un nom pour copier ou renommer (§ 8.7)', ()
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ligneAffichee : les mesures du retenu (§ 5.4, § 8.1)', () => {
|
describe('ligneAffichee : les mesures du retenu (§ 5.4, § 8.1)', () => {
|
||||||
test('les collisions cumulées du retenu, celles de la façade, mises en forme, sans calcul ici', () => {
|
test('les collisions cumulées et les rencontres répétées choisies du retenu, celles de la façade, mises en forme chacune à sa place, sans calcul ici', () => {
|
||||||
for (const collisionsCumulees of [500, 7, 0]) {
|
for (const [collisionsCumulees, rencontresRepetees] of [
|
||||||
const affichee = ligneAffichee(ligne(), format, { ...AUCUN, mesuresDuRetenu: { collisionsCumulees } });
|
[500, 3],
|
||||||
|
[7, 12],
|
||||||
|
[0, 0],
|
||||||
|
]) {
|
||||||
|
const affichee = ligneAffichee(ligne(), format, { ...AUCUN, mesuresDuRetenu: { collisionsCumulees, rencontresRepetees } });
|
||||||
assert.equal(affichee.collisionsCumulees, format.nombre(collisionsCumulees), String(collisionsCumulees));
|
assert.equal(affichee.collisionsCumulees, format.nombre(collisionsCumulees), String(collisionsCumulees));
|
||||||
|
assert.equal(affichee.rencontresRepetees, format.nombre(rencontresRepetees), String(rencontresRepetees));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sans retenu mesurable, et pour une ligne illisible, les collisions valent « — »', () => {
|
test('sans retenu mesurable, et pour une ligne illisible, les collisions et les rencontres répétées valent « — »', () => {
|
||||||
for (const champs of [{}, { nom: null, etat: null, participants: null, illisible: { raison: 'VIDE', chemin: null } }]) {
|
for (const champs of [{}, { nom: null, etat: null, participants: null, illisible: { raison: 'VIDE', chemin: null } }]) {
|
||||||
const affichee = ligneAffichee(ligne(champs), format, AUCUN);
|
const affichee = ligneAffichee(ligne(champs), format, AUCUN);
|
||||||
assert.equal(affichee.collisionsCumulees, VIDE);
|
assert.equal(affichee.collisionsCumulees, VIDE);
|
||||||
|
assert.equal(affichee.rencontresRepetees, VIDE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('COLONNES_DU_RETENU : les seules collisions cumulées, sous l’intitulé de la colonne du tableau des propositions, avec l’explication propre à la liste ; plus rien de la différence à la cible', () => {
|
test('COLONNES_DU_RETENU : les collisions cumulées puis les rencontres répétées choisies, chacune sous l’intitulé de sa colonne du tableau des propositions, avec son explication propre à la liste ; plus rien de la différence à la cible', () => {
|
||||||
assert.deepEqual(COLONNES_DU_RETENU, [
|
assert.deepEqual(COLONNES_DU_RETENU, [
|
||||||
{
|
{
|
||||||
colonne: 'collisionsCumulees',
|
colonne: 'collisionsCumulees',
|
||||||
cle: 'propositions.colonne.collisionsCumulees',
|
cle: 'propositions.colonne.collisionsCumulees',
|
||||||
explication: 'accueil.explication.collisionsCumulees',
|
explication: 'accueil.explication.collisionsCumulees',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
colonne: 'rencontresRepetees',
|
||||||
|
cle: 'propositions.colonne.rencontresRepetees',
|
||||||
|
explication: 'accueil.explication.rencontresRepetees',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
assert.ok(Object.isFrozen(COLONNES_DU_RETENU));
|
assert.ok(Object.isFrozen(COLONNES_DU_RETENU));
|
||||||
assert.ok(Object.isFrozen(COLONNES_DU_RETENU[0]));
|
for (const colonne of COLONNES_DU_RETENU) assert.ok(Object.isFrozen(colonne), colonne.colonne);
|
||||||
assert.equal(t('propositions.colonne.collisionsCumulees'), 'Collisions cumulées (paire et tour)');
|
assert.equal(t('propositions.colonne.collisionsCumulees'), 'Collisions cumulées (paire et tour)');
|
||||||
|
assert.equal(t('propositions.colonne.rencontresRepetees'), 'Rencontres répétées choisies (paires)');
|
||||||
const explication = t('accueil.explication.collisionsCumulees');
|
const explication = t('accueil.explication.collisionsCumulees');
|
||||||
for (const fragment of ['paire', 'tour', 'Collisions cumulées (paire et tour)', '\u{2014}']) {
|
for (const fragment of ['paire', 'tour', 'Collisions cumulées (paire et tour)', '\u{2014}']) {
|
||||||
assert.ok(explication.includes(fragment), fragment);
|
assert.ok(explication.includes(fragment), fragment);
|
||||||
}
|
}
|
||||||
for (const cle of ['accueil.colonne.diffRedondance', 'accueil.explication.diffRedondance', 'accueil.colonne.collisionsCumulees']) {
|
// L'explication des rencontres répétées dit ce qu'elle compte, d'où elle
|
||||||
|
// vient et quand elle vaut « — », et n'est pas celle des collisions.
|
||||||
|
const repetees = t('accueil.explication.rencontresRepetees');
|
||||||
|
for (const fragment of ['paires', "plus d'un tour", 'réservations', 'Rencontres répétées choisies (paires)', 'retenu', '\u{2014}']) {
|
||||||
|
assert.ok(repetees.includes(fragment), fragment);
|
||||||
|
}
|
||||||
|
assert.notEqual(repetees, explication);
|
||||||
|
for (const cle of [
|
||||||
|
'accueil.colonne.diffRedondance',
|
||||||
|
'accueil.explication.diffRedondance',
|
||||||
|
'accueil.colonne.collisionsCumulees',
|
||||||
|
'accueil.colonne.rencontresRepetees',
|
||||||
|
]) {
|
||||||
assert.throws(() => t(cle), RangeError, cle);
|
assert.throws(() => t(cle), RangeError, cle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ligneAffichee : les résumés en attente (§ 8.1)', () => {
|
describe('ligneAffichee : les résumés en attente (§ 8.1)', () => {
|
||||||
test('sans résumé reçu, la ligne est en attente et ses deux valeurs de résumé ne disent rien ; reçu, elle ne l’est plus', () => {
|
test('sans résumé reçu, la ligne est en attente et ses trois valeurs de résumé ne disent rien ; reçu, elle ne l’est plus', () => {
|
||||||
const attente = ligneAffichee(ligne(), format);
|
const attente = ligneAffichee(ligne(), format);
|
||||||
assert.equal(attente.enAttente, true);
|
assert.equal(attente.enAttente, true);
|
||||||
assert.equal(attente.collisionsCumulees, VIDE);
|
assert.equal(attente.collisionsCumulees, VIDE);
|
||||||
|
assert.equal(attente.rencontresRepetees, VIDE);
|
||||||
assert.deepEqual(attente.meilleure, { id: VIDE });
|
assert.deepEqual(attente.meilleure, { id: VIDE });
|
||||||
assert.equal(ligneAffichee(ligne(), format, AUCUN).enAttente, false);
|
assert.equal(ligneAffichee(ligne(), format, AUCUN).enAttente, false);
|
||||||
const recu = ligneAffichee(ligne(), format, {
|
const recu = ligneAffichee(ligne(), format, {
|
||||||
mesuresDuRetenu: { collisionsCumulees: 10 },
|
mesuresDuRetenu: { collisionsCumulees: 10, rencontresRepetees: 4 },
|
||||||
meilleureDuClassement: { id: 3 },
|
meilleureDuClassement: { id: 3 },
|
||||||
});
|
});
|
||||||
assert.equal(recu.enAttente, false);
|
assert.equal(recu.enAttente, false);
|
||||||
assert.equal(recu.collisionsCumulees, format.nombre(10));
|
assert.equal(recu.collisionsCumulees, format.nombre(10));
|
||||||
|
assert.equal(recu.rencontresRepetees, format.nombre(4));
|
||||||
assert.deepEqual(recu.meilleure, { id: format.nombre(3) });
|
assert.deepEqual(recu.meilleure, { id: format.nombre(3) });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('les six informations ne dépendent pas des résumés : en attente ou non, elles sont les mêmes', () => {
|
test('les six informations ne dépendent pas des résumés : en attente ou non, elles sont les mêmes', () => {
|
||||||
const sans = ({ enAttente, collisionsCumulees, meilleure, ...reste }) => reste;
|
const sans = ({ enAttente, collisionsCumulees, rencontresRepetees, meilleure, ...reste }) => reste;
|
||||||
assert.deepEqual(sans(ligneAffichee(ligne(), format)), sans(ligneAffichee(ligne(), format, AUCUN)));
|
assert.deepEqual(sans(ligneAffichee(ligne(), format)), sans(ligneAffichee(ligne(), format, AUCUN)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,10 @@ export const TABLE = new Map([
|
||||||
['accueil.colonne.participants', 'Participants'],
|
['accueil.colonne.participants', 'Participants'],
|
||||||
['accueil.colonne.modification', 'Dernière modification'],
|
['accueil.colonne.modification', 'Dernière modification'],
|
||||||
['accueil.colonne.chemin', 'Fichier'],
|
['accueil.colonne.chemin', 'Fichier'],
|
||||||
// La mesure du placement retenu (§ 5.4, § 8.1) : son intitulé est celui de
|
// Les mesures du placement retenu (§ 5.4, § 8.1) : l'intitulé de chacune
|
||||||
// la colonne du tableau des propositions qui porte la même mesure ;
|
// est celui de la colonne du tableau des propositions qui porte la même
|
||||||
// l'explication que l'infobulle de l'intitulé donne dit ce que compte le
|
// mesure ; l'explication que l'infobulle de l'intitulé donne dit ce que
|
||||||
// nombre, d'où il vient, et quand il vaut « — ».
|
// compte le nombre, d'où il vient, et quand il vaut « — ».
|
||||||
[
|
[
|
||||||
'accueil.explication.collisionsCumulees',
|
'accueil.explication.collisionsCumulees',
|
||||||
'Les collisions du placement retenu. Chaque fois que deux personnes de la même appartenance partagent une ' +
|
'Les collisions du placement retenu. Chaque fois que deux personnes de la même appartenance partagent une ' +
|
||||||
|
|
@ -85,6 +85,15 @@ export const TABLE = new Map([
|
||||||
"Plus il est bas, mieux c'est. « \u{2014} » quand aucun placement n'est retenu, ou que le retenu ne décrit " +
|
"Plus il est bas, mieux c'est. « \u{2014} » quand aucun placement n'est retenu, ou que le retenu ne décrit " +
|
||||||
"plus la configuration, ou se contredit.",
|
"plus la configuration, ou se contredit.",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'accueil.explication.rencontresRepetees',
|
||||||
|
"Les rencontres répétées du placement retenu : les paires de personnes réunies à la même table à plus d'un " +
|
||||||
|
'tour que le placement a choisies, comptées une fois par paire : deux personnes ensemble à trois tours font 1. ' +
|
||||||
|
"Une paire que les réservations réunissent à chacune de ses rencontres n'y entre pas. Le même nombre que " +
|
||||||
|
'la colonne « Rencontres répétées choisies (paires) » du tableau des propositions, sur la ligne du retenu. ' +
|
||||||
|
"Plus il est bas, mieux c'est. « \u{2014} » quand aucun placement n'est retenu, ou que le retenu ne décrit " +
|
||||||
|
'plus la configuration, ou se contredit.',
|
||||||
|
],
|
||||||
|
|
||||||
// La meilleure proposition du classement de chaque événement (§ 5.4,
|
// La meilleure proposition du classement de chaque événement (§ 5.4,
|
||||||
// § 5.7) : son titre et son identifiant s'écrivent sous les libellés du
|
// § 5.7) : son titre et son identifiant s'écrivent sous les libellés du
|
||||||
|
|
|
||||||
|
|
@ -643,13 +643,14 @@ export const ECRANS = Object.freeze([
|
||||||
mode: null,
|
mode: null,
|
||||||
async atteindre(contexte) {
|
async atteindre(contexte) {
|
||||||
// La liste paraît avant ses résumés : tant que l'horloge pilotée
|
// La liste paraît avant ses résumés : tant que l'horloge pilotée
|
||||||
// n'avance pas, chaque valeur de résumé porte le signe du chargement.
|
// n'avance pas, chaque valeur de résumé porte le signe du chargement :
|
||||||
// Rien ici ne l'avance : les signes restent pendant les relevés des
|
// trois par ligne, les deux mesures du retenu et la meilleure
|
||||||
// deux thèmes, où le trait de chacun se mesure comme la marque
|
// proposition. Rien ici ne l'avance : les signes restent pendant les
|
||||||
// « chargement ».
|
// relevés des deux thèmes, où le trait de chacun se mesure comme la
|
||||||
|
// marque « chargement ».
|
||||||
const m = await contexte.monter({ fichiers: [fichierEtat(chargePrincipale()), fichierEtat(chargeGelee(), 'gelee')] });
|
const m = await contexte.monter({ fichiers: [fichierEtat(chargePrincipale()), fichierEtat(chargeGelee(), 'gelee')] });
|
||||||
await attendre('le signe du chargement dans chaque valeur de résumé', () =>
|
await attendre('le signe du chargement dans chaque valeur de résumé', () =>
|
||||||
[BASE, 'gelee'].every((base) => m.racine.querySelectorAll(`[data-evenement="${base}"] [aria-busy="true"] [data-chargement]`).length === 2),
|
[BASE, 'gelee'].every((base) => m.racine.querySelectorAll(`[data-evenement="${base}"] [aria-busy="true"] [data-chargement]`).length === 3),
|
||||||
);
|
);
|
||||||
return m.racine;
|
return m.racine;
|
||||||
},
|
},
|
||||||
|
|
@ -659,17 +660,19 @@ export const ECRANS = Object.freeze([
|
||||||
mode: null,
|
mode: null,
|
||||||
async atteindre(contexte) {
|
async atteindre(contexte) {
|
||||||
// La principale, sa proposition retenue : la ligne montre les
|
// La principale, sa proposition retenue : la ligne montre les
|
||||||
// collisions cumulées du retenu et la meilleure proposition du
|
// collisions cumulées et les rencontres répétées choisies du retenu et
|
||||||
// classement, et le pointeur réel sur l'intitulé des collisions — un
|
// la meilleure proposition du classement, et le pointeur réel sur
|
||||||
// en-tête de colonne du tableau, une fois pour la liste — ouvre son
|
// l'intitulé des collisions — un en-tête de colonne du tableau, une
|
||||||
// infobulle.
|
// fois pour la liste — ouvre son infobulle.
|
||||||
const principale = chargePrincipale();
|
const principale = chargePrincipale();
|
||||||
const charge = executer(principale, 'retenirProposition', { id: principale.propositions[0].id });
|
const charge = executer(principale, 'retenirProposition', { id: principale.propositions[0].id });
|
||||||
const m = await contexte.monter({ fichiers: [fichierEtat(charge)] });
|
const m = await contexte.monter({ fichiers: [fichierEtat(charge)] });
|
||||||
const ligne = `[data-evenement="${BASE}"]`;
|
const ligne = `[data-evenement="${BASE}"]`;
|
||||||
await chargerResumes(m);
|
await chargerResumes(m);
|
||||||
await attendre('les collisions du retenu et la meilleure proposition', () =>
|
await attendre('les mesures du retenu et la meilleure proposition', () =>
|
||||||
['[data-colonne="collisionsCumulees"]', '[data-meilleure-id]'].every((valeur) => /\d/.test(m.racine.querySelector(`${ligne} ${valeur}`)?.textContent ?? '')),
|
['[data-colonne="collisionsCumulees"]', '[data-colonne="rencontresRepetees"]', '[data-meilleure-id]'].every((valeur) =>
|
||||||
|
/\d/.test(m.racine.querySelector(`${ligne} ${valeur}`)?.textContent ?? ''),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const { x, y } = await centre(await element(m, '[data-ecran="accueil"] thead [data-terme="collisionsCumulees"]'));
|
const { x, y } = await centre(await element(m, '[data-ecran="accueil"] thead [data-terme="collisionsCumulees"]'));
|
||||||
await pointer('bouger', x, y);
|
await pointer('bouger', x, y);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue