import assert from 'node:assert/strict'; import test from 'node:test'; 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'].map(id => ({id, name: id.toUpperCase()})); const group: CoalitionGroup = {id: 'group', members: ['a', 'b', 'c'], formedTurn: 1, edges: [ {members: ['a', 'b'], formedTurn: 1}, {members: ['a', 'c'], formedTurn: 1}, {members: ['b', 'c'], formedTurn: 1}, ]}; const view = (me: string): CoalitionNexusView => ({ me, players, groups: [group], proposal: null, revision: 3, variant: 'unlimited', }); const render = (value: CoalitionNexusView) => renderToStaticMarkup(createElement(CoalitionNexus, { view: value, onIntent() {}, })); void test('only the specified group and entrant invitation is blocked', () => { const member = view('a'); member.blockedInvitations = [{groupId: 'group', entrant: 'd'}, {groupId: 'other', entrant: 'e'}]; const choices = coalitionNexusOptions(member); assert.deepEqual(choices.invitations.map(row => row.entrant), ['e']); assert.equal(choices.mayLeave, true); const html = render(member); assert.doesNotMatch(html, /Invite D to/); assert.match(html, /Invite E to A, B, C/); assert.match(html, /conflict with your commitments/); assert.match(html, /Leave A, B, C/); const entrant = view('d'); entrant.blockedInvitations = [{groupId: 'group', entrant: 'e'}]; assert.match(render(entrant), /Ask to join A, B, C/); entrant.blockedInvitations = [{groupId: 'group', entrant: 'd'}]; assert.doesNotMatch(render(entrant), /Ask to join/); }); void test('a blocked pending acceptance can still be declined or withdrawn', () => { const pending = view('d'); pending.proposal = {event: 'event', nexus: 'nexus', turn: 2, groupId: group.id, original: group, entrant: 'd', proposer: 'a', approvals: ['a', 'b']}; pending.acceptBlocked = true; assert.equal(coalitionNexusOptions(pending).mayAccept, false); assert.equal(coalitionNexusOptions(pending).mayDecline, true); assert.doesNotMatch(render(pending), /Accept invitation/); assert.match(render(pending), /conflict with your commitments/); assert.match(render(pending), /Decline and cancel invitation/); pending.me = 'b'; assert.match(render(pending), /Withdraw approval and cancel invitation/); pending.me = 'd'; pending.acceptBlocked = false; assert.equal(coalitionNexusOptions(pending).mayAccept, true); assert.match(render(pending), /Accept invitation/); }); void test('legacy admissionBlocked still blocks all invitations and acceptance', () => { const member = view('a'); member.admissionBlocked = true; assert.deepEqual(coalitionNexusOptions(member).invitations, []); assert.equal(coalitionNexusOptions(member).mayLeave, true); const pending = view('d'); pending.admissionBlocked = true; pending.proposal = {event: 'event', nexus: 'nexus', turn: 2, groupId: group.id, original: group, entrant: 'd', proposer: 'a', approvals: ['a']}; assert.equal(coalitionNexusOptions(pending).mayAccept, false); assert.equal(coalitionNexusOptions(pending).mayDecline, true); });