v3 : modules d'ordre et de titre — l'interface, restes mineurs (L11, C25–C27)

Chaque section du lecteur finit par l'ordre de ses articles (inertie 2) : ses
articles numérotés, son vote déplié, « Proposer un autre ordre » — une liste où
l'on clique un article puis sa place, ses flèches, ou le clavier. Le titre du
document (inertie 4) se vote et se propose sous le titre, « traduction à
revoir » quand il a changé. Les deux sont dans la liste des propositions, le
panier et la chaîne Ğ1 ; un nouvel article se propose dans une section, à sa fin.
La liste du groupe compte les propositions ouvertes des modules.

Restes mineurs : la participation ramenée se dit en deux temps (la mesure, puis
ce que le groupe demande), les lignes « habituelle » des réglages resserrées, le
sélecteur d'inertie se parcourt aux flèches (le cran actuel atteignable, jamais
choisi), la marque « seed a minima » porte le vrai nombre de votes.

Contrôles : C25 (ordre, dont la chaîne et le téléphone), C26 (titre), C27
(participation ramenée) ; C3, C17 (41 modules), C20 mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 05:35:02 +01:00
co-authored by Claude Opus 5.5
parent 3831ddb1bf
commit 785580c78f
31 changed files with 1480 additions and 136 deletions
+24 -4
View File
@@ -43,7 +43,9 @@ test('C3 — le texte en vigueur : raison d’être et deux engagements, seed a
await expect(units.nth(0).locator('h2')).toHaveText("Raison d'être");
await expect(units.nth(1).locator('h2')).toHaveText('Unicité du compte');
await expect(units.nth(2).locator('h2')).toHaveText('Réciprocité');
await expect(page.getByText('seed a minima · 0 vote')).toHaveCount(3);
// The seed mark, with the real number of votes beside it (none yet on these three).
await expect(units.getByText('seed a minima · 0 vote')).toHaveCount(3);
await expect(units.getByTitle("Entré en vigueur sans vote, pour ne pas partir d'une page blanche. Il reste sous vote, comme tout texte.")).toHaveCount(3);
await expect(page.getByRole('heading', { name: 'Propositions en cours' })).toBeVisible();
// One line per article: E3 and E7 hold their two variants inside.
await expect(page.locator('ul.deep button.row')).toHaveCount(30);
@@ -287,7 +289,8 @@ test('C20 — réglages de la formule et cinq crans', async ({ page }) => {
// At 100 voters: C plus each level's share of the 50 votes beyond C.
const required = panel.locator('table tbody td.required');
await expect(required).toHaveText(['80', '84', '88', '92', '95']);
await expect(panel.getByText('celles exigées à la participation habituelle, 100 votants')).toBeVisible();
await expect(panel.getByText("Voix pour : à 100 votants, comme d'habitude.")).toBeVisible();
await expect(panel.getByText("Part exigée : les « pour » demandés au-delà de C, à 100 votants comme d'habitude.")).toBeVisible();
await page.keyboard.press('Escape');
// From the vote of an article: Unicité du compte is at 4 / 5, its row is marked.
@@ -300,7 +303,7 @@ test('C20 — réglages de la formule et cinq crans', async ({ page }) => {
await expect(panel.locator('tr.current th')).toContainText('4');
await expect(panel.locator('tr.current th')).toContainText('cet article');
await expect(required).toHaveText(['50', '50', '50', '50', '50']);
await expect(panel.getByText('celles exigées à la participation de cette version, 0 votant.')).toBeVisible();
await expect(panel.getByText('Voix pour : à 0 votant, la participation de cette version.')).toBeVisible();
await page.keyboard.press('Escape');
// The five levels to choose from, in the test group; the change is voted at the article's level.
@@ -311,9 +314,26 @@ test('C20 — réglages de la formule et cinq crans', async ({ page }) => {
await e2.getByText("Proposer un changement d'inertie").click();
const levels = e2.getByRole('radiogroup', { name: 'Nouvelle inertie' }).getByRole('radio');
await expect(levels).toHaveCount(5);
await expect(levels.nth(3)).toBeDisabled();
// The current level is reachable — one stop in the tab order — and never chosen.
await expect(levels.nth(3)).toHaveAttribute('aria-disabled', 'true');
await expect(levels.nth(3)).toContainText('actuelle');
await expect(e2.locator('[role="radio"][tabindex="0"]')).toHaveCount(1);
await expect(levels.nth(3)).toHaveAttribute('tabindex', '0');
await expect(e2.getByText("Un changement d'inertie se vote lui-même à l'inertie de l'article : 4 / 5 · haute.")).toBeVisible();
await levels.nth(3).click();
await expect(e2.locator('[role="radio"][aria-checked="true"]')).toHaveCount(0);
// With the keyboard: the arrows go from level to level and choose it, wrapping round.
await levels.nth(3).focus();
await page.keyboard.press('ArrowRight');
await expect(levels.nth(4)).toBeFocused();
await expect(levels.nth(4)).toHaveAttribute('aria-checked', 'true');
await page.keyboard.press('ArrowRight');
await expect(levels.nth(0)).toBeFocused();
await expect(levels.nth(0)).toHaveAttribute('aria-checked', 'true');
await expect(e2.locator('[role="radio"][tabindex="0"]')).toHaveCount(1);
await page.keyboard.press('ArrowLeft');
await expect(levels.nth(4)).toHaveAttribute('aria-checked', 'true');
// Or with a click.
await levels.nth(4).click();
await expect(levels.nth(4)).toHaveAttribute('aria-checked', 'true');
await e2.getByRole('button', { name: 'Soumettre au vote' }).click();