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.
85 lines
6.5 KiB
TypeScript
85 lines
6.5 KiB
TypeScript
import {test} from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import {readFileSync} from 'node:fs';
|
||
import {runInNewContext} from 'node:vm';
|
||
import {buildRoomMenuModel, roomMenuAction, roomEscapeConfirmation, type RoomMenuContext} from '../../YouleNexus/assets/framework/presentation/room-menu-model.ts';
|
||
import {resolveRoomChatPhrase, validateRoomChatText, roomChatByteLength, resolveRoomChatPolicy} from '../../YouleNexus/assets/framework/presentation/room-chat-config.ts';
|
||
import {getGameDefinition} from '../fixtures/game-definitions.ts';
|
||
const getRoomChatConfig=(key:string)=>getGameDefinition(key).chat;
|
||
import {resolveRoomMenuContext} from '../../YouleNexus/assets/framework/presentation/room-menu-context.ts';
|
||
|
||
const context: RoomMenuContext = {status:1,canexit:1,stage:0,playercnt:2,roomMode:1,infinite:0,shortcode:0,matchId:0,h5Version:1,appdownload:'https://example.test/app',hideEscape:false,mainSceneButton:false,vipInfinite:false,versionState:0,roomCardName:'房卡',bean:200,starName:'金币'};
|
||
const ids=(patch:Partial<RoomMenuContext>)=>buildRoomMenuModel({...context,...patch}).map(button=>button.spiritId);
|
||
test('legacy panel keeps ordered, compact visible buttons and final infinite override',()=>{
|
||
assert.deepEqual(ids({}),[151,150,185,233,513,656]);
|
||
assert.deepEqual(buildRoomMenuModel(context).map(button=>button.y),[75,168,261,354,447,540]);
|
||
assert.deepEqual(ids({stage:1,canexit:0}),[151,150,185,656]);
|
||
assert.deepEqual(ids({stage:1,canexit:1}),[151,150,233,656]);
|
||
assert.deepEqual(ids({stage:1,canexit:0,infinite:1}),[151,150,233,3014,656]);
|
||
assert.deepEqual(ids({infinite:1,shortcode:123}),[151,150,233,656]);
|
||
assert.deepEqual(ids({infinite:1,vipInfinite:true}),[151,150,233,656]);
|
||
});
|
||
test('legacy escape guards and match hide ordering are preserved',()=>{
|
||
for(const patch of [{status:2},{playercnt:3},{roomMode:0},{hideEscape:true},{mainSceneButton:true}])assert.ok(!ids(patch).includes(513));
|
||
assert.ok(ids({matchId:3}).includes(185));
|
||
assert.ok(!ids({h5Version:0}).includes(656));
|
||
assert.ok(!ids({appdownload:''}).includes(656));
|
||
});
|
||
test('menu actions distinguish owner exit, pregame break and in-game dissolution',()=>{
|
||
assert.deepEqual(roomMenuAction(233,context),{kind:'confirm',checkType:2,message:'房间仍存在,是否退出?',command:'room.exit'});
|
||
assert.deepEqual(roomMenuAction(233,{...context,status:2}),{kind:'command',command:'room.exit'});
|
||
assert.deepEqual(roomMenuAction(185,context),{kind:'confirm',checkType:1,message:'解散房间不扣房卡,是否解散?',command:'room.break'});
|
||
assert.equal(roomMenuAction(185,{...context,versionState:1}).message,' 是否解散房间?');
|
||
assert.deepEqual(roomMenuAction(185,{...context,stage:1,canexit:0}),{kind:'command',command:'room.apply-dissolution'});
|
||
assert.throws(()=>roomMenuAction(3014,context),/not visible/);
|
||
});
|
||
test('escape asks for quantity then keeps legacy confirmation and balance error',()=>{
|
||
assert.deepEqual(roomMenuAction(513,context),{kind:'input',message:'请输入投降扣除数量(底翻)'});
|
||
assert.deepEqual(roomEscapeConfirmation(context,30),{kind:'confirm',checkType:3,message:'将扣除30金币,确定逃跑?',command:'room.escape',count:30});
|
||
assert.deepEqual(roomEscapeConfirmation(context,300),{kind:'tip',message:'输入大于您拥有的300金币数:200!'});
|
||
assert.throws(()=>roomEscapeConfirmation(context,NaN));
|
||
});
|
||
test('chat config uses exact game source, paired audio and one-based incoming indexes',()=>{
|
||
const template=getRoomChatConfig('Game_Surface_3'),eqw=getRoomChatConfig('erqiwang');
|
||
assert.equal(resolveRoomChatPhrase(template,1).text,'你好');
|
||
assert.equal(resolveRoomChatPhrase(eqw,1).text,'快点吧,我等的花儿都谢了。');
|
||
assert.equal(resolveRoomChatPhrase(eqw,7).text,'我先走了,后会有期');
|
||
assert.ok(eqw.phrases.every(phrase=>phrase.audio===''));
|
||
assert.equal(eqw.limitLength,40);assert.equal(eqw.showChatMs,2000);
|
||
assert.throws(()=>getRoomChatConfig('unknown'),/Unknown/);
|
||
assert.equal(resolveRoomChatPhrase(eqw,8).text,eqw.phrases[0].text);
|
||
for(const index of [0,1.5,NaN])assert.throws(()=>resolveRoomChatPhrase(eqw,index),/index/);
|
||
});
|
||
test('chat validation preserves source byte count and substring filtering',()=>{
|
||
const config=getRoomChatConfig('Game_Surface_3');
|
||
assert.equal(roomChatByteLength('a^你😀'),9);
|
||
assert.doesNotThrow(()=>validateRoomChatText(config,'你好'));
|
||
assert.throws(()=>validateRoomChatText(config,' '),/请输入/);
|
||
assert.doesNotThrow(()=>validateRoomChatText(config,'你'.repeat(40)));
|
||
assert.throws(()=>validateRoomChatText(config,'你'.repeat(41)),/40/);
|
||
assert.doesNotThrow(()=>validateRoomChatText(config,'你'.repeat(21),true));
|
||
assert.throws(()=>validateRoomChatText(config,'毛泽东'),/内容含有敏感词汇请重新输入!/);
|
||
assert.equal(config.announcementLimit,10000);assert.equal(config.voiceHistoryTag,20000);
|
||
assert.deepEqual(resolveRoomChatPolicy({raw:{},getValue:()=>undefined}),{noticeLimit:-1,noticeCost:0});
|
||
assert.deepEqual(resolveRoomChatPolicy({raw:{},getValue:key=>key==='noticelimit'?100:2}),{noticeLimit:100,noticeCost:2});
|
||
});
|
||
test('room menu source resolver reports missing authoritative room state',()=>{
|
||
assert.throws(()=>resolveRoomMenuContext({room:{kind:'outside'}} as any,{} as any,getGameDefinition('Game_Surface_3').roomMenu,{h5Version:1,versionState:0}),/inside room/);
|
||
const state:any={room:{kind:'inside',roomcode:'123456',stage:0,infinite:0,roomMode:1,status:1,canexit:1,shortcode:null,matchId:null,seatPlayerIds:[1,null]},players:{selfPlayerId:1,entities:{1:{bean:100}}}};
|
||
const config:any={identity:{},remoteConfig:{raw:{},getValue:()=>undefined}};
|
||
const resolved=resolveRoomMenuContext(state,config,getGameDefinition('Game_Surface_3').roomMenu,{h5Version:1,versionState:0});
|
||
assert.equal(resolved.playercnt,1);assert.equal(resolved.shortcode,null);assert.equal(resolved.bean,100);
|
||
assert.equal(resolved.roomCardName,'房卡');assert.equal(resolved.appdownload,'');
|
||
delete state.room.status;
|
||
assert.throws(()=>resolveRoomMenuContext(state,config,getGameDefinition('Game_Surface_3').roomMenu,{h5Version:1,versionState:0}),/status/);
|
||
});
|
||
test('both configured banned corpora match their own authoritative legacy source',()=>{
|
||
for(const gameKey of ['Game_Surface_3','erqiwang']){
|
||
const source=readFileSync(new URL(`../../../projects/${gameKey}/js/00_Surface/03_Banwords.js`,import.meta.url),'utf8');
|
||
const literal=/var banwords\s*=\s*("[\s\S]*?");/.exec(source);
|
||
assert.ok(literal);
|
||
const words=String(runInNewContext(literal[1])).split('、');
|
||
assert.deepEqual(getRoomChatConfig(gameKey).bannedWords,words);
|
||
}
|
||
});
|