import assert from 'node:assert/strict'; import test from 'node:test'; import { registerHooks } from 'node:module'; import { readFileSync } from 'node:fs'; import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { viewGame, type Action, type Game, type GameView } from '../game/engine'; import { RicheseBidPromiseGuide } from '../components/richese-bid-promise-panel'; import { act, inventory, passTruthRicheseFreshDiscard, createTruthRicheseSilentFixture, askTruthRicheseSilent } from './fixture-truth-richese-silent'; 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(match => [match[1], match[1]])); return { format: 'module', source: `export default ${JSON.stringify(classes)}`, shortCircuit: true }; }, }); const { GameTable } = await import('../components/game-table'); const { Truthtrance } = await import('../components/truthtrance'); const { RicheseAuctionLot } = await import('../components/richese-auctions'); aliases.deregister(); function table(game: GameView) { return renderToStaticMarkup(createElement(GameTable, { game, busy: false, send: async () => {}, onExit() {} })); } function truth(game: GameView) { return renderToStaticMarkup(createElement(Truthtrance, { game, busy: false, act() {} })); } function auction(game: GameView) { return renderToStaticMarkup(createElement(RicheseAuctionLot, { game, busy: false, act() {} })); } function declare(game: Game, asker: string, truthCard: string): Game { let current = act(game, asker, { type:'card', card:truthCard }); for (let count = 0; current.truthtrance?.stage === 'priority' && count < current.players.length; count++) { const seat = current.players.find(row => !current.truthtrance!.passed.includes(row.id)); assert.ok(seat); current = act(current, seat.id, {type:'truthPass'}); } assert.equal(current.truthtrance?.stage, 'ask'); return current; } for (const advanced of [false, true]) void test( `${advanced ? 'Advanced' : 'Basic'} real Silent question, answer and guide connect to the original private auction`, () => { // Native public R/A/E setup, originally dealt Truth card and actual Silent // cache; the fixture selects unopened deal/Spice positions and lotteries. const f = createTruthRicheseSilentFixture(advanced); const declared = declare(f.game, f.asker, f.truth); const asker = viewGame(declared, f.asker); assert.equal(asker.richeseAuction?.method, 'silent'); assert.ok(asker.truthRicheseBidTargets.includes(f.target)); assert.match(truth(asker), /