70 lines
3.7 KiB
TypeScript
70 lines
3.7 KiB
TypeScript
import assert from 'node:assert/strict';
|
||
import test from 'node:test';
|
||
import { createElement, isValidElement, type ReactElement, type ReactNode } from 'react';
|
||
import { renderToStaticMarkup } from 'react-dom/server';
|
||
import { TruthFutureBattleLeader } from '../components/truth-future-battle-leader';
|
||
import { createGame, joinGame, newPlayer, viewGame, type GameView } from '../game/engine';
|
||
|
||
type ElementProps = { children?: ReactNode; [key: string]: unknown };
|
||
type Element = ReactElement<ElementProps>;
|
||
function elements(node: ReactNode): Element[] {
|
||
if (Array.isArray(node)) return node.flatMap(elements);
|
||
if (!isValidElement<ElementProps>(node)) return [];
|
||
return [node, ...elements(node.props.children)];
|
||
}
|
||
function control(node: ReactNode, id: string): Element {
|
||
const result = elements(node).find(element => element.props['data-testid'] === id);
|
||
assert.ok(result, `Expected ${id}`);
|
||
return result;
|
||
}
|
||
function projection(): GameView {
|
||
const game = createGame('TRUTHFUTURE', newPlayer('asker', 'Asker', 'atreides'), false);
|
||
joinGame(game, newPlayer('target', 'Target', 'emperor'));
|
||
joinGame(game, newPlayer('other', 'Other', 'harkonnen'));
|
||
return viewGame(game, 'asker');
|
||
}
|
||
|
||
void test('future battle question lists only the target faction’s public native leaders', () => {
|
||
const game = projection();
|
||
const native = game.allLeaders.find(item => item.faction === 'emperor');
|
||
const other = game.allLeaders.find(item => item.faction === 'harkonnen');
|
||
assert.ok(native && other);
|
||
const props = { game, target: 'target', leader: native.id, available: true, busy: false,
|
||
onLeaderChange: (_leader: string) => {}, onAsk: (_target: string, _leader: string) => {} };
|
||
const html = renderToStaticMarkup(createElement(TruthFutureBattleLeader, props));
|
||
assert.match(html, /If you fight a battle later this turn/);
|
||
assert.match(html, /first such battle/);
|
||
assert.match(html, /there is no opportunity/);
|
||
assert.ok(html.includes(native.name));
|
||
assert.ok(!html.includes(other.name));
|
||
assert.equal(control(TruthFutureBattleLeader(props), 'truth-future-battle-leader-ask').props.disabled, false);
|
||
});
|
||
|
||
void test('authoring callbacks reject unavailable, missing and foreign leaders', () => {
|
||
const game = projection();
|
||
const native = game.allLeaders.filter(item => item.faction === 'emperor');
|
||
const foreign = game.allLeaders.find(item => item.faction === 'harkonnen');
|
||
assert.ok(native.length >= 2 && foreign);
|
||
const changes: string[] = [], asks: string[][] = [];
|
||
const props = { game, target: 'target', leader: '', available: true, busy: false,
|
||
onLeaderChange: (id: string) => changes.push(id),
|
||
onAsk: (target: string, id: string) => asks.push([target, id]) };
|
||
const change = (tree: ReactNode, value: string) =>
|
||
(control(tree, 'truth-future-battle-leader-select').props.onChange as (event: { target: { value: string } }) => void)({ target: { value } });
|
||
const ask = (tree: ReactNode) => (control(tree, 'truth-future-battle-leader-ask').props.onClick as () => void)();
|
||
|
||
const empty = TruthFutureBattleLeader(props);
|
||
assert.equal(control(empty, 'truth-future-battle-leader-ask').props.disabled, true);
|
||
change(empty, foreign.id); ask(empty); assert.deepEqual(changes, []); assert.deepEqual(asks, []);
|
||
change(empty, native[1].id); assert.deepEqual(changes, [native[1].id]);
|
||
const selected = TruthFutureBattleLeader({ ...props, leader: native[1].id });
|
||
ask(selected); assert.deepEqual(asks, [['target', native[1].id]]);
|
||
|
||
for (const override of [{ busy: true }, { available: false }, { leader: foreign.id }, { target: 'asker' }]) {
|
||
const tree = TruthFutureBattleLeader({ ...props, leader: native[1].id, ...override });
|
||
assert.equal(control(tree, 'truth-future-battle-leader-ask').props.disabled, true);
|
||
ask(tree);
|
||
}
|
||
assert.equal(asks.length, 1);
|
||
});
|