Recette 1g : textes corrigés, boutons en bas, barre haute en paysage

Leçon 1 §2 (flèche 7 vide, 5 déjà bloqué), leçon 2 « Faire des points »,
flèches 7 à 12, reformulations des leçons 3 et 4. Écran des leçons et du
tutoriel : « Retour » et « Continuer » en bas en portrait, « Revoir » sur
la ligne du titre, ligne « Leçon n · … » gardée en paysage. Barre haute
visible hors de l'écran de jeu en paysage (défaut antérieur à 1g), avec
un contrôle dans la recette automatique. Rappel des règles haut de
44 px ; « Aucune leçon terminée sur 5 · … » sur l'accueil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Pierre MARTIN
2026-11-12 13:55:17 +01:00
co-authored by Claude Opus 5.5
parent 5d0f7a9616
commit 351482fa9e
15 changed files with 147 additions and 49 deletions
+20 -3
View File
@@ -13,7 +13,7 @@
import { mkdirSync } from "node:fs";
import { chromium, firefox } from "playwright";
import { hiddenUnderActionBar, tallButtons } from "./recette.mjs";
import { hiddenUnderActionBar, missingTopbar, tallButtons } from "./recette.mjs";
const [base, browserName, shots] = process.argv.slice(2);
const browserType = { chromium, firefox }[browserName];
@@ -68,6 +68,8 @@ async function capture(page, name, settle = async () => {}) {
await settle();
const hidden = await hiddenUnderActionBar(page);
check(hidden.length === 0, `${name} ${width}x${height} : caché sous la barre d'action : ${hidden.join(", ")}`);
const topbar = await missingTopbar(page);
check(topbar === null, `${name} : ${topbar}`);
const tall = await tallButtons(page);
check(tall.length === 0, `${name} ${width}x${height} : boutons étirés : ${tall.join(", ")}`);
await page.screenshot({ path: `${dir}/${name}-${width}x${height}.png`, fullPage: true });
@@ -155,7 +157,8 @@ try {
await until(page, "carte de la leçon", () => {
const card = document.getElementById("home-lesson");
return location.hash === "#/accueil" && !card.hidden && !card.closest("[hidden]") &&
card.textContent.includes("Leçon 1") && !document.getElementById("home-new").disabled;
card.textContent.includes("Leçon 1") && !document.getElementById("home-new").disabled &&
document.getElementById("home-progress").textContent.includes(" · ");
});
const home = await page.evaluate(() => ({
go: document.getElementById("home-lesson-go").getAttribute("href"),
@@ -170,7 +173,7 @@ try {
}));
check(home.go === "#/lecons/1/1" && home.goText === "Commencer" && home.goClass.includes("secondary") &&
home.all === "#/lecons" && home.title === "Les ouvertures" && home.state === "À commencer · 4 exercices" &&
home.summary.startsWith("0 leçon terminée sur 5") && home.primaries === 1,
home.summary.startsWith("Aucune leçon terminée sur 5 · ") && home.primaries === 1,
`carte de l'accueil : ${JSON.stringify(home)}`);
await capture(page, "01-accueil-carte", async () => {
await page.locator("#home-lesson").scrollIntoViewIfNeeded();
@@ -188,6 +191,8 @@ try {
[...document.querySelectorAll("#lessons a.button:not(.secondary)")].filter((el) => !el.closest("[hidden]")).length);
check(primaries === 1, `liste : ${primaries} actions principales`);
check(await page.getAttribute("#lessons-rules", "href") === "#/tutoriel/1", "liste : rappel des règles");
const rulesHeight = await page.evaluate(() => document.getElementById("lessons-rules").getBoundingClientRect().height);
check(rulesHeight >= 44, `liste : rappel des règles haut de ${rulesHeight} px`);
await capture(page, "02-liste");
log("liste : cinq leçons, la première proposée, une seule action principale, rappel des règles");
@@ -208,6 +213,18 @@ try {
explain.segments === 4 && explain.skip === "#/lecons" && explain.back === "#/lecons" &&
explain.next === "#/lecons/1/1/a-vous" && explain.topbar === "none" &&
explain.glossary.includes("#/glossaire/jan-interieur"), `explication : ${JSON.stringify(explain)}`);
// « Revoir » sur la ligne du titre ; « Retour » et « Continuer » en bas
// de l'écran, sous le pouce (recette 1g).
const layout = await page.evaluate(() => {
const title = document.getElementById("lesson-title").getBoundingClientRect();
const replay = document.getElementById("lesson-replay").getBoundingClientRect();
const next = document.getElementById("lesson-next").getBoundingClientRect();
return { titleTop: title.top, titleBottom: title.bottom, replayTop: replay.top,
replayBottom: replay.bottom, nextBottom: next.bottom, height: innerHeight };
});
check(layout.replayTop < layout.titleBottom && layout.replayBottom > layout.titleTop,
`« Revoir » hors de la ligne du titre : ${JSON.stringify(layout)}`);
check(layout.nextBottom > layout.height - 64, `« Continuer » loin du bas : ${JSON.stringify(layout)}`);
check(await page.locator("#lesson-board .trace-arrow").count() > 0, "le coup illustré n'a pas laissé de traces");
await capture(page, "03-explication");
log("explication : coup illustré 8/5 6/5 joué tout seul, barre de quatre étapes, liens du glossaire");