72 lines
3.7 KiB
TypeScript
72 lines
3.7 KiB
TypeScript
import test from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { createElement } from 'react';
|
||
import { renderToStaticMarkup } from 'react-dom/server';
|
||
import { MoritaniEntry, MoritaniTerrorSupply } from '../components/moritani-terror';
|
||
import { TerrorBoardMarkers } from '../components/terror-board-markers';
|
||
import { createGame, joinGame, newPlayer, viewGame, type GameView } from '../game/engine';
|
||
import { createTerrorState, placeTerror, revealTerror } from '../game/moritani-terror';
|
||
|
||
function fixture() {
|
||
const game = createGame('ATOMICS-CONTROLS', newPlayer('m', 'Moritani', 'moritani'));
|
||
joinGame(game, newPlayer('a', 'Atreides', 'atreides'));
|
||
joinGame(game, newPlayer('f', 'Fremen', 'fremen'));
|
||
game.players.find((player) => player.id === 'm')!.ally = 'a';
|
||
game.players.find((player) => player.id === 'a')!.ally = 'm';
|
||
game.moritaniTerror = createTerrorState(() => 0);
|
||
const token = game.moritaniTerror.tokens.find((item) => item.kind === 'atomics')!;
|
||
game.moritaniTerror = placeTerror(game.moritaniTerror, token.id, 'arrakeen', 1);
|
||
return { game, token };
|
||
}
|
||
|
||
const supply = (view: GameView) =>
|
||
renderToStaticMarkup(createElement(MoritaniTerrorSupply, { game: view }));
|
||
const markers = (view: GameView) =>
|
||
renderToStaticMarkup(createElement('svg', null, createElement(TerrorBoardMarkers, {
|
||
tokens: view.moritaniTerror?.tokens ?? [], atomics: view.moritaniAtomics,
|
||
})));
|
||
|
||
void test('a placed hidden face stays private; only owner sees the Atomics reveal consequences', () => {
|
||
const { game } = fixture();
|
||
const owner = viewGame(game, 'm');
|
||
owner.decision = { kind: 'moritaniTerror', player: 'm', entrant: 'f', territory: 'arrakeen' };
|
||
owner.terrorEntry = {
|
||
entrant: 'f', territory: 'arrakeen', sector: 10, cause: 'shipment', stage: 'offer',
|
||
kind: 'atomics', canReveal: true, canOfferAlliance: false,
|
||
};
|
||
const offer = renderToStaticMarkup(createElement(MoritaniEntry, { game: owner, busy: false, act() {} }));
|
||
assert.match(offer, /Reveal Atomics/);
|
||
assert.match(offer, /every faction’s forces/);
|
||
assert.match(offer, /including Fremen reinforcements/);
|
||
assert.match(offer, /discards one random card per excess/);
|
||
const rival = viewGame(game, 'f');
|
||
assert.match(supply(rival), /Inspect Hidden Terror/);
|
||
assert.doesNotMatch(supply(rival), /Inspect Atomics|Atomics Aftermath/);
|
||
assert.match(markers(rival), /hidden Terror token in Arrakeen/);
|
||
assert.doesNotMatch(markers(rival), /Atomics Aftermath/);
|
||
assert.equal(renderToStaticMarkup(createElement(MoritaniEntry, { game: rival, busy: false, act() {} })), '');
|
||
});
|
||
|
||
void test('after revelation all seats see persistent Aftermath and its exact shipment and hand-limit consequences', () => {
|
||
const { game, token } = fixture();
|
||
game.moritaniTerror = revealTerror(game.moritaniTerror!, token.id);
|
||
game.moritaniAtomics = { territory: 'arrakeen', turn: 2, moritaniId: 'm',
|
||
allyAtActivation: 'a', alliancePolicy: 'unresolved' };
|
||
game.turn = 2;
|
||
game.players.find((player) => player.id === 'm')!.atomicsHandLimitPenalty = true;
|
||
game.players.find((player) => player.id === 'a')!.atomicsHandLimitPenalty = true;
|
||
for (const id of ['m', 'a', 'f']) {
|
||
const view = viewGame(game, id);
|
||
const notice = supply(view);
|
||
assert.match(notice, /Atomics Aftermath · Arrakeen/);
|
||
assert.match(notice, /From turn 2, no faction may ship forces/);
|
||
assert.match(notice, /including Fremen reinforcements/);
|
||
assert.match(notice, /ordinary movement and Sneak Attack remain possible/);
|
||
assert.match(notice, /Moritani and Atreides/);
|
||
assert.match(notice, /lasting hand-limit reduction of one/);
|
||
const board = markers(view);
|
||
assert.match(board, /Atomics Aftermath in Arrakeen: permanent shipment ban/);
|
||
assert.doesNotMatch(board, /hidden Terror token in Arrakeen/);
|
||
}
|
||
});
|