Recette design du match : Crawford en paysage, « Nouvelle partie » en deux colonnes, « Rejouer »

- Paysage : pendant la partie Crawford, la consigne de la bande centrale
  commence par « Partie Crawford · » ; la police se resserre si besoin.
- Écran « Nouvelle partie » en paysage : deux colonnes au-dessus de la
  barre d'action (type de partie et longueur à gauche, niveau et conseil
  du maître à droite), tout tient en 915x412 et en 844x320.
- Fin de partie : « Rejouer » relance tout de suite une partie aux mêmes
  réglages (un match de même longueur après un match), « Partie suivante »
  dans un match qui continue, lien « Changer les réglages ».
- Recette automatique : aucun élément interactif visible sous une barre
  d'action collée, vérifié sur chaque écran capturé (tests/browser/recette.mjs).
- Scénario « Comment rejouer avec les mêmes réglages ? ».

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Pierre MARTIN
2026-11-10 05:03:59 +01:00
co-authored by Claude Opus 5.5
parent 67bd207337
commit 74693c4277
16 changed files with 484 additions and 51 deletions
+25 -10
View File
@@ -21,6 +21,7 @@
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { chromium, firefox } from "playwright";
import { hiddenUnderActionBar } from "./recette.mjs";
const [base, browserName, shots] = process.argv.slice(2);
const browserType = { chromium, firefox }[browserName];
@@ -343,6 +344,8 @@ async function checkView(width, height, when) {
const where = `${width}x${height} (${when})`;
const wrapped = await wrappedButtons(page);
check(wrapped.length === 0, `${where} : boutons sur plusieurs lignes : ${wrapped.join(", ")}`);
const hidden = await hiddenUnderActionBar(page);
check(hidden.length === 0, `${where} : caché sous la barre d'action : ${hidden.join(", ")}`);
const seen = await page.evaluate(() => {
const visible = (el) => {
if (el.closest("[hidden]")) {
@@ -809,6 +812,8 @@ async function checkAddresses() {
const where = `${name} ${width}x${height}`;
const wrapped = await wrappedButtons(tab);
check(wrapped.length === 0, `${where} : boutons sur plusieurs lignes : ${wrapped.join(", ")}`);
const hidden = await hiddenUnderActionBar(tab);
check(hidden.length === 0, `${where} : caché sous la barre d'action : ${hidden.join(", ")}`);
if (name === "question" || name === "terme") {
// La barre haute de l'aide reste en vue, et ce qu'on a
// ouvert s'arrête juste sous elle.
@@ -996,6 +1001,15 @@ async function startChosenGame() {
"« Commencer » ne ramène pas à la partie");
}
/** « Rejouer » après une partie finie : une nouvelle partie aux mêmes
* réglages commence tout de suite, sans passer par l'écran « Nouvelle
* partie ». */
async function rematch() {
await page.click("#again");
check((await page.evaluate(() => location.hash)) === "#/" && !(await page.isVisible("#new-game")),
"« Rejouer » ouvre un autre écran que la partie");
}
/** Nouvelle partie depuis le menu, par l'écran « Nouvelle partie » : effet
* immédiat, bandeau 10 s. Renvoie le journal de la partie quittée. */
async function newGameFromMenu() {
@@ -1525,8 +1539,10 @@ const won = journal.events.at(-1).winner === "player";
check((await vibrations()).at(-1) === (won ? "30,50,30,50,60" : "60"),
`fin de partie : vibration ${(await vibrations()).at(-1)}`);
check((await page.textContent("#panel-secondary")) === "Revoir la partie" &&
(await page.textContent("#panel-primary")) === "Nouvelle partie",
"fin de partie : panneau sans « Revoir la partie » et « Nouvelle partie »");
(await page.textContent("#panel-primary")) === "Rejouer" &&
(await page.textContent("#panel-settings")) === "Changer les réglages" &&
(await page.getAttribute("#panel-settings", "href")) === "#/nouvelle-partie",
"fin de partie : panneau sans « Revoir la partie », « Rejouer » et « Changer les réglages »");
await review("6-fin-de-partie");
await page.setViewportSize({ width: 844, height: 320 });
await sleep(150);
@@ -1557,11 +1573,12 @@ log(skips > 0 ? `aucun coup possible ${skips} fois (${skipsByDice} en touchant l
log(`vibrations demandées : ${(await vibrations()).length}`);
await shot("4-fin-de-partie");
// « Revoir la partie » ferme le panneau ; « Nouvelle partie » reste dans la
// barre d'actions.
// « Revoir la partie » ferme le panneau ; « Rejouer » reste dans la barre
// d'actions.
await page.click("#panel-secondary");
check(!(await page.isVisible("#panel")) && (await page.isVisible("#again")),
"« Revoir la partie » : panneau toujours là, ou pas de « Nouvelle partie »");
check(!(await page.isVisible("#panel")) && (await page.textContent("#again")) === "Rejouer" &&
(await page.isVisible("#again")),
"« Revoir la partie » : panneau toujours là, ou pas de « Rejouer »");
step("mes parties");
// Mes parties (IndexedDB), depuis le menu : une deuxième partie commencée,
@@ -1569,8 +1586,7 @@ step("mes parties");
// recharger la page.
{
const finished = await savedJournal();
await page.click("#again");
await startChosenGame();
await rematch();
await waitForPlayer();
const second = await savedJournal();
check(second.head !== finished.head || second.events.length !== finished.events.length,
@@ -1651,8 +1667,7 @@ log("compte de points : bulle avec la fiche du glossaire");
step("thème sombre");
await page.emulateMedia({ colorScheme: "dark" });
await page.click("#again");
await startChosenGame();
await rematch();
await waitForPlayer();
await shot("6-sombre");
await page.emulateMedia({ colorScheme: "light" });