import assert from 'node:assert/strict'; import test from 'node:test'; import { Children, createElement, isValidElement, type ButtonHTMLAttributes, type MouseEvent, type ReactElement, type ReactNode } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { ShipmentPromises } from '../components/shipment-promises'; import { viewGame, type Action, type GameView } from '../game/engine'; import { deployRicheseNoField } from '../game/richese-no-field'; import type { TruthQuestion } from '../game/truthtrance'; import { createTruthtranceRichesePromisesFixture as fixture, actTruthtranceRichesePromises as act, askTruthtranceRichesePromises as ask, advanceTruthtranceRichesePromises as advance, truthtranceRichesePromisesClean as clean, truthtranceRichesePromisesPlayer as player, } from './fixture-truthtrance-richese-promises'; const question: TruthQuestion = { kind: 'shipment', target: 'richese', claim: {kind: 'noField', territory: 'carthag', sector: 11}, }; /** Genuine public setup and Truthtrance action; the optional existing marker * is staged in conserved custody to represent a previous turn. */ function promised(existing: boolean) { const f = fixture(); const source = structuredClone(f.game); if (existing) { const owner = player(source), marker = owner.noField!.tokens.find(token => token.value === 3); assert.ok(marker); owner.noField = deployRicheseNoField(owner.noField!, { tokenId: marker.id, controller: owner.id, location: {territory:'polar_sink', sector:0}, }); } let game = ask(source, f, question); game = act(game, f.target, {type:'truthAnswer', answer:'yes'}); game = advance(game, state => state.phase === 5 && state.active === f.target && clean(state)); return {f, game}; } function button(game: GameView, actions: Action[], label: string) { function find(node: ReactNode): ReactElement> | undefined { if (!isValidElement<{children?: ReactNode; onClick?: unknown}>(node)) return; if (node.props.children === label && typeof node.props.onClick === 'function') return node as ReactElement>; return Children.toArray(node.props.children).map(find).find(Boolean); } const found = find(ShipmentPromises({game, busy:false, act: action => actions.push(action)})); assert.ok(found, label); found.props.onClick!({} as MouseEvent); } function html(game: GameView) { return renderToStaticMarkup(createElement(ShipmentPromises, {game, busy:false, act() {}})); } void test('a real No-Field promise offers a correctly priced one-marker shipment', () => { const {f, game} = promised(false), projected = viewGame(game, f.target); const witness = projected.shipmentCompletion?.actions[0]; assert.equal(witness?.type, 'ship'); assert.ok(witness.noField); const output = html(projected); assert.match(output, /Ship your No-Field to Carthag, sector 11/); assert.match(output, /Cost: 1 spice/); assert.doesNotMatch(output, /NaN|Ship 1 forces|Play your preparation card/); const sent: Action[] = []; button(projected, sent, 'Ship your No-Field to Carthag, sector 11'); assert.deepEqual(sent, [witness]); const done = advance(act(game, f.target, sent[0]), clean); assert.equal(done.shipmentPromises![0].fulfilled, true); assert.equal(player(done).noField?.deployed?.tokenId, witness.noField); }); void test('an existing marker is labeled as a reveal and the next shipment uses its fresh event', () => { const {f, game: original} = promised(true), projected = viewGame(original, f.target); const first = projected.shipmentCompletion?.actions[0]; assert.equal(first?.type, 'revealNoField'); const output = html(projected); assert.match(output, /Reveal your current No-Field/); assert.doesNotMatch(output, /NaN|Play your preparation card|Cost:/); const sent: Action[] = []; button(projected, sent, 'Reveal your current No-Field'); assert.deepEqual(sent, [first]); const revealed = act(original, f.target, sent[0]); const next = viewGame(revealed, f.target).shipmentCompletion?.actions[0]; assert.equal(next?.type, 'ship'); assert.notEqual(next?.noField, first?.token); assert.equal(next?.event, player(revealed).noFieldEvent); const done = advance(act(revealed, f.target, next!), clean); assert.equal(done.shipmentPromises![0].fulfilled, true); assert.equal(player(done).noField?.deployed?.tokenId, next?.noField); });