Automatically return the full combined hand when it is the only legal special-Karama return. Preserve real choices and restore the suspended response or auction payment through the same settlement. Record the actual blind draw as owner-only historical card faces, with a bound integrity receipt; legacy forced saves describe held-card history truthfully. Add readable private history and enlarged inspectors. Both manual and forced returns publish only a faction/action completion tag. Wake idle legacy exchanges through production recovery without writes for real choices. Keep provisional auction funding rules and release gates. Validation: types, lint, 5150 offline tests, build and 49 HTTP tests passed. Independent review resolved history substitution and root review completed idle recovery wiring. New tests cover 1-4 forced draws, real choices, overlays, exact saved history, immutable rejection, all four legal AI paths, production competing actions/recovery, private views and custody. One genuine Advanced four-player sample completed 158 actions with 4 JSON restores and no rejected action; no difficulty tuning. Browser QA QUTHVPTT used genuine Advanced setup and conserved Bidding positions. The actual power returned four cards without a prompt; private history/enlargement and exact refresh worked. Phone panel fit without horizontal overflow with 44px inspector controls. Screenshot capture timed out; complete visual acceptance and transient notice capture remain open. Preserved every prior row across all ten tables, including 1586 rooms and 3354 seats. New QA/integration records bring totals to 1626 rooms and 3442 seats. No reset, restart or automation.
42 lines
1.9 KiB
TypeScript
42 lines
1.9 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { createElement } from 'react';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import { HarkonnenExchangeInspection } from '../components/harkonnen-exchange-inspection';
|
|
import { createGame, joinGame, newPlayer, viewGame, type GameView } from '../game/engine';
|
|
import { baseDeck } from '../game/cards';
|
|
|
|
function view() {
|
|
const game = createGame('INSPECTION', newPlayer('h', 'Harkonnen', 'harkonnen'));
|
|
joinGame(game, newPlayer('e', 'Emperor', 'emperor'));
|
|
const result = viewGame(game, 'h');
|
|
result.harkonnenExchangeInspection = {
|
|
owner: 'h', target: 'e', turn: 3, phase: 3, kind: 'draw',
|
|
cards: baseDeck().filter(card => card.name === 'Shield').slice(0, 1),
|
|
};
|
|
return result;
|
|
}
|
|
const render = (game: GameView) => renderToStaticMarkup(createElement(HarkonnenExchangeInspection, { game }));
|
|
|
|
void test('private exchange record offers readable cards and inspection without a return confirmation', () => {
|
|
const html = render(view());
|
|
assert.match(html, /Your Harkonnen inspection · Turn 3/);
|
|
assert.match(html, /Cards you took and inspected from Emperor/);
|
|
assert.match(html, /may have changed hands since/);
|
|
assert.match(html, /Shield/);
|
|
assert.match(html, /Inspect card: Shield/);
|
|
assert.doesNotMatch(html, /Return cards|Confirm|Acknowledge/);
|
|
});
|
|
|
|
void test('legacy history describes held cards without inventing their source', () => {
|
|
const game = view(); game.harkonnenExchangeInspection!.kind = 'return';
|
|
const html = render(game);
|
|
assert.match(html, /Cards in your hand before the automatic return to Emperor/);
|
|
assert.doesNotMatch(html, /Cards you took/);
|
|
});
|
|
|
|
void test('absent and mismatched-owner records render no private card information', () => {
|
|
const game = view(); game.me = 'e'; assert.equal(render(game), '');
|
|
game.me = 'h'; game.harkonnenExchangeInspection = null; assert.equal(render(game), '');
|
|
});
|