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:
co-authored by
Claude Opus 5.5
parent
67bd207337
commit
74693c4277
@@ -0,0 +1,74 @@
|
||||
// recette.mjs -- règles de la recette design communes aux tests dans un
|
||||
// navigateur (play.mjs, accueil.mjs, match.mjs), vérifiées sur chaque écran
|
||||
// capturé.
|
||||
//
|
||||
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
|
||||
/**
|
||||
* Les éléments interactifs visibles cachés, même en partie, sous une barre
|
||||
* d'action collée en bas de l'écran (position: sticky, ancrée par bottom) :
|
||||
* un bouton, un lien ou un interrupteur que la barre recouvre ne se voit
|
||||
* pas et ne se touche pas. Un élément entièrement sous le bas de l'écran
|
||||
* n'est pas visible : il n'est pas compté. Ce qui passe devant la barre (le
|
||||
* menu, un panneau) n'est pas caché : seul compte ce que la barre recouvre
|
||||
* vraiment, au point du recouvrement.
|
||||
*
|
||||
* Renvoie la liste des éléments cachés, décrits (vide si tout va bien).
|
||||
*/
|
||||
export async function hiddenUnderActionBar(target) {
|
||||
return target.evaluate(() => {
|
||||
const shown = (el) => {
|
||||
if (el.closest("[hidden]")) {
|
||||
return false;
|
||||
}
|
||||
const style = getComputedStyle(el);
|
||||
if (style.display === "none" || style.visibility === "hidden") {
|
||||
return false;
|
||||
}
|
||||
const r = el.getBoundingClientRect();
|
||||
return r.width > 0 && r.height > 0;
|
||||
};
|
||||
const bars = [...document.querySelectorAll("body *")].filter((el) => {
|
||||
const style = getComputedStyle(el);
|
||||
return style.position === "sticky" && style.bottom !== "auto" && shown(el);
|
||||
});
|
||||
const describe = (el) =>
|
||||
el.id
|
||||
? `#${el.id}`
|
||||
: `${el.tagName.toLowerCase()}${[...el.classList].map((c) => `.${c}`).join("")} « ${
|
||||
el.getAttribute("aria-label") ?? el.textContent.trim().slice(0, 40)
|
||||
} »`;
|
||||
const hidden = [];
|
||||
const interactive = document.querySelectorAll(
|
||||
"button, a[href], input, select, textarea, [role=switch], [role=radio], " +
|
||||
"[tabindex]:not([tabindex='-1'])",
|
||||
);
|
||||
for (const bar of bars) {
|
||||
const b = bar.getBoundingClientRect();
|
||||
for (const el of interactive) {
|
||||
if (bar.contains(el) || el.contains(bar) || !shown(el)) {
|
||||
continue;
|
||||
}
|
||||
const r = el.getBoundingClientRect();
|
||||
if (r.top >= innerHeight) {
|
||||
continue;
|
||||
}
|
||||
const top = Math.max(r.top, b.top);
|
||||
const bottom = Math.min(r.bottom, b.bottom, innerHeight);
|
||||
const left = Math.max(r.left, b.left);
|
||||
const right = Math.min(r.right, b.right);
|
||||
if (bottom - top <= 1 || right - left <= 1) {
|
||||
continue;
|
||||
}
|
||||
const hit = document.elementFromPoint((left + right) / 2, (top + bottom) / 2);
|
||||
if (hit !== null && bar.contains(hit)) {
|
||||
hidden.push(
|
||||
`${describe(el)} sous ${describe(bar)} (${Math.round(bottom - top)} px)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hidden;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user