Bind current Traitor declarations with Truthtrance

This commit is contained in:
Quintine committed 2026-10-25 19:23:14 +11:00
1 parent 724eabf99c
commit 86608f7ea9
20 files changed
+1362 -35

No files matched your search

@@ -0,0 +1,41 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { createElement, type ReactElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { Truthtrance } from '../components/truthtrance';
import { TraitorDecisionAnswers, TraitorDecisionPromises } from '../components/traitor-decision-promises';
import { viewGame, type Action } from '../game/engine';
import { createTruthTraitorDecisionFixture as fixture, actTruthTraitorDecision as act,
askTruthTraitorDecision as ask } from './fixture-truth-traitor-decision';
function callbacks(node: unknown): (() => void)[] {
if (Array.isArray(node)) return node.flatMap(callbacks);
if (!node || typeof node !== 'object' || !('props' in node)) return [];
const props = (node as ReactElement<{ children?: unknown; onClick?: () => void }>).props;
return [...(props.onClick ? [props.onClick] : []), ...callbacks(props.children)];
}
void test('actual current-decision answer controls and persistent declaration notice explain later prevention', () => {
const f = fixture(); let g = ask(f.game, f.asker, f.truth, f.target);
const html = renderToStaticMarkup(createElement(Truthtrance,
{ game: viewGame(g, f.target), busy: false, act() {} }));
assert.match(html, /Answer Yes/); assert.match(html, /Answer No/);
assert.match(html, /later Karama or betrayal response/);
const actions: Action[] = [], props = { game: viewGame(g, f.target), busy: false, act: (action: Action) => { actions.push(action); } };
callbacks(TraitorDecisionAnswers(props))[1]();
assert.deepEqual(actions, [{ type: 'truthAnswer', answer: 'yes' }]);
callbacks(TraitorDecisionAnswers({ ...props, busy: true }))[1](); assert.equal(actions.length, 1);
g = act(g, f.target, actions[0]);
const notice = renderToStaticMarkup(createElement(TraitorDecisionPromises, { game: viewGame(g, f.target) }));
assert.match(notice, /requires Reveal traitor/);
assert.match(notice, /original declaration fulfills your answer/);
assert.equal(TraitorDecisionPromises({ game: viewGame(g, f.asker) }), null);
});
void test('only the current declared battle has the new question authoring choice', () => {
const f = fixture(); let g = act(f.game, f.asker, { type: 'card', card: f.truth });
for (const p of g.players) if (!g.truthtrance!.passed.includes(p.id)) g = act(g, p.id, { type: 'truthPass' });
const html = renderToStaticMarkup(createElement(Truthtrance, { game: viewGame(g, f.asker), busy: false, act() {} }));
assert.match(html, /Bind this battle’s Traitor declaration/);
assert.ok(viewGame(g, f.asker).truthTraitorTargets.includes(f.target));
});