Files
foundryvtt-reve-de-dragon/tests/scripts/tc-06-acteur-commerce.sh
T
fabres 1272cfdfd1 test: ajout plan de test et scripts TC-01 a TC-06
- rdd_test_plan.md : plan global, 227 cas, 33 sections
- TC-01 : infrastructure coeur (15/15)
- TC-02 : acteur personnage (7/7)
- TC-03 : acteur creature (11/11)
- TC-04 : acteur entite + subtypes + possession (14/14)
- TC-05 : acteur vehicule + structure/resistance (10/10)
- TC-06 : acteur commerce + achat/vente simules (14/14)

Scripts atomiques, async polling, page-state race-free
2026-07-24 08:08:04 +02:00

225 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
# ============================================================================
# TC-06 — Acteur Commerce
# Usage: ./tc-06-acteur-commerce.sh [--navigate]
# --navigate : force navigation + login
# Env: URL, PASS (admin password)
#
# Atomic single evaluate_script: no cross-call races.
# ============================================================================
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-06 — Acteur Commerce ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
R=$(jse_wait_ready 15 2)
if [ "$R" != "ready" ] && [ "$NAVIGATE" = "--navigate" ]; then
[ -z "$PASS" ] && die "PASS=..."
echo "→ Navigating ..."
chrome-devtools new_page "$URL" --background false --timeout 15000 --isolatedContext "rdd-tc06" >/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 ;;
*/auth) chrome-devtools evaluate_script "() => {
const i=document.querySelector('input[type=password]'); if(i) i.value='$PASS';
const b=document.querySelector('form button,button[type=submit]'); if(b) b.click();
}" >/dev/null 2>&1 || true ;;
esac
echo "→ Waiting..."; R=$(jse_wait_ready 60 2)
fi
[ "$R" != "ready" ] && die "Game not ready"
echo " ✓ Game ready"; echo ""
# ============================================================================
# Single atomic evaluate_script: all sections in one call
# ============================================================================
RESULT=$(chrome-devtools evaluate_script "async () => {
if (typeof game === 'undefined' || !game.ready) return {error: 'NOT_READY'};
const out = {};
// ==============================
// Cleanup any previous TC-06 actors
// ==============================
for (const n of ['TC-06 Marchand', 'TC-06 Acheteur']) {
const a = game.actors.getName(n);
if (a) await a.delete();
}
// ==============================
// TC-06-001: Commerce creation
// ==============================
try {
const [actor] = await Actor.createDocuments([{type: 'commerce', name: 'TC-06 Marchand'}]);
await actor.createEmbeddedDocuments('Item', [
{name: 'Denier (etain)', type: 'monnaie', system: {cout: 0.01, quantite: 0}},
{name: 'Sou (bronze)', type: 'monnaie', system: {cout: 0.05, quantite: 0}},
{name: 'Sol (argent)', type: 'monnaie', system: {cout: 1, quantite: 0}},
{name: 'Dragon (or)', type: 'monnaie', system: {cout: 240, quantite: 10}}
]);
const sheet = await actor.sheet.render(true);
await new Promise(r => setTimeout(r, 800));
out.sheetName = sheet.constructor.name;
await sheet.close();
out.monnaieCount = actor.items.filter(i => i.type === 'monnaie').length;
out.pourcentage = actor.system.pourcentage;
out.c001_1 = (out.sheetName === 'RdDCommerceSheetV2');
out.c001_2 = (out.monnaieCount >= 4);
out.c001_3 = (out.pourcentage === 100);
out.commerceId = actor.id;
} catch(e) { out.c001_error = e?.message || String(e); }
// ==============================
// TC-06-002: Purchase simulation
// ==============================
try {
const commerce = game.actors.get(out.commerceId);
if (!commerce) { out.c002_error = 'Commerce not found'; }
else {
const [item] = await commerce.createEmbeddedDocuments('Item', [{
name: 'Epee longue', type: 'objet',
system: { cout: 50, quantite: 3, encombrement: 2 }
}]);
out.prixCalcule = commerce.calculerPrix(item);
out.quantiteDispo = commerce.getQuantiteDisponible(item);
const [buyer] = await Actor.createDocuments([{type: 'personnage', name: 'TC-06 Acheteur'}]);
await buyer.createEmbeddedDocuments('Item', [{
name: 'Dragon (or)', type: 'monnaie', system: { cout: 240, quantite: 5 }
}]);
out.peutPayer = buyer.verifierFortune(out.prixCalcule);
const reste = await buyer.depenserSols(out.prixCalcule);
out.resteApres = reste;
out.c002_payeurResteOk = (reste >= 0);
await commerce.ajouterSols(out.prixCalcule);
await commerce.decrementerQuantiteItem(item.id, 1);
await buyer.createEmbeddedDocuments('Item', [{
name: item.name, type: item.type,
system: { cout: item.system.cout, quantite: 1, encombrement: item.system.encombrement }
}]);
out.itemTransfere = buyer.items.filter(i => i.name === 'Epee longue').length > 0;
out.c002_1 = (out.prixCalcule === 50);
out.c002_2 = (out.quantiteDispo === 3);
out.c002_3 = (out.peutPayer === true);
out.c002_4 = (out.c002_payeurResteOk === true);
out.c002_5 = (!(reste >= 0) || (item.system.cout === 50 && out.c002_payeurResteOk));
out.c002_6 = (out.itemTransfere === true);
}
} catch(e) { out.c002_error = e?.message || String(e); }
// ==============================
// TC-06-003: Sell simulation
// ==============================
try {
const commerce = game.actors.getName('TC-06 Marchand');
const buyer = game.actors.getName('TC-06 Acheteur');
if (!commerce || !buyer) { out.c003_error = 'actors not found for TC-06-003'; }
else {
const [item] = await buyer.createEmbeddedDocuments('Item', [{
name: 'Potion de soin', type: 'potion',
system: { cout: 30, quantite: 2, encombrement: 1 }
}]);
out.prixItem = item.calculerPrixCommercant();
out.prixVente = Math.floor(out.prixItem / 2);
out.commerceSuffisant = commerce.verifierFortune(out.prixVente);
const fortuneVendeurAvant = buyer.getFortune();
const depenseOk = await commerce.depenserSols(out.prixVente);
await buyer.ajouterSols(out.prixVente);
const fortuneVendeurApres = buyer.getFortune();
out.gainReel = fortuneVendeurApres - fortuneVendeurAvant;
out.c003_1 = (typeof out.prixItem === 'number' && out.prixItem > 0);
out.c003_2 = (typeof out.prixVente === 'number' && out.prixVente > 0);
out.c003_3 = (out.commerceSuffisant === true);
out.c003_4 = (out.gainReel === out.prixVente);
}
} catch(e) { out.c003_error = e?.message || String(e); }
// ==============================
// Cleanup
// ==============================
for (const n of ['TC-06 Marchand', 'TC-06 Acheteur']) {
const a = game.actors.getName(n);
if (a) await a.delete();
}
out.cleaned = true;
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-06-001: Commerce creation + sheet ---"
[ "$(eval_ok c001_1)" = "True" ] && pass "TC-06-001a: Sheet $(eval_ok sheetName)" || fail "TC-06-001a: Sheet = $(eval_ok sheetName) (expected RdDCommerceSheetV2)"
[ "$(eval_ok c001_2)" = "True" ] && pass "TC-06-001b: $(eval_ok monnaieCount) monnaies" || fail "TC-06-001b: monnaies = $(eval_ok monnaieCount) (expected ≥4)"
[ "$(eval_ok c001_3)" = "True" ] && pass "TC-06-001c: pourcentage = $(eval_ok pourcentage)" || fail "TC-06-001c: pourcentage = $(eval_ok pourcentage)"
[ -n "$(eval_ok c001_error)" ] && fail "TC-06-001: $(eval_ok c001_error)"
echo ""
echo "--- TC-06-002: Achat (simulation) ---"
[ "$(eval_ok c002_1)" = "True" ] && pass "TC-06-002a: prix = $(eval_ok prixCalcule) sols" || fail "TC-06-002a: prix = $(eval_ok prixCalcule)"
[ "$(eval_ok c002_2)" = "True" ] && pass "TC-06-002b: quantite = $(eval_ok quantiteDispo)" || fail "TC-06-002b: quantite = $(eval_ok quantiteDispo)"
[ "$(eval_ok c002_3)" = "True" ] && pass "TC-06-002c: peut payer $(eval_ok prixCalcule) sols" || fail "TC-06-002c: verifierFortune = $(eval_ok peutPayer)"
[ "$(eval_ok c002_4)" = "True" ] && pass "TC-06-002d: depenserSols (reste=$(eval_ok resteApres))" || fail "TC-06-002d: depenserSols returned $(eval_ok resteApres)"
[ "$(eval_ok c002_6)" = "True" ] && pass "TC-06-002e: item transfere" || fail "TC-06-002e: item transfere = $(eval_ok itemTransfere)"
[ -n "$(eval_ok c002_error)" ] && fail "TC-06-002: $(eval_ok c002_error)"
echo ""
echo "--- TC-06-003: Vente (simulation) ---"
[ "$(eval_ok c003_1)" = "True" ] && pass "TC-06-003a: prix item = $(eval_ok prixItem) sols" || fail "TC-06-003a: prix item = $(eval_ok prixItem)"
[ "$(eval_ok c003_2)" = "True" ] && pass "TC-06-003b: prix vente = $(eval_ok prixVente) sols" || fail "TC-06-003b: prix vente = $(eval_ok prixVente)"
[ "$(eval_ok c003_3)" = "True" ] && pass "TC-06-003c: commerce peut payer $(eval_ok prixVente)" || fail "TC-06-003c: commerceSuffisant = $(eval_ok commerceSuffisant)"
GE=$(eval_ok gainReel); PV=$(eval_ok prixVente)
[ -n "$GE" ] && [ "$GE" -ge "$PV" ] 2>/dev/null && pass "TC-06-003d: vendeur recoit $GE sols (prix vente $PV)" || fail "TC-06-003d: gain = $GE (expected >= $PV)"
[ -n "$(eval_ok c003_error)" ] && fail "TC-06-003: $(eval_ok c003_error)"
echo ""
echo "--- Cleanup ---"
[ "$(eval_ok cleaned)" = "True" ] && pass "Cleanup: done" || fail "Cleanup: not run"
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