Files
joywayer 35216f75e7 feat: integrate room framework and isolated subgame bundles
Complete room UI and protocol integration, move game definitions and resources behind bundle entries, publish authoritative version XML, and document single-game builds. Include all current resource changes and experiment artifacts.
2026-09-09 03:13:18 +08:00

60 lines
3.9 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {DeferredNumericToolsPort} from '../../YouleNexus/assets/framework/presentation/deferred-numeric-tools.ts';
import type {NumericToolsPort} from '../../YouleNexus/assets/framework/platform/game-host-adapter.ts';
function target(events:string[]):NumericToolsPort{return {
openNumberInput:request=>{events.push('input:'+request.message);},closeNumberInput:()=>{events.push('closeInput');},
showMultiplier:request=>{events.push('multiplier:'+request.values.join(','));},hideMultiplier:()=>{events.push('hideMultiplier');},
close:()=>{events.push('close');},
};}
const input=(message:string)=>({message,onConfirm:()=>{}});
const multiplier=(value:number)=>({values:[value],onConfirm:()=>{}});
test('early attach requests flush after binding in latest per-tool call order',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.openNumberInput(input('old'));port.showMultiplier(multiplier(2));port.openNumberInput(input('new'));
assert.deepEqual(events,[]);port.bind(target(events));
assert.deepEqual(events,['multiplier:2','input:new']);
port.showMultiplier(multiplier(3));assert.equal(events[events.length-1],'multiplier:3');
});
test('close and hide cancel pending requests independently',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.openNumberInput(input('cancelled'));port.showMultiplier(multiplier(2));port.closeNumberInput();
port.bind(target(events));assert.deepEqual(events,['multiplier:2']);
port.unbind();port.showMultiplier(multiplier(3));port.hideMultiplier();port.bind(target(events));
assert.deepEqual(events,['multiplier:2']);
});
test('lease close clears pending callbacks and unbinds the previous room',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.openNumberInput(input('old'));port.close();port.bind(target(events));assert.deepEqual(events,[]);
port.close();assert.deepEqual(events,['close']);port.openNumberInput(input('new'));
assert.deepEqual(events,['close']);port.bind(target(events));assert.deepEqual(events,['close','input:new']);
});
test('scene unbind retains pending requests while dropping only the current target',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.openNumberInput(input('pending'));port.unbind();port.bind(target(events));
port.unbind();port.showMultiplier(multiplier(4));assert.deepEqual(events,['input:pending']);
port.bind(target(events));assert.deepEqual(events,['input:pending','multiplier:4']);
});
test('bound dismissals forward directly and flushed requests are consumed exactly once',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.openNumberInput(input('once'));port.bind(target(events));port.closeNumberInput();port.hideMultiplier();
port.unbind();port.bind(target(events));assert.deepEqual(events,['input:once','closeInput','hideMultiplier']);
});
test('lease close unbinds even when target cleanup throws',()=>{
const port=new DeferredNumericToolsPort(),events:string[]=[];
port.bind({...target(events),close(){throw Error('cleanup failed');}});
assert.throws(()=>port.close(),/cleanup failed/);port.openNumberInput(input('next'));
assert.deepEqual(events,[]);port.bind(target(events));assert.deepEqual(events,['input:next']);
});
test('pending numeric requests receive cancellation on replacement and lease close',()=>{
const port=new DeferredNumericToolsPort();const cancelled:string[]=[];
port.openNumberInput({message:'first',onConfirm(){},onCancel(){cancelled.push('first');}});
port.openNumberInput({message:'second',onConfirm(){},onCancel(){cancelled.push('second');}});
port.showMultiplier({values:[1],onConfirm(){},onCancel(){cancelled.push('multiplier');throw Error('cancel failure');}});
assert.deepEqual(cancelled,['first']);assert.throws(()=>port.close(),/cancel failure/);assert.deepEqual(cancelled,['first','second','multiplier']);
assert.doesNotThrow(()=>port.close());
});