76 lines
4.2 KiB
TypeScript
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);
|
|
});
|