84 lines
3.5 KiB
TypeScript
84 lines
3.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { createElement } from 'react';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import { SemutaReaction } from '../components/semuta-reaction';
|
|
import type { GameView } from '../game/engine';
|
|
|
|
const first = { id: 'fresh-projectile-1', name: 'Projectile', kind: 'projectile' as const };
|
|
const second = { id: 'fresh-projectile-2', name: 'Projectile', kind: 'projectile' as const };
|
|
const event = 'discard-turn-7';
|
|
|
|
type Reaction = NonNullable<GameView['semutaReaction']>;
|
|
|
|
function view(reaction: Reaction | null): GameView {
|
|
return { semutaReaction: reaction } as GameView;
|
|
}
|
|
|
|
function offer(overrides: Partial<Reaction> = {}): Reaction {
|
|
return {
|
|
event,
|
|
stage: 'offer',
|
|
passed: false,
|
|
canCommit: false,
|
|
blocked: null,
|
|
candidates: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function markup(reaction: Reaction | null, busy = false) {
|
|
return renderToStaticMarkup(
|
|
createElement(SemutaReaction, { game: view(reaction), act() {}, busy }),
|
|
);
|
|
}
|
|
|
|
void test('all seats see the same fresh-discard event without a card face before commitment', () => {
|
|
const holder = markup(offer({ canCommit: true }));
|
|
const rival = markup(offer());
|
|
assert.match(holder, /Fresh Treachery discard/);
|
|
assert.match(rival, /Fresh Treachery discard/);
|
|
assert.match(holder, /discard-turn-7/);
|
|
assert.match(rival, /discard-turn-7/);
|
|
assert.match(holder, /Commit Semuta Drug/);
|
|
assert.doesNotMatch(rival, /Commit Semuta Drug/);
|
|
assert.match(holder, />Continue<\/button>/);
|
|
assert.match(rival, />Continue<\/button>/);
|
|
for (const html of [holder, rival]) {
|
|
assert.doesNotMatch(html, /Projectile|fresh-projectile|Inspect card|Claim Projectile/);
|
|
}
|
|
assert.equal(markup(null), '');
|
|
});
|
|
|
|
void test('only the claimant sees exact physical candidates after commitment', () => {
|
|
const selecting = offer({ stage: 'select', candidates: [first, second] });
|
|
const holder = markup(selecting);
|
|
const rival = markup(offer({ stage: 'select' }));
|
|
for (const id of [first.id, second.id]) {
|
|
assert.match(holder, new RegExp(id));
|
|
assert.doesNotMatch(rival, new RegExp(id));
|
|
}
|
|
assert.equal((holder.match(/Claim Projectile \(/g) ?? []).length, 2);
|
|
assert.match(holder, /Inspect card/);
|
|
assert.match(holder, /cannot be declined or canceled/);
|
|
assert.doesNotMatch(holder, /Continue|Cancel|Decline/);
|
|
assert.match(rival, /Waiting for the Semuta Drug claim to finish/);
|
|
assert.doesNotMatch(rival, /Projectile|Inspect card|Commit Semuta Drug|>Continue</);
|
|
});
|
|
|
|
void test('passed, blocked, busy and waiting seats hide or disable unavailable controls', () => {
|
|
const passed = offer({ passed: true, canCommit: true });
|
|
assert.match(markup(passed), /You continued/);
|
|
assert.doesNotMatch(markup(passed), /Commit Semuta Drug|>Continue</);
|
|
const blocked = offer({ blocked: 'Reserved for an unfinished transaction.' });
|
|
assert.doesNotMatch(markup(blocked), /Commit Semuta Drug/);
|
|
assert.match(markup(offer({ canCommit: true }), true), /disabled=""[^>]*>Commit Semuta Drug/);
|
|
assert.match(markup(offer(), true), /disabled=""[^>]*>Continue/);
|
|
assert.match(markup(offer({ stage: 'select', candidates: [first, second] }), true),
|
|
/disabled=""[^>]*>Claim Projectile/);
|
|
const required = markup(offer({ canCommit: true, passBlocked: 'Keep this required fresh exchange.' }));
|
|
assert.match(required, /Keep this required fresh exchange/);
|
|
assert.match(required, /disabled=""[^>]*>Continue/);
|
|
assert.doesNotMatch(required, /disabled=""[^>]*>Commit Semuta Drug/);
|
|
});
|