57 lines
3.8 KiB
TypeScript
57 lines
3.8 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 { ShipmentClauseFields, invalidShipmentClause, type ShipmentClauseInput } from '../components/shipment-claim-fields';
|
|
import { shipmentExpressionInputError } from '../game/shipment-expression-input';
|
|
import type { ShipmentExpression } from '../game/shipment-promises';
|
|
|
|
type Element = ReactElement<{ children?: ReactNode; [key: string]: unknown }>;
|
|
function elements(node: ReactNode): Element[] {
|
|
if (Array.isArray(node)) return node.flatMap(elements);
|
|
if (!isValidElement<{ children?: ReactNode; [key: string]: unknown }>(node)) return [];
|
|
return [node, ...elements(node.props.children)];
|
|
}
|
|
void test('standalone shipment editor defaults to any sector and accepts only printed sectors', () => {
|
|
let value: ShipmentClauseInput = { territory: 'wind_pass', minimum: 2 };
|
|
const tree = () => ShipmentClauseFields({ id: 'shipment', value, onChange(next) { value = next; } });
|
|
const select = (suffix: string, chosen: string) => {
|
|
const element = elements(tree()).find(item => item.props.id === `shipment-${suffix}`); assert.ok(element);
|
|
(element.props.onChange as (event: { currentTarget: { value: string }; target: { value: string } }) => void)(
|
|
{ currentTarget: { value: chosen }, target: { value: chosen } });
|
|
};
|
|
const html = renderToStaticMarkup(createElement(ShipmentClauseFields, { id: 'shipment', value, onChange() {} }));
|
|
assert.match(html, /Any sector in this territory/);
|
|
assert.match(html, /Sector 14/);
|
|
assert.doesNotMatch(html, /Sector 9/);
|
|
select('sector', '15'); assert.deepEqual(value, { territory: 'wind_pass', minimum: 2, sector: 15 });
|
|
assert.equal(invalidShipmentClause(value), false);
|
|
select('sector', '19'); assert.deepEqual(value, { territory: 'wind_pass', minimum: 2, sector: 15 });
|
|
select('sector', ''); assert.deepEqual(value, { territory: 'wind_pass', minimum: 2 });
|
|
select('sector', '14'); select('territory', 'arrakeen');
|
|
assert.deepEqual(value, { territory: 'arrakeen', minimum: 2 });
|
|
});
|
|
|
|
void test('sector input validation checks the actual territory while preserving incomplete legacy drafts', () => {
|
|
const exact: ShipmentExpression = { territory: 'wind_pass', minimum: 2, sector: 15 };
|
|
assert.equal(shipmentExpressionInputError(exact), null);
|
|
assert.notEqual(shipmentExpressionInputError({ ...exact, sector: 9 }), null);
|
|
assert.equal(invalidShipmentClause({ territory: 'wind_pass', minimum: 2, sector: 9 }), true);
|
|
assert.equal(invalidShipmentClause({ territory: 'wind_pass', minimum: 0 }), true);
|
|
// Structural input checks intentionally allow ordinary fields to be repaired.
|
|
assert.equal(shipmentExpressionInputError({ territory: '', minimum: 0 }), null);
|
|
assert.equal(shipmentExpressionInputError({ op: 'or', terms: [exact, { territory: 'carthag', minimum: 1 }] }), null);
|
|
assert.notEqual(shipmentExpressionInputError({ op: 'and', terms: [exact,
|
|
{ territory: 'wind_pass', minimum: 2, sector: 3 }] }), null);
|
|
});
|
|
|
|
void test('mixed-shipment fields remain on their existing territory-only question interface', () => {
|
|
const mixed = { kind: 'shipment' as const, territory: 'wind_pass', minimum: 2 };
|
|
let result: ShipmentClauseInput = mixed;
|
|
const tree = () => ShipmentClauseFields({ id: 'mixed', value: mixed, onChange(next) { result = next; } });
|
|
assert.equal(elements(tree()).some(item => item.props.id === 'mixed-sector'), false);
|
|
const territory = elements(tree()).find(item => item.props.id === 'mixed-territory'); assert.ok(territory);
|
|
(territory.props.onChange as (event: { target: { value: string } }) => void)({ target: { value: 'arrakeen' } });
|
|
assert.deepEqual(result, { kind: 'shipment', territory: 'arrakeen', minimum: 2 });
|
|
});
|