import test from 'node:test'; import assert from 'node:assert/strict'; import { registerHooks } from 'node:module'; import { readFileSync } from 'node:fs'; import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { applyAction, viewGame, type GameView } from '../game/engine'; import { BattlePromises } from '../components/battle-promises'; import { nexusAllow, nexusReload } from './fixture-nexus-cards'; import { NexusSardaukar } from '../components/nexus-sardaukar'; import { nexusSardaukarAction } from '../game/nexus-sardaukar-options'; import { nexusSardaukarFixture, beginNexusSardaukar, allowNexusSardaukar, prepareSardaukarBattle, } from './fixture-nexus-sardaukar'; const aliases = registerHooks({ resolve(specifier, context, next) { return next( specifier === 'next/image' ? 'vinext/shims/image' : specifier === 'next/link' ? 'vinext/shims/link' : specifier, context, ); }, load(url, context, next) { if (!url.endsWith('.module.css')) return next(url, context); const css = readFileSync(new URL(url), 'utf8'); const classes = Object.fromEntries( [...css.matchAll(/\.([a-zA-Z_][\w-]*)/g)].map((m) => [m[1], m[1]]), ); return { format: 'module', source: `export default ${JSON.stringify(classes)}`, shortCircuit: true, }; }, }); const { GameTable } = await import('../components/game-table'); aliases.deregister(); const html = (game: GameView, busy = false) => renderToStaticMarkup(createElement(NexusSardaukar, { game, busy, act() {} })); void test('Emperor Cunning offers one event-bound five-counter play with explicit physical identity', () => { const v = viewGame(nexusSardaukarFixture(), 'p'); const offer = v.nexusSardaukar!.offer!; assert.deepEqual(nexusSardaukarAction(v, offer.event), { type: 'nexusSardaukar', event: offer.event, }); assert.match(html(v), /Use five forces as Sardaukar/); assert.match(html(v), /ordinary physical identity, including when lost/); assert.doesNotMatch(html(v), /disabled=""|checkbox/); assert.match(html(v, true), /disabled=""/); assert.equal(nexusSardaukarAction(v, 'stale'), null); offer.blocked = 'An earlier Battle Plan commitment would become impossible.'; assert.equal(nexusSardaukarAction(v, offer.event), null); assert.match(html(v), /earlier Battle Plan commitment/); }); void test('Emperor control retains actor, Advanced, unsealed plan and interruption fences', () => { const initial = viewGame(nexusSardaukarFixture(), 'p'); for (const mutate of [ (v: GameView) => { v.me = 'q'; }, (v: GameView) => { v.advanced = false; }, (v: GameView) => { v.phase = 5; }, (v: GameView) => { v.nexusCards!.card = null; }, (v: GameView) => { v.battle!.submitted = ['p']; }, (v: GameView) => { v.decision = { kind: 'nullentropy', player: 'p' }; }, (v: GameView) => { v.truthtrance = { stage: 'priority', queue: [], passed: [], question: null, }; }, (v: GameView) => { v.automaticContinuationPending = true; }, (v: GameView) => { v.nexusCards!.waiting = ['q']; }, ]) { const v = structuredClone(initial); mutate(v); assert.equal(nexusSardaukarAction(v, v.nexusSardaukar!.offer!.event), null); } for (const options of [{ starred: 1 }]) { const v = viewGame(nexusSardaukarFixture(options), 'p'); const offer = v.nexusSardaukar!.offer!; assert.ok(offer.blocked); assert.equal(nexusSardaukarAction(v, offer.event), null); assert.ok(html(v).includes(offer.blocked)); } }); void test('public pending and active notices avoid private hands and identify ordinary-counter losses', () => { const initial = nexusSardaukarFixture(); const index = initial.deck.findIndex((c) => c.effect === 'karama'); initial.players[1].hand.push(initial.deck.splice(index, 1)[0]); const g = beginNexusSardaukar(initial); assert.equal(g.response?.kind, 'nexusSardaukar'); for (const id of ['p', 'q', 'r']) { const v = viewGame(g, id); assert.equal(v.nexusSardaukar!.active, false); assert.equal(v.battle!.ownForces?.temporaryElite, undefined); assert.equal(v.battle!.opponentForces?.temporaryElite, undefined); Object.defineProperty(v.nexusSardaukar!, 'offer', { get() { throw new Error('Private offer read'); }, }); for (const p of v.players) Object.defineProperty(p, 'hand', { get() { throw new Error('Private hand read'); }, }); assert.match(html(v), /Karama may cancel the entire enhancement/); } const allowed = allowNexusSardaukar(g); const observer = viewGame(allowed, 'q'); assert.equal(observer.battle!.opponentForces?.temporaryElite, 5); assert.match(html(observer), /Cunning declared five ordinary Emperor counters/); assert.match(html(observer), /own-Homeworld reserve fee can reduce that group/); assert.match(html(observer), /ordinary Tanks/); assert.doesNotMatch(html(observer), /