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:
+32
-3
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user