MatchMaster/tools/test-cloud-rise-counters.cjs
2026-09-21 12:44:48 +08:00

116 lines
8.6 KiB
JavaScript

const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),vm=require('node:vm'),ts=require('typescript');
test('continue prompt uses the shared artwork without a label or clock binding',()=>{
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_home/prefab/CloudRise.prefab'));
const panel=a.find(o=>o.pages&&o.buttons),node=a[panel.continueHint.__id__];
const spriteRef=node._components.find(r=>a[r.__id__].__type__==='cc.Sprite'),sprite=a[spriteRef.__id__];
const meta=JSON.parse(fs.readFileSync('assets/cloud_rise_art/images/continue.png.meta'));
assert.equal(node._name,'ClickToContinue');
assert.equal(sprite._spriteFrame.__uuid__,meta.subMetas.continue.uuid);
assert.equal(node._components.length,1);
assert.equal(node._contentSize.width-4,224);
assert.equal(node._contentSize.height-4,43);
assert.equal(sprite._sizeMode,0,'custom size reserves the outline margin');
const material=JSON.parse(fs.readFileSync('assets/cloud_rise_home/images/continue_outline.mtl'));
assert.equal(sprite._materials[0].__uuid__,JSON.parse(fs.readFileSync('assets/cloud_rise_home/images/continue_outline.mtl.meta')).uuid);
assert.equal(material._techniqueData['0'].props.outlineWidth,2);
assert.deepEqual(material._techniqueData['0'].props.outlineColor,{__type__:'cc.Vec4',x:0,y:0,z:0,w:0.5});
assert.equal(meta.packable,false,'outline UVs require an independent texture');
assert(!panel.clocks.some(r=>r&&r.__id__===spriteRef.__id__));
});
test('matching and all stages share the continue prompt without stale legacy text or clock rebinding',()=>{
for(const name of ['home','matching','stage1','stage2','stage3']){
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_'+name+'/prefab/CloudRise.prefab'));
for(const label of a.filter(o=>o.__type__==='cc.Label')){
if(label._string!==undefined&&label['_N$string']!==undefined)
assert.equal(label['_N$string'],label._string,name+'/'+a[label.node.__id__]._name);
}
const bindings=a.find(o=>o.nodeKeys);
if(!bindings)continue;
assert(!bindings.nodeKeys.includes('continueHint'),name+' must keep the shared prompt');
bindings.componentKeys.forEach((key,i)=>{
if(!key.startsWith('clocks:'))return;
const label=a[bindings.components[i].__id__];
assert.equal(a[label.node.__id__]._name,'Clock',name+' must only bind its activity clock');
});
}
});
test('image fractions reuse seven glyphs, preserve slash artwork, and fit the counter width',()=>{
const exports={},cc={Component:class{},Sprite:class{},SpriteFrame:class{},_decorator:{ccclass:c=>c,executeInEditMode:c=>c,property:(...args)=>args.length>1?undefined:()=>{}}};
cc.Sprite.SizeMode={CUSTOM:0};
vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/cloud_rise_home/scripts/CloudRiseCounter.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2017,experimentalDecorators:true}}).outputText,{exports,cc});
const counter=new exports.default();counter.frames=Array.from({length:11},(_,i)=>({id:i,getRect:()=>({width:i===10?18:29,height:39})}));
counter.node={width:300,children:Array.from({length:7},()=>({sprite:{},getComponent(){return this.sprite;},setContentSize(w,h){this.width=w;this.height=h;},setPosition(x,y){this.x=x;this.y=y;}}))};
for(const value of ['0/5','5/5','0/7','7/7','9/9','0/100','57/100','100/100']){
counter.string=value;const active=counter.node.children.filter(n=>n.active);
assert.equal(active.length,value.length);assert.equal(active.map(n=>n.sprite.spriteFrame.id===10?'/':n.sprite.spriteFrame.id).join(''),value);
assert(active.every(n=>Math.abs(n.x)+n.width/2<=150.0001));
assert(Math.abs(active[0].x-active[0].width/2+active.at(-1).x+active.at(-1).width/2)<.001);
}
counter.string='0/5';assert.equal(counter.node.children.length,7);
});
test('all stages bind image counters and the supplied hint without cross-stage dependencies',()=>{
const frameId=name=>Object.values(JSON.parse(fs.readFileSync('assets/cloud_rise_home/images/'+name+'.png.meta')).subMetas)[0].uuid;
const ids=Array.from({length:10},(_,i)=>frameId('stat_'+i)).concat(frameId('stat_slash'));
for(let i=1;i<=3;i++){
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_stage'+i+'/prefab/CloudRise.prefab'));
const binding=a.find(o=>o.componentKeys),get=key=>a[binding.components[binding.componentKeys.indexOf(key)].__id__];
for(const key of ['progressCount','survivorCount']){
const c=get(key),n=a[c.node.__id__];assert.deepEqual(c.frames.map(f=>f.__uuid__),ids);assert.equal(n._children.length,7);
assert(!n._components.some(r=>a[r.__id__].__type__==='cc.Label'));
}
const hint=a.find(o=>o._name==='RewardHint');assert.equal(hint._active,false);
assert(a.some(o=>o._spriteFrame?.__uuid__===frameId('streak_hint')));
const button=a.find(o=>o._name==='ShowParticipants');assert.equal(button._active,false);
assert.equal(a[button._components.find(r=>a[r.__id__].__type__==='cc.Button').__id__]._enabled,false);
}
const matching=JSON.parse(fs.readFileSync('assets/cloud_rise_matching/prefab/CloudRise.prefab'));
assert(matching.some(o=>o._name==='StreakHint'));assert(matching.some(o=>o._spriteFrame?.__uuid__===frameId('streak_hint')));
});
test('authored counter layout survives changing survivor counts and showing slots again',()=>{
const exports={},cc={Component:class{},Sprite:class{},SpriteFrame:class{},_decorator:{ccclass:c=>c,executeInEditMode:c=>c,property:(...args)=>args.length>1?undefined:()=>{}}};
vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/cloud_rise_home/scripts/CloudRiseCounter.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2017,experimentalDecorators:true}}).outputText,{exports,cc});
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_stage1/prefab/CloudRise.prefab'));
const node=a.find(n=>n._name==='SurvivorsValue'),component=a[node._components[0].__id__],counter=new exports.default();
assert.equal(component.usePrefabLayout,true);counter.usePrefabLayout=component.usePrefabLayout;
counter.frames=Array.from({length:11},(_,id)=>({id}));
counter.node={children:node._children.map(r=>{const n=a[r.__id__],s=a[n._components[0].__id__];return {x:n._trs.array[0],y:n._trs.array[1],width:n._contentSize.width,height:n._contentSize.height,sprite:{sizeMode:s._sizeMode},getComponent(){return this.sprite;}};})};
const style=()=>counter.node.children.map(n=>[n.x,n.y,n.width,n.height,n.sprite.sizeMode]);const before=style();
for(const value of ['100/100','57/100','9/100','0/100','100/100']){
counter.string=value;assert.deepEqual(style(),before);
const active=counter.node.children.filter(n=>n.active);assert.equal(active.length,value.length);
assert.equal(active.map(n=>n.sprite.spriteFrame.id===10?'/':n.sprite.spriteFrame.id).join(''),value);
}
});
test('matching count and all victory counts bind sprite counters with complete digit sets',()=>{
for(const name of ['matching','stage1','stage2','stage3']){
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_'+name+'/prefab/CloudRise.prefab'));
const b=a.find(o=>o.componentKeys),key=name==='matching'?'matchingCount':'victoryOthers';
const c=a[b.components[b.componentKeys.indexOf(key)].__id__],n=a[c.node.__id__];
assert.equal(c.frames.length,name==='matching'?11:10);assert.equal(n._children.length,name==='matching'?7:3);
assert(!n._components.some(r=>a[r.__id__].__type__==='cc.Label'));assert.equal(c.usePrefabLayout,false);
}
});
test('stage bubbles have exactly 20 pixels of image padding and guides share the home-owned icon',()=>{
const home=JSON.parse(fs.readFileSync('assets/cloud_rise_home/prefab/CloudRise.prefab')),b=home.find(o=>o.pages&&o.buttons);
const guide=home[b.activityGuide.__id__];assert.equal(guide._active,false);
const help=home[b.overviewHelp.__id__],sprite=home[help._components[0].__id__]._spriteFrame.__uuid__;
assert(home[b.guideShade.__id__]._components.some(r=>home[r.__id__].__type__==='cc.BlockInputEvents'));
assert.equal(b.guideClose,undefined);assert(!home.some(n=>n._name==='GuideClose'));
for(let i=1;i<=3;i++){
const a=JSON.parse(fs.readFileSync('assets/cloud_rise_stage'+i+'/prefab/CloudRise.prefab'));
const hint=a.find(n=>n._name==='RewardHint'),art=a[hint._children[0].__id__];
assert.equal(hint._contentSize.width-art._contentSize.width*art._trs.array[7],40);
assert.equal(hint._contentSize.height-art._contentSize.height*art._trs.array[8],40);
const surface=hint._components.map(r=>a[r.__id__]).find(n=>n.radius!==undefined);assert.equal(surface.radius,20);assert.equal(surface.tailSize,0);
const binding=a.find(n=>n.nodeKeys),button=a[binding.nodes[binding.nodeKeys.indexOf('stageHelp')].__id__];
assert.equal(a[button._components[0].__id__]._spriteFrame.__uuid__,sprite);
}
});