Files
Dune/tests/truthtrance-force-controls.test.tsx
T
Quintine 89589878fc
Verify and publish checkpoint container / container (push) Canceled after 0s
Support board-wide Truthtrance force questions
2026-10-29 03:41:29 +11:00

76 lines
4.2 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { ForceCountFields, forceCountInputError } from '../components/truthtrance-force-count';
import type { ForceCountFact, ForceCountPlayer } from '../game/truthtrance-force-count';
import type { MobileBoard } from '../game/board';
const fact: ForceCountFact = { kind: 'forceCount', zone: { kind: 'reserves' }, counter: 'total', compare: 'gte', value: 6 };
const player: Pick<ForceCountPlayer, 'faction' | 'elites'> = { faction: 'atreides' };
function markup(value = fact, game: MobileBoard = {}, target = player) {
return renderToStaticMarkup(createElement(ForceCountFields, { value, game, player: target, onChange() {} }));
}
void test('force fact controls name physical pools and comparisons without revealing a hidden marker value', () => {
const html = markup();
for (const value of ['reserves', 'tanks', 'board', 'territory', 'location', 'total', 'normal', 'elite', 'eq', 'gte', 'lte'])
assert.match(html, new RegExp(`value="${value}"`));
assert.match(html, /Number of force counters/);
assert.match(html, /Each physical counter counts once, including advisors/);
assert.match(html, /Concealed No-Field markers and their hidden values do not count/);
assert.match(html, /Reserves includes counters on native Homeworlds/);
assert.match(html, /min="0"/); assert.match(html, /max="9007199254740991"/);
});
void test('the whole board choice needs no selected territory or sector', () => {
const board: ForceCountFact = { ...fact, zone: { kind: 'board' } };
assert.equal(forceCountInputError(board, {}, player), null);
const html = markup(board);
assert.match(html, /Entire Arrakis board/);
assert.doesNotMatch(html, /Force territory/);
assert.doesNotMatch(html, /Force sector/);
});
void test('whole-territory choice names the territory without requiring a sector', () => {
const whole: ForceCountFact = { ...fact, zone: { kind: 'territory', territory: 'wind_pass' } };
assert.equal(forceCountInputError(whole, {}, player), null);
const html = markup(whole);
assert.match(html, /Entire board territory/);
assert.match(html, /Force territory/);
assert.match(html, /Wind Pass/);
assert.doesNotMatch(html, /Force sector/);
assert.ok(forceCountInputError({ ...whole, zone: { kind: 'territory', territory: 'hidden_mobile_stronghold' } }, {}, player));
});
void test('location choices use the room board and the selected territory sectors', () => {
const html = markup({ ...fact, zone: { kind: 'location', territory: 'carthag', sector: 11 } });
assert.match(html, /Force territory/); assert.match(html, /Carthag/);
assert.match(html, /Force sector/); assert.match(html, /value="11" selected/);
assert.doesNotMatch(html, /value="hidden_mobile_stronghold"/);
assert.doesNotMatch(html, /value="jacurutu"/);
assert.ok(forceCountInputError({ ...fact, zone: { kind: 'location', territory: 'carthag', sector: 99 } }, {}, player));
});
void test('an absent special-counter split disables typed comparisons without inventing counts', () => {
const basic = { faction: 'emperor' } as const;
assert.equal(forceCountInputError(fact, {}, basic), null);
assert.ok(forceCountInputError({ ...fact, counter: 'elite' }, {}, basic));
const html = markup({ ...fact, counter: 'elite' }, {}, basic);
assert.match(html, /value="normal" disabled/); assert.match(html, /value="elite" disabled/);
assert.match(html, /role="alert"/); assert.match(html, /does not track this faction/);
const split = { faction: 'emperor' as const, elites: { reserves: 3, tanks: 1, forces: {} } };
assert.equal(forceCountInputError({ ...fact, counter: 'elite' }, {}, split), null);
assert.doesNotMatch(markup({ ...fact, counter: 'elite' }, {}, split), /role="alert"/);
});
void test('invalid numeric input stays visible and blocks the shared form validation', () => {
for (const value of [-1, 0.5, NaN, Infinity, Number.MAX_SAFE_INTEGER + 1]) {
const input = { ...fact, value };
assert.ok(forceCountInputError(input, {}, player));
assert.match(markup(input), /aria-invalid="true"/);
assert.match(markup(input), /role="alert"/);
}
assert.equal(forceCountInputError({ ...fact, value: 0 }, {}, player), null);
});