136 lines
6.8 KiB
JavaScript
136 lines
6.8 KiB
JavaScript
/**
|
|
* TutorialGuide test (dev tool, run with Node — no browser):
|
|
*
|
|
* node dev/tutorial.test.mjs
|
|
*
|
|
* Pins the starter quest's POINTING FINGER beat table
|
|
* (js/quests/TutorialGuide.js — the pure core of the opening-arc
|
|
* guidance, applied per frame by GameScene._applyTutorialHighlight onto
|
|
* ActionBar.setHighlight / ResearchWindow.setHighlight):
|
|
* - the welcome sequence acknowledged (oac-intro → oac-tether →
|
|
* oac-scan all on the run's comms played-ledger) and SCAN not yet
|
|
* pressed → 'scan';
|
|
* - OAC's 'research a Tether Level 2 upgrade' line (oac-upgrade) heard
|
|
* → 'research' (console closed) / 'console' (console open);
|
|
* - the goal node's research in flight / done → REST (null);
|
|
* - missing records and the data's master switches degrade to rest;
|
|
* - the data the beats stand on (data/comms.json's newGame/firstScan
|
|
* triggers) is what the module's constants name.
|
|
* The rim's drawing is the surface's job (ActionBar's update gate —
|
|
* dev/actionbar.test.mjs pins the standby hiding).
|
|
*/
|
|
import fs from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import assert from 'node:assert/strict';
|
|
import { tutorialTarget, WELCOME_COMMS, UPGRADE_COMM } from '../js/quests/TutorialGuide.js';
|
|
|
|
const dataDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'data');
|
|
const comms = JSON.parse(fs.readFileSync(join(dataDir, 'comms.json'), 'utf8'));
|
|
const quests = JSON.parse(fs.readFileSync(join(dataDir, 'quests.json'), 'utf8'));
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// The data the beats stand on — the module's constants name REAL comms,
|
|
// in the triggers' real order (a typo here would be a beat that never
|
|
// arms — the finger would rest where it should be pointing).
|
|
// ---------------------------------------------------------------------------
|
|
{
|
|
const byTrigger = (t) => comms.comms.filter((c) => c.trigger === t).map((c) => c.id);
|
|
assert.deepEqual(
|
|
WELCOME_COMMS,
|
|
byTrigger('newGame'),
|
|
'WELCOME_COMMS = the newGame sequence, in order',
|
|
);
|
|
assert.ok(byTrigger('firstScan').includes(UPGRADE_COMM), 'UPGRADE_COMM is a firstScan comm');
|
|
assert.equal(UPGRADE_COMM, 'oac-upgrade', 'the scan reaction is oac-upgrade');
|
|
// The starter quest's research check is the arc's goal — the tutorial's
|
|
// console beat (category + node) points at the same node.
|
|
const starter = quests.quests.find((q) => q.starter === true);
|
|
const researchCheck = starter.checks.find((c) => c.type === 'research');
|
|
assert.equal(quests.tutorial.console.category, researchCheck.category, 'tutorial category = the starter check\u2019s category');
|
|
assert.equal(quests.tutorial.console.node, researchCheck.node, 'tutorial node = the starter check\u2019s node');
|
|
assert.ok(quests.tutorial.scan.slot, 'the scan beat names a slot');
|
|
assert.ok(quests.tutorial.research.slot, 'the research beat names a slot');
|
|
}
|
|
|
|
// A run's records: the starter granted + the played-ledger as an id set.
|
|
const records = (...ids) => ({
|
|
starterGranted: true,
|
|
hasPlayed: (id) => ids.includes(id),
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fresh run — the comms own the screen (the welcome is mid-sequence).
|
|
// ---------------------------------------------------------------------------
|
|
assert.equal(tutorialTarget({}), null, 'no records → rest');
|
|
assert.equal(tutorialTarget(records()), null, 'a fresh ledger → rest (the comms own the screen)');
|
|
assert.equal(tutorialTarget(records('oac-intro')), null, 'welcome beat one playing → rest');
|
|
assert.equal(tutorialTarget(records('oac-intro', 'oac-tether')), null, 'welcome beat two playing → rest');
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Beat 1 — the welcome is acknowledged: the deck's SCAN slot is the finger.
|
|
// ---------------------------------------------------------------------------
|
|
assert.equal(tutorialTarget(records('oac-intro', 'oac-tether', 'oac-scan')), 'scan', 'welcome ack\u2019d, SCAN unpressed → scan');
|
|
// The player pressed SCAN (the scene's one-shot): the beat is past — the
|
|
// finger rests until oac-upgrade starts (which re-arms it, below).
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan'), scanPressed: true }),
|
|
null,
|
|
'welcome ack\u2019d, SCAN already pressed → rest (the comm is coming)',
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Beat 2 — oac-upgrade is on the ledger (the 'research a Tether Level 2'
|
|
// line): the deck's RESEARCH slot (console closed) or the console itself
|
|
// (console open — the tab's rim, then the node's, by the window).
|
|
// ---------------------------------------------------------------------------
|
|
assert.equal(
|
|
tutorialTarget(records('oac-intro', 'oac-tether', 'oac-scan', 'oac-upgrade')),
|
|
'research',
|
|
'oac-upgrade heard, console closed → research',
|
|
);
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan', 'oac-upgrade'), consoleOpen: true }),
|
|
'console',
|
|
'oac-upgrade heard, console open → console',
|
|
);
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan', 'oac-upgrade'), consoleOpen: true, scanPressed: true }),
|
|
'console',
|
|
'the scan one-shot is irrelevant once the upgrade line is heard',
|
|
);
|
|
// The scan beat's slot and the research beat's slot are the data's own
|
|
// names (GameScene reads them — the pure table just picks the beat).
|
|
assert.notEqual(quests.tutorial.scan.slot, quests.tutorial.research.slot, 'the two deck beats point at different slots');
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// REST — the arc's goal is underway / done: the finger steps off.
|
|
// ---------------------------------------------------------------------------
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan', 'oac-upgrade'), nodeResolving: true }),
|
|
null,
|
|
'the goal research in flight → rest',
|
|
);
|
|
assert.equal(tutorialTarget({ ...records(), nodeResolving: true }), null, 'goal done on a fresh ledger → rest');
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Degradation — missing records / the data's master switches → rest.
|
|
// ---------------------------------------------------------------------------
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan'), starterGranted: false }),
|
|
null,
|
|
'the starter quest not granted → rest',
|
|
);
|
|
assert.equal(
|
|
tutorialTarget({ starterGranted: true }),
|
|
null,
|
|
'no played-ledger to read → rest (never a throw)',
|
|
);
|
|
assert.equal(
|
|
tutorialTarget({ ...records('oac-intro', 'oac-tether', 'oac-scan'), enabled: false }),
|
|
null,
|
|
'the data\u2019s master switch off → rest',
|
|
);
|
|
|
|
console.log('all TutorialGuide checks passed \u2714');
|