65 lines
3.5 KiB
TypeScript
65 lines
3.5 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 players = ['a', 'b', 'c', 'd', 'e', 'f'].map(id => ({id, name: id.toUpperCase()}));
|
|
function group(members: string[]): CoalitionGroup {
|
|
const edges: CoalitionGroup['edges'] = [];
|
|
for (let left = 0; left < members.length; left++)
|
|
for (let right = left + 1; right < members.length; right++)
|
|
edges.push({members: [members[left], members[right]], formedTurn: 1});
|
|
return {id: 'group', members, edges, formedTurn: 1};
|
|
}
|
|
function view(me: string, members: string[], unlimited = true): CoalitionNexusView {
|
|
return {me, groups: [group(members)], players, proposal: null, revision: 7,
|
|
...(unlimited ? {variant: 'unlimited' as const} : {})};
|
|
}
|
|
function render(value: CoalitionNexusView, disabled = false) {
|
|
return renderToStaticMarkup(createElement(CoalitionNexus, {view: value, disabled, onIntent() {}}));
|
|
}
|
|
|
|
void test('unlimited membership offers entry only to a member or the unallied entrant', () => {
|
|
const member = view('a', ['a', 'b', 'c']);
|
|
assert.deepEqual(coalitionNexusOptions(member).invitations.map(row => row.entrant), ['d', 'e', 'f']);
|
|
assert.match(render(member), /Invite D to A, B, C/);
|
|
const entrant = view('d', ['a', 'b', 'c']);
|
|
assert.deepEqual(coalitionNexusOptions(entrant).invitations.map(row => row.entrant), ['d']);
|
|
assert.match(render(entrant), /Ask to join A, B, C/);
|
|
assert.deepEqual(coalitionNexusOptions(view('missing', ['a', 'b', 'c'])).invitations, []);
|
|
assert.deepEqual(coalitionNexusOptions(view('a', ['a', 'b', 'c'], false)).invitations, [],
|
|
'An older three-member prototype must not admit a fourth player.');
|
|
});
|
|
|
|
void test('unlimited invitations allow a sixth player but never a seventh', () => {
|
|
const five = view('a', ['a', 'b', 'c', 'd', 'e']);
|
|
assert.deepEqual(coalitionNexusOptions(five).invitations.map(row => row.entrant), ['f']);
|
|
assert.match(render(five), /Invite F to A, B, C, D, E/);
|
|
assert.match(render(five), /Leaving keeps the other players allied/);
|
|
const six = view('a', ['a', 'b', 'c', 'd', 'e', 'f']);
|
|
assert.deepEqual(coalitionNexusOptions(six).invitations, []);
|
|
assert.match(render(six), /Leave A, B, C, D, E, F/);
|
|
assert.match(render(six, true), /disabled=""[^>]*>Leave A, B, C, D, E, F/);
|
|
});
|
|
|
|
void test('every existing member and the entrant can answer one pending group invitation', () => {
|
|
const original = group(['a', 'b', 'c']);
|
|
const value = view('d', original.members);
|
|
value.proposal = {event: 'event', nexus: 'nexus', turn: 2, groupId: original.id,
|
|
original, entrant: 'd', proposer: 'a', approvals: ['a', 'b']};
|
|
assert.equal(coalitionNexusOptions(value).mayAccept, true);
|
|
assert.match(render(value), /Every current member and the invited player must agree/);
|
|
assert.match(render(value), /Approved: A, B/);
|
|
assert.match(render(value), /Waiting for: C, D/);
|
|
assert.match(render(value), /Accept invitation/);
|
|
value.me = 'b';
|
|
assert.equal(coalitionNexusOptions(value).mayAccept, false);
|
|
assert.match(render(value), /Withdraw approval and cancel invitation/);
|
|
value.me = 'e';
|
|
assert.equal(coalitionNexusOptions(value).mayAccept, false);
|
|
assert.equal(coalitionNexusOptions(value).mayDecline, false);
|
|
assert.doesNotMatch(render(value), /Accept invitation|cancel invitation/);
|
|
});
|