61 lines
3.4 KiB
TypeScript
61 lines
3.4 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createElement } from 'react';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import { CoalitionNexus, coalitionNexusOptions, type CoalitionNexusView } from '../components/coalition-nexus';
|
|
import type { CoalitionGroup } from '../game/coalition-alliance';
|
|
|
|
const pair: CoalitionGroup = { id: 'pair', members: ['a', 'b'], formedTurn: 2,
|
|
edges: [{ members: ['a', 'b'], formedTurn: 2 }] };
|
|
function view(me = 'a'): CoalitionNexusView {
|
|
return { me, revision: 4, groups: [pair], proposal: null, players: [
|
|
{ id: 'a', name: 'Atreides' }, { id: 'b', name: 'Emperor' },
|
|
{ id: 'c', name: 'Fremen' }, { id: 'd', name: 'Guild' },
|
|
] };
|
|
}
|
|
const html = (value: CoalitionNexusView, disabled = false) =>
|
|
renderToStaticMarkup(createElement(CoalitionNexus, { view: value, disabled, onIntent() {} }));
|
|
|
|
void test('only a pair member or the unallied entrant is offered an invitation', () => {
|
|
const member = coalitionNexusOptions(view('a'));
|
|
assert.deepEqual(member.invitations.map(({ entrant }) => entrant), ['c', 'd']);
|
|
assert.equal(member.mayLeave, true);
|
|
const entrant = coalitionNexusOptions(view('c'));
|
|
assert.deepEqual(entrant.invitations.map(({ entrant }) => entrant), ['c']);
|
|
assert.equal(entrant.mayLeave, false);
|
|
assert.match(html(view('a')), /Invite Fremen to Atreides, Emperor/);
|
|
assert.match(html(view('c')), /Ask to join Atreides, Emperor/);
|
|
assert.deepEqual(coalitionNexusOptions(view('missing')).invitations, []);
|
|
assert.doesNotMatch(html(view('missing')), /Invite Fremen|Ask to join|Leave Atreides/);
|
|
});
|
|
|
|
void test('pending invitation presents approval and withdrawal only to its participants', () => {
|
|
const v = view('c');
|
|
v.proposal = { event: 'offer', nexus: 'nexus', turn: 3, groupId: pair.id, original: pair,
|
|
entrant: 'c', proposer: 'a', approvals: ['a'] };
|
|
let options = coalitionNexusOptions(v);
|
|
assert.equal(options.mayAccept, true); assert.equal(options.mayDecline, true);
|
|
assert.deepEqual(options.invitations, []); assert.equal(options.mayLeave, false);
|
|
assert.match(html(v), /Approved: Atreides/); assert.match(html(v), /Waiting for: Emperor, Fremen/);
|
|
assert.match(html(v), /Accept invitation/); assert.match(html(v), /Decline and cancel invitation/);
|
|
v.me = 'a'; options = coalitionNexusOptions(v);
|
|
assert.equal(options.mayAccept, false); assert.equal(options.mayDecline, true);
|
|
assert.match(html(v), /Withdraw and cancel invitation/); assert.doesNotMatch(html(v), /Accept invitation/);
|
|
v.me = 'b'; v.proposal.approvals.push('b');
|
|
assert.match(html(v), /Withdraw approval and cancel invitation/);
|
|
assert.doesNotMatch(html(v), /Accept invitation/);
|
|
v.me = 'd'; options = coalitionNexusOptions(v);
|
|
assert.equal(options.mayAccept, false); assert.equal(options.mayDecline, false);
|
|
assert.doesNotMatch(html(v), /Accept invitation|cancel invitation/);
|
|
});
|
|
|
|
void test('three-member groups cannot receive another entrant and describe leaving policy', () => {
|
|
const v = view('a');
|
|
v.groups = [{ ...pair, members: ['a', 'b', 'c'] }];
|
|
const options = coalitionNexusOptions(v);
|
|
assert.deepEqual(options.invitations, []); assert.equal(options.mayLeave, true);
|
|
assert.match(html(v), /Leaving keeps the other two players allied/);
|
|
assert.match(html(v), /Leave Atreides, Emperor, Fremen/);
|
|
assert.match(html(v, true), /disabled=""[^>]*>Leave Atreides, Emperor, Fremen/);
|
|
});
|