import assert from 'node:assert/strict'; import test from 'node:test'; import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { CoalitionFunding, coalitionFundingContributions, coalitionPledgeAction, type CoalitionFundingView } from '../components/coalition-funding'; const view: CoalitionFundingView = { targets: [ { id: 'b', name: 'Emperor', pledged: 2, maximum: 6 }, { id: 'c', name: 'Fremen', pledged: 1, maximum: 5 }, ], incoming: [ { donor: 'b', name: 'Emperor', amount: 3 }, { donor: 'c', name: 'Fremen', amount: 2 }, ], blocked: null, }; void test('each outgoing ally has a separate labelled pledge and incoming amount', () => { const html = renderToStaticMarkup(createElement(CoalitionFunding, { view, act() {}, onContributionsChange() {}, })); assert.match(html, /Pledge to Emperor · 2 spice currently set aside/); assert.match(html, /Pledge to Fremen · 1 spice currently set aside/); assert.match(html, /Set funding for Emperor/); assert.match(html, /Set funding for Fremen/); assert.match(html, /Emperor: 3 spice/); assert.match(html, /Fremen: 2 spice/); assert.match(html, /Use from Emperor for your next shipment/); assert.match(html, /Use from Fremen for your next shipment/); const bidding = renderToStaticMarkup(createElement(CoalitionFunding, { view, act() {}, onContributionsChange() {}, purpose: 'bid', })); assert.match(bidding, /Use from Emperor for your next bid/); assert.match(bidding, /Use from Fremen for your next bid/); assert.doesNotMatch(bidding, /for your next shipment/); }); void test('pledge action uses the named recipient and validates whole, safe limits', () => { const target = view.targets[0]; assert.deepEqual(coalitionPledgeAction(target, '6', null), { type: 'coalitionPledge', recipient: 'b', amount: 6 }); assert.deepEqual(coalitionPledgeAction(target, '0', null), { type: 'coalitionPledge', recipient: 'b', amount: 0 }); for (const invalid of ['', '-1', '1.5', '7', '01', '1e2', '9007199254740992']) assert.equal(coalitionPledgeAction(target, invalid, null), null); assert.equal(coalitionPledgeAction(target, '2', 'Finish the pending payment.'), null); assert.equal(coalitionPledgeAction(target, '2', null, true), null); const committed = {...target, minimum: 2}; assert.equal(coalitionPledgeAction(committed, '1', null), null); assert.deepEqual(coalitionPledgeAction(committed, '2', null), {type: 'coalitionPledge', recipient: 'b', amount: 2}); const html = renderToStaticMarkup(createElement(CoalitionFunding, { view:{...view, targets:[committed]}, act() {}, purpose:'bid', })); assert.match(html, /The current high bid commits 2 spice of this pledge/); assert.match(html, /min="2"/); }); void test('shipment selections retain donor identity and clamp to live pledges', () => { assert.deepEqual(coalitionFundingContributions(view.incoming, {b: 9, c: 1.9, missing: 8}), [ { donor: 'b', amount: 3 }, { donor: 'c', amount: 1 }, ]); assert.deepEqual(coalitionFundingContributions(view.incoming, {b: -2, c: 0}), []); assert.deepEqual(coalitionFundingContributions([{donor: 'b', name: 'Emperor', amount: 1}], {b: 3, c: 2}), [{ donor: 'b', amount: 1 }]); }); void test('busy or blocked controls cannot submit and omit shipment inputs when not requested', () => { const blocked = renderToStaticMarkup(createElement(CoalitionFunding, { view: {...view, blocked: 'Finish the pending payment.'}, act() {}, })); assert.match(blocked, /Finish the pending payment/); assert.match(blocked, /disabled=""[^>]*>Set funding for Emperor/); assert.doesNotMatch(blocked, /Use from Emperor for your next shipment/); const busy = renderToStaticMarkup(createElement(CoalitionFunding, { view, busy: true, act() {} })); assert.match(busy, /disabled=""[^>]*>Set funding for Fremen/); });