Lot U2 : les contrôles d'abord, vus rouges sur l'interface d'avant — cibles de 44 px sur téléphone et les trois lectures entières (M2), « Proposer cet ordre » dit pourquoi il est éteint (C35), une information à un seul endroit dans le vote déplié (onceonly, C19, C36), l'en-tête compte ce que montre la liste et un changement d'inertie d'un module se trouve à sa ligne (C38), settings.reading et l'anglais (lexicon)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 11:24:00 +01:00
co-authored by Claude Opus 5.5
parent cbf477c562
commit b39f7678b0
5 changed files with 275 additions and 1 deletions
+66 -1
View File
@@ -1,5 +1,5 @@
import { expect, test, type Locator } from '@playwright/test';
import { DOC, expectWelcome, shot, signIn } from './helpers';
import { DOC, expectWelcome, propose, seedsInForce, shot, signIn, TEST_DOC, testDoc } from './helpers';
/** WCAG contrast ratio of two CSS colours `rgb(r, g, b)`. */
function contrast(a: string, b: string): number {
@@ -112,3 +112,68 @@ test('M1 — mobile : logo dans la barre, l’accueil sous la voûte, menu en ti
await panel.getByRole('group', { name: 'Tout le document' }).getByRole('button', { name: 'Vider' }).click();
await expect(panel.getByText("Rien dans le panier pour l'instant.")).toBeVisible();
});
/** The height of a target, in CSS pixels. */
async function height(target: Locator): Promise<number> {
return (await target.boundingBox())?.height ?? 0;
}
/** Every gesture in sight under `root` — buttons, links, summaries — under 44 px high. */
async function small(root: Locator): Promise<string[]> {
return root.evaluate((el) =>
[...el.querySelectorAll('button, a[href], summary, [role="radio"]')]
.filter((b) => b.checkVisibility({ visibilityProperty: true }) && b.getClientRects().length > 0)
.map((b) => ({ what: (b.textContent || b.getAttribute('aria-label') || '').replace(/\s+/g, ' ').trim(), h: b.getBoundingClientRect().height }))
.filter((x) => x.h < 44)
.map((x) => `${x.what} (${Math.round(x.h)} px)`),
);
}
test('M2 — téléphone : les gestes du lecteur font au moins 44 px, les trois lectures se lisent en entier', async ({ page, browser }) => {
await seedsInForce(browser);
await signIn(page, 'Chiara');
// In the test group: a rival of the Raison d'être (in force), so that its vote names its
// « Autres versions », and an article of hers, whose quorum she sets before any vote.
const doc = await testDoc(page);
const i1 = doc.units.find((u) => u.code === 'I1');
const r = await page.request.post(`/api/docs/${doc.id}/proposals`, {
data: { kind: 'text', unitId: i1?.id, title: "Raison d'être", body: 'Une clé, une personne vivante : la certification ne garantit rien d’autre.', submit: true },
headers: { 'x-requested-with': 'libredecision' },
});
expect(r.ok()).toBe(true);
const mine = await propose(page, 'Cibles au doigt', 'Je propose cet article pour régler son quorum au doigt.');
await page.goto(TEST_DOC);
// The three readings, each whole on the screen: « Si tout passe » is never cut (lot U2).
const views = page.getByRole('radiogroup', { name: 'Lire' });
const frame = await views.boundingBox();
expect.soft(await views.evaluate((el) => el.scrollWidth <= el.clientWidth + 1)).toBe(true);
for (const reading of await views.getByRole('radio').all()) {
const b = await reading.boundingBox();
expect.soft(b && frame && b.x >= frame.x - 1 && b.x + b.width <= frame.x + frame.width + 1, await reading.textContent()).toBe(true);
}
await expect.soft(views.getByRole('radio', { name: 'Si tout passe' })).toBeInViewport({ ratio: 1 });
await shot(page, 'm2-1-lectures');
// « Tout déplier », then the vote of the Raison d'être: every gesture a finger's target.
expect.soft(await height(page.getByRole('button', { name: 'Tout déplier' }))).toBeGreaterThanOrEqual(44);
const first = page.locator('section.text article.unit').first();
await first.getByRole('button', { name: 'Le vote' }).click();
const fold = first.locator('section.fold');
const others = fold.getByRole('button', { name: 'Autres versions (1)' });
expect.soft(await height(others)).toBeGreaterThanOrEqual(44);
await others.click();
await fold.getByText('La formule du seuil').click();
expect.soft(await small(fold)).toEqual([]);
await shot(page, 'm2-2-vote-deplie');
// Her own article: « Fixer ce quorum » and the rest of its vote.
await page.goto(`${TEST_DOC}?vote=${mine}`);
const hers = page.getByRole('region', { name: 'Le vote de « Cibles au doigt »' });
const set = hers.getByRole('button', { name: 'Fixer ce quorum' });
expect.soft(await height(set)).toBeGreaterThanOrEqual(44);
await hers.getByText("Proposer un changement d'inertie").click();
expect.soft(await small(hers)).toEqual([]);
await set.scrollIntoViewIfNeeded();
await shot(page, 'm2-3-quorum');
});