Files
youle_cocos/cocoscreator_projects/framework-tests/presentation/room-menu-model.test.ts
T
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

85 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
});