test: TC-07 feuilles item (44 types)

- creation + ouverture sheet pour les 44 types d'item
- verification scroll via .sheet-body overflow-y:auto
- 4/4 passed, 0 failed
This commit is contained in:
fabres
2026-07-24 08:51:07 +02:00
parent 1272cfdfd1
commit 882aa5fbcb
2 changed files with 244 additions and 3 deletions
+32 -3
View File
@@ -1,8 +1,37 @@
# Quelques notes pour les tests
# Commandes utiles
## Scripts automatisés
Dans la console dans les "devtools" de Chrome, quelques commandes sont utiles:
Tous les scripts sont dans `tests/scripts/`. Ils utilisent `chrome-devtools` (CLI) pour interagir avec Foundry.
* `CONFIG.debug.hooks = true` permet de logger les appels des hooks et les objets passés
### TC-07 - Feuilles d'Item (44 types)
Teste la création, l'ouverture et le défilement de chaque type d'item.
```
URL=https://localhost:31000 PASS=adminpw bash tests/scripts/tc-07-feuilles-item.sh
```
- Crée chaque item (44 types), ouvre sa feuille, vérifie le nom de la classe sheet
- Vérifie que `.sheet-body` a `overflow-y: auto` pour le défilement
- Nettoie tous les items créés
- Si la session Foundry n'existe pas encore, utiliser `--navigate` pour login auto
### API des scripts
Les scripts suivent une convention commune :
| Variable | Rôle |
|---|---|
| `URL` | URL Foundry |
| `PASS` | Mot de passe admin |
| `--navigate` | Force login + attente game.ready |
Chaque script commence par `jse_wait_ready` (polling game.ready). Les opérations lourdes sont atomiques (un seul `evaluate_script` asynchrone) pour éviter les races de page.
## Commandes utiles (console manuelle)
```
CONFIG.debug.hooks = true # log tous les hooks Foundry
```
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/env bash
# ============================================================================
# TC-07 — Feuilles d'Item (44 types)
# Usage: ./tc-07-feuilles-item.sh [--navigate]
# --navigate : force navigation + login
# Env: URL, PASS (admin password)
# ============================================================================
set -uo pipefail
URL="${URL:-https://localhost:31000}"
PASS="${PASS:-}"
NAVIGATE="${1:-}"
PASSED=0; FAILED=0
die() { echo "$*"; exit 1; }
pass() { echo "$1"; PASSED=$((PASSED+1)); }
fail() { echo "$1"; FAILED=$((FAILED+1)); }
jse_get() { chrome-devtools evaluate_script "() => $1" 2>/dev/null | sed -n '/^```json$/{n;p;}'; }
jse_wait_ready() {
local t="${1:-60}" p="${2:-1000}"
chrome-devtools evaluate_script "async () => {
for(let i=0;i<$t*1000/$p;i++){ if(typeof game!=='undefined'&&game.ready) return 'ready'; await new Promise(r=>setTimeout(r,$p)); }
return 'timeout';
}" 2>/dev/null | sed -n '/^```json$/{n;p;}' | tr -d '"\n'
}
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ TC-07 — Feuilles d'Item (44 types) ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
R=$(jse_wait_ready 15 2)
if [ "$R" != "ready" ] && [ "$NAVIGATE" = "--navigate" ]; then
[ -z "$PASS" ] && die "PASS=..."
chrome-devtools new_page "$URL" --background false --timeout 15000 --isolatedContext "rdd-tc07" >/dev/null 2>&1; sleep 3
PW=$(jse_get "window.location.pathname" | tr -d '"\n')
case "$PW" in
*/join) chrome-devtools evaluate_script "() => {
const s=document.querySelector('select[name=userid]'); if(s){ const o=Array.from(s.options); const g=o.find(x=>x.text==='Gamemaster'); const p=g||o.find(x=>x.value&&!x.disabled)||o.find(x=>x.value); if(p) s.value=p.value; }
const a=document.querySelector('input[name=adminPassword]'); if(a) a.value='$PASS';
const b=document.querySelector('button[name=join]'); if(b) b.click();
}" >/dev/null 2>&1 || true ;;
*/setup) chrome-devtools evaluate_script "() => {
document.querySelectorAll('[data-action=worldLaunch]').forEach(l=>{ const li=l.closest('li.package.world'); if(li&&li.querySelector('.package-title')?.textContent==='Reve de Dragon') l.click(); });
}" >/dev/null 2>&1 || true ;;
esac
R=$(jse_wait_ready 60 2)
fi
[ "$R" != "ready" ] && die "Game not ready"
echo " ✓ Game ready"; echo ""
# ============================================================================
# Atomic test: all items in one evaluate_script
# ============================================================================
RESULT=$(chrome-devtools evaluate_script "async () => {
if (typeof game === 'undefined' || !game.ready) return {error: 'NOT_READY'};
const out = {};
const ok = []; const fail = [];
// Item types with dedicated sheets (from registerAll)
const TYPES = [
'arme', 'armure', 'blessure', 'casetmr', 'chant', 'competence',
'competencecreature', 'conteneur', 'danse', 'empoignade', 'extraitpoetique',
'faune', 'gemme', 'herbe', 'ingredient', 'jeu', 'livre', 'maladie',
'meditation', 'monnaie', 'munition', 'musique', 'nombreastral',
'nourritureboisson', 'objet', 'oeuvre', 'ombre', 'plante', 'poison',
'possession', 'potion', 'queue', 'race', 'recettealchimique',
'recettecuisine', 'rencontre', 'service', 'signedraconique', 'sort',
'sortreserve', 'souffle', 'tache', 'tarot', 'tete'
];
const MIN_TYPES = 15; // plan de test: 15 types principaux listes
// Create a test actor for owner-bound items
const [actor] = await Actor.createDocuments([{type: 'personnage', name: 'TC-07 Support'}]);
out.actorCreated = !!actor;
out.sheets = {};
for (const type of TYPES) {
try {
const data = {type, name: 'TC-07 ' + type};
// Add minimal system data for types that need it
if (type === 'arme') data.system = {categorie: 'cac', degats: '1d6', mains: 1, encombrement: 2};
if (type === 'armure') data.system = {protection: 5, encombrement: 3};
if (type === 'competence') data.system = {categorie: 'physique', carac: 'force'};
if (type === 'competencecreature') data.system = {categorie: 'naturelle', isnaturelle: true, dommages: 6};
if (type === 'sort') data.system = {niveau: 1, voie: 'feu'};
if (type === 'blessure') data.system = {gravite: 2};
if (type === 'possession') data.system = {entiteid: 'test', victimeid: 'test'};
// Create item (standalone or on actor depending on type)
let item;
const needsActor = ['blessure', 'possession', 'competencecreature'];
if (needsActor.includes(type)) {
[item] = await actor.createEmbeddedDocuments('Item', [data]);
} else {
item = await Item.create(data);
}
if (!item) { fail.push(type + ':null'); continue; }
const sheet = await item.sheet.render(true);
await new Promise(r => setTimeout(r, 500));
const sn = sheet.constructor.name;
const html = sheet.element;
// Check scrollable: .sheet-body with overflow-y:auto is the scroll mechanism
let scrollOk = false;
if (html) {
const sb = html.querySelector('.sheet-body');
if (sb) {
const cs = window.getComputedStyle(sb);
scrollOk = cs.overflowY === 'auto' || cs.overflowY === 'scroll';
}
// Also check the content-area class uses the item-type-content pattern
const wc = html.querySelector('.window-content');
if (wc && wc.className.includes(type + '-content')) scrollOk = true;
}
await sheet.close();
// Wait a tick after close
await new Promise(r => setTimeout(r, 200));
// Delete standalone items
if (!needsActor.includes(type)) {
await item.delete();
}
ok.push(type + ':' + sn + (scrollOk ? '+scroll' : ''));
out.sheets[type] = {sheetName: sn, hasScrollableBody: scrollOk};
} catch(e) {
fail.push(type + ':' + (e?.message || String(e)).substring(0, 80));
}
}
// Cleanup actor and its embedded items
if (actor) await actor.delete();
out.ok = ok;
out.fail = fail;
out.totalOk = ok.length;
out.totalFail = fail.length;
// TC-07-016: scroll test details
out.scrollCount = Object.values(out.sheets).filter(s => s.hasScrollableBody).length;
out.scrollableTypes = Object.entries(out.sheets).filter(([,s]) => s.hasScrollableBody).map(([t]) => t);
return out;
}" 2>/dev/null | sed -n '/^```json$/{n;p;}')
[ -z "$RESULT" ] && die "Empty result"
eval_ok() { echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$1',''))" 2>/dev/null; }
echo "--- TC-07-001 a 015: Creation + ouverture feuilles ---"
OK_LIST=$(eval_ok ok)
FAIL_LIST=$(eval_ok fail)
TOTAL_OK=$(eval_ok totalOk)
TOTAL_FAIL=$(eval_ok totalFail)
[ "$TOTAL_OK" -ge 15 ] && pass "TC-07-001: $TOTAL_OK types cree + sheet ouverte (≥15 attendus)" || fail "TC-07-001: seulement $TOTAL_OK OK (attendu ≥15)"
# Print details
echo "$RESULT" | python3 -c "
import sys, json
d = json.load(sys.stdin)
sheets = d.get('sheets', {})
for t, info in sorted(sheets.items()):
sn = info.get('sheetName', '?')
sc = '📜' if info.get('hasScrollableBody') else ''
print(f' {t}: {sn} {sc}')
fails = d.get('fail', [])
for f in fails:
print(f' ✘ {f}')
" 2>/dev/null
echo ""
echo "--- TC-07-016: Scrollable body ---"
SCROLL_CNT=$(eval_ok scrollCount)
SCROLL_TYPES=$(eval_ok scrollableTypes)
[ "$SCROLL_CNT" -gt 0 ] 2>/dev/null && pass "TC-07-016: $SCROLL_CNT types avec .item-sheet-scrollable-body" || fail "TC-07-016: aucun scrollable"
echo ""
echo "--- Cleanup ---"
CLEANUP=$(chrome-devtools evaluate_script "async () => {
const a = game.actors.getName('TC-07 Support');
if (a) await a.delete();
// Clean up any standalone items left
const items = game.items.filter(i => i.name && i.name.startsWith('TC-07 '));
for (const it of items) await it.delete();
return 'ok';
}" 2>/dev/null | sed -n '/^```json$/{n;p;}' | tr -d '"\n')
[ "$CLEANUP" = "ok" ] && pass "Cleanup: done" || fail "Cleanup: $CLEANUP"
echo ""
echo "--- Console errors ---"
ERRS=$(chrome-devtools list_console_messages --types error 2>/dev/null | grep -c '^msgid=' || true)
[ "$ERRS" -le 5 ] 2>/dev/null && pass "Console errors ≤5 ($ERRS)" || fail "Console errors: $ERRS"
TOTAL=$((PASSED + FAILED))
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
printf "║ %2d / %2d passed, %2d failed ║\n" "$PASSED" "$TOTAL" "$FAILED"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
[ "$FAILED" -gt 0 ] && exit 1 || exit 0