import assert from 'node:assert/strict'; import test from 'node:test'; import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { Truthtrance } from '../components/truthtrance'; import { applyAction, viewGame, type Game } from '../game/engine'; import { knowledgeGame } from './truthtrance-knowledge-fixture'; /** Actual native game and Truthtrance actions, with the target's scalar * spice staged to show both personalized result branches. */ function pending(spice: number, text = 'Do you have at least 5 spice?'): Game { let game = knowledgeGame(); game.players.find(player => player.id === 'b')!.spice = spice; const card = game.players[0].hand.find(item => item.effect === 'truthtrance'); assert.ok(card); game = applyAction(game,'a',{type:'card',card:card.id}); while (game.truthtrance?.stage === 'priority') { const voter = game.players.find(player => !game.truthtrance!.passed.includes(player.id)); assert.ok(voter); game = applyAction(game,voter.id,{type:'truthPass'}); } return applyAction(game,'a',{type:'truthAsk',question:{kind:'freeform',target:'b', scope:'fact',text}}); } function markup(game: Game, seat: string) { return renderToStaticMarkup(createElement(Truthtrance, {game:viewGame(game,seat), busy:false, act() {}})); } for (const [spice, answer] of [[5,'Yes'],[4,'No']] as const) { void test(`recognized freeform shows checked meaning to the table and private ${answer} to the target`, () => { const game = pending(spice); const target = markup(game,'b'); const observer = markup(game,'o'); for (const html of [target, observer]) { assert.match(html, /Do you have at least 5 spice\?/); assert.match(html, /Checked as:/); assert.match(html, /you currently hold at least 5 spice/); } assert.match(target, /Your current game information establishes:/); assert.match(target, new RegExp(`${answer}`)); assert.match(target, /Only you see this until you answer/); assert.match(target, /Publish my answer/); assert.doesNotMatch(observer, /Your current game information establishes:/); assert.doesNotMatch(observer, /Publish my answer/); assert.match(observer, /Waiting for .* to answer/); }); } void test('a compound freeform shows its aggregate interpretation to the table', () => { const game = pending(5, 'Do you have at least 5 spice or a Shield?'); const target = markup(game, 'b'), observer = markup(game, 'o'); for (const html of [target, observer]) { assert.match(html, /Checked as:/); assert.match(html, /you currently hold at least 5 spice/); assert.match(html, /OR/); assert.match(html, /Shield/); } assert.match(target, /Yes<\/strong>/); assert.doesNotMatch(observer, /Your current game information establishes:/); }); void test('a category freeform displays its primary-role meaning without exposing the target answer', () => { const game = pending(5, 'Do you have a weapon?'); const target = markup(game, 'b'), observer = markup(game, 'o'); for (const html of [target, observer]) { assert.match(html, /Checked as:/); assert.match(html, /you currently hold at least 1 card whose primary role is a weapon/); } assert.match(target, /Your current game information establishes:/); assert.doesNotMatch(observer, /Your current game information establishes:/); }); void test('a negative freeform displays the checked zero-card meaning without exposing the target answer', () => { const game = pending(5, 'Do you have no Shield?'); const target = markup(game, 'b'), observer = markup(game, 'o'); for (const html of [target, observer]) { assert.match(html, /Checked as:/); assert.match(html, /you currently hold exactly 0 cards named Shield/); } assert.match(target, /Your current game information establishes:/); assert.doesNotMatch(observer, /Your current game information establishes:/); });