Framework in codice
Testare con gli scenari
Costruisci un Elemento, esegui azioni e controlla ogni valore e ogni scrittura.
Cosa contiene uno scenario
Uno scenario è un file JSON in scenarios/. Costruisce un Elemento dagli input, esegue le azioni in ordine e controlla i risultati. Il manifest elenca ogni file di scenario, e ogni file contiene uno scenario.
{
"id": "heal-after-damage",
"framework": "keeper",
"parameters": { "name": "Mira", "max_hp": 10 },
"expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
"actions": [
{
"action": "hurt",
"parameters": { "amount": 6 },
"expectedWrites": [{ "op": "ledger.add", "key": "hp", "value": -6 }]
},
{
"action": "heal",
"parameters": { "amount": 9 },
"expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 10 }]
},
{ "action": "heal", "parameters": { "amount": 3 }, "expectedWrites": [] }
]
}- Righe 4-5 Costruisci l'Elemento da questi input e controlla i suoi valori iniziali.
- Riga 6 actions elenca i passi da eseguire, in ordine.
- Riga 10 Le scritture esatte che il passo deve fare.
- Riga 17 Nessuna scrittura: nulla è cambiato.
| Campo | Significato |
|---|---|
| id | Il nome dello scenario. |
| framework | L'id del Framework da costruire. |
| parameters | Gli input con cui costruirlo. |
| expectedValues | Ogni valore salvato subito dopo la costruzione. |
| expectedChildren | I valori di ogni slot figlio. |
| actions | I passi da eseguire, al massimo 32 per scenario. Ognuno ha un'azione, i suoi parametri e expectedWrites. |
| ledger | Controlli dei valori derivati. |
| lists | Riempie un elenco con voci, tramite l'id del preset. |
| expectedLists | Le voci che ogni elenco contiene alla fine, tramite l'id del preset. |
| poolRolls | Tiri registrati per un pool che usa il metodo roll, per id del pool. Vedi Punteggi tirati. |
| content | Voci di contenuto fissate all'Element costruito (kind, entryId, name e parameters), unite alla release come quelle fissate a un personaggio. |
Un passo può usare altri campi:
| Campo del passo | Significato |
|---|---|
| choose | Per un'azione su elenco: la voce che il giocatore sceglie per ogni input ContentRef, tramite l'id del preset. |
| expectedRefusal | Il rifiuto con cui il passo deve finire. Il passo allora non si aspetta scritture. |
| expectedValuesAfter | I valori salvati e derivati dopo il passo. I valori derivati vengono prima ricalcolati. |
Scritture
expectedWrites elenca ogni scrittura che il passaggio fa, in ordine. Usa ledger.set per un valore impostato e ledger.add per una quantità aggiunta. Un passaggio che non cambia nulla non elenca scritture, così si testa anche la guardia che salta una scrittura.
Se le scritture reali sono diverse, la verifica mostra entrambi gli elenchi.
"expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 9 }]Expected ordered writes [{"op":"ledger.set","key":"hp","value":9}]; observed [{"op":"ledger.set","key":"hp","value":10}].Valori derivati e figli
L'elenco ledger controlla una funzione del registro. Indica l'id della funzione, il valore da cui parte e il valore che deve restituire. expectedChildren controlla gli Elementi figli.
"expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } },- Riga 17 Controlla gli Elementi negli slot figlio.
"ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }]- Riga 18 Chiama la funzione di guardia con il valore attuale 0 e si aspetta 3.
Cosa deve essere coperto
- Ogni costruttore, azione e funzione del registro richiede uno scenario.
- Ogni azione richiede almeno un passo. Aggiungi un passo per ogni ramo che vuoi dimostrare, per esempio uno che scrive e uno che non scrive nulla.
- Un'azione che aggiunge a un elenco richiede un passo accettato e un passo con expectedRefusal, qualunque cosa dica la sua offerta. Il passo rifiutato dimostra che l'offerta può dire no. Un'azione che rimuove soltanto da un elenco richiede un passo accettato.
- I valori attesi devono corrispondere a ciò che il costruttore restituisce davvero, con le funzioni del registro già applicate.
I passi prendono il nome dal loro scenario. Un messaggio come heal-after-damage-2 indica il terzo passo di heal-after-damage, perché il numero parte da 0. Gli altri messaggi contano i passi da 1.
Un passo che si aspetta un rifiuto da un'azione che non è stata rifiutata fallisce, e lo stesso vale al contrario:
the step expects it to be refused with NOT_OFFEREDAdd expectedRefusalUn'azione senza passi fallisce con:
SELF_SCALAR_ACTION_SCENARIO_COVERAGE_MISSING: action keeper.hurt (Take damage) has no executable scenario steps.Un'azione su elenco a cui manca un passo accettato o rifiutato fallisce con:
add a step with expectedRefusal, so its refused branch is proven.add a step whose list.add is accepted, so its accepted branch is proven.Valori attesi diversi da quelli del costruttore falliscono con:
Constructor output does not match its scenario.Rifiuti
Un'azione su elenco può essere rifiutata. Un passo che se lo aspetta indica il codice in expectedRefusal e non elenca scritture.
| Codice | Quando |
|---|---|
| NOT_OFFERED | La voce scelta non è tra quelle che l'offerta ammette. |
| ALREADY_IN_LIST | L'elenco contiene già la voce. |
| LIST_FULL | L'elenco ha raggiunto la dimensione massima. |
| NOT_IN_LIST | list.remove indica una voce che l'elenco non contiene. |
| LIST_CAP | Verrebbe superato un limite dichiarato dall'elenco. |
| SAME_NAME | L'elenco contiene già una voce con quel nome. |
Questo progetto ne dimostra diversi. Ha anche un passo in cui una guardia ferma l'azione, quindi il passo non si aspetta né scritture né rifiuti.
{
"id": "pack-trinkets",
"framework": "keeper",
"parameters": { "name": "Mira", "max_hp": 10 },
"expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
"actions": [
{
"action": "pack_trinket",
"parameters": {},
"choose": { "trinket": "rope" },
"expectedWrites": [{ "op": "list.add", "key": "pack", "value": "rope" }]
},
{
"action": "pack_trinket",
"parameters": {},
"choose": { "trinket": "rope" },
"expectedRefusal": "ALREADY_IN_LIST",
"expectedWrites": []
},
{
"action": "pack_trinket",
"parameters": {},
"choose": { "trinket": "anvil" },
"expectedRefusal": "NOT_OFFERED",
"expectedWrites": []
},
{
"action": "pack_trinket",
"parameters": {},
"choose": { "trinket": "candle" },
"expectedWrites": [
{ "op": "list.add", "key": "pack", "value": "candle" }
]
},
{
"action": "pack_trinket",
"parameters": {},
"choose": { "trinket": "bell" },
"expectedWrites": []
},
{
"action": "unpack_trinket",
"parameters": {},
"choose": { "trinket": "rope" },
"expectedWrites": [
{ "op": "list.remove", "key": "pack", "value": "rope" }
]
},
{
"action": "unpack_trinket",
"parameters": {},
"choose": { "trinket": "rope" },
"expectedRefusal": "NOT_IN_LIST",
"expectedWrites": []
}
],
"expectedLists": { "pack": ["candle"] }
}- Righe 7-12 Accettato: list.add scrive la corda.
- Righe 13-19 Rifiutato: l'elenco contiene già la corda.
- Righe 20-26 Rifiutato: l'offerta non ammette l'incudine.
- Righe 35-40 Lo zaino contiene già due voci, quindi la guardia non scrive nulla e non ci si aspetta un rifiuto.
- Righe 41-48 Rimosso: la corda esce dall'elenco.
- Righe 49-55 Rifiutato: la corda non è più nell'elenco.
- Riga 57 Lo zaino finisce con la sola candela.
Eseguirli
Verifica e anteprima in The Forge compila il progetto ed esegue ogni scenario. Non viene pubblicato nulla e nessun personaggio attivo cambia.

