MatchMaster/tools/test-cloud-rise-prefab.cjs
2026-09-16 18:19:34 +08:00

104 lines
7.3 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const ts = require('typescript');
const base = 'assets/Script/module/Config/CloudRiseService.ts';
function fixture(data) {
const calls = { status: 0, bundle: 0, prefab: 0, runtime: 0, retained: 0 };
const info = { uid: 'u', userId: 1001, otherLevel: 0 }, pending = [];
let bridge, failBundle = false, hold = false;
const runtime = { subscribe() {}, sync: async () => { calls.runtime++; return true; },
data: () => data, now: () => Date.now(), unseenResult: () => false, error: '' };
const cc = { warn() {}, Prefab: class {}, fx: { GameConfig: { GM_INFO: info } },
assetManager: { loadBundle(name, done) {
calls.bundle++; assert(['cloud_rise_home','cloud_rise_matching'].includes(name));
if (failBundle) return done(new Error('offline'));
if (name === 'cloud_rise_home') bridge.install(runtime);
done(null, { load(path, type, cb) { calls.prefab++; assert.equal(path, 'prefab/CloudRise'); cb(null, { addRef() { calls.retained++; } }); } });
} } };
const exports = {};
vm.runInNewContext(ts.transpileModule(fs.readFileSync(base,'utf8'), {compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2017}}).outputText,
{exports,cc,Date,console,require(name) {
if(name.includes('Utils')) return {default:{POST(url,body,done) {assert.equal(url,"cloudRise/cloudRise"); calls.status++; assert.equal(body.uid,'1001'); assert.equal(body.action,'status'); if(hold) pending.push(done); else done(data ? {code:1,data} : {code:0,data:null,msg:"活动未开启"});}}};
return {default:{apply() {}}};
}});
bridge = exports.default;
return {bridge,calls,info,pending,hold(){hold=true;},failBundle(value){failBundle=value;}};
}
const state = () => ({serverNow:Date.now(),available:true,period:{enabled:true,startsAt:Date.now()-1000,endsAt:Date.now()+100000},run:null});
test('login preloads bundle and prefab only with fresh eligible server status', async () => {
const f=fixture(state()); assert.equal(await f.bridge.sync(),true);
await f.bridge.pagePrefab(); await f.bridge.pagePrefab();
await f.bridge.pageFragment('matching');
assert.deepEqual(f.calls,{status:1,bundle:2,prefab:2,runtime:1,retained:2});
});
test('closed, future, exhausted and unknown eligibility never automatically load the bundle',async()=>{
const closed=state();closed.period.endsAt=Date.now()-1;
const future=state();future.period.startsAt=Date.now()+10000;
const used=state();used.available=false;
for(const data of [closed,future,used]) {const f=fixture(data);await f.bridge.sync();assert.equal(f.calls.bundle,0);assert.equal(f.calls.prefab,0);}
});
test('an active personal deadline permits preloading after registration closes, including waiting',async()=>{
for(const status of ['playing','waiting']) {
const data=state();data.available=false;data.period.endsAt=Date.now()-1;
data.run={status,expiresAt:Date.now()+10000};const f=fixture(data);await f.bridge.sync();assert.equal(f.calls.bundle,1);
}
const data=state();data.available=false;data.run={status:'expired',expiresAt:Date.now()-1};
const f=fixture(data);await f.bridge.sync();assert.equal(f.calls.bundle,0);
});
test('login is nonblocking, coalesces in-flight checks and ignores results for a switched account',async()=>{
const f=fixture(state());f.hold();assert.equal(f.bridge.afterLogin(),undefined);
const pending=f.bridge.sync();assert.equal(f.calls.status,1);f.info.userId=2002;f.pending[0]({code:1,data:state()});
assert.equal(await pending,false);assert.equal(f.calls.bundle,0);
});
test('failed background bundle load can be retried without creating a match',async()=>{
const f=fixture(state());f.failBundle(true);assert.equal(await f.bridge.sync(),false);
f.failBundle(false);assert.equal(await f.bridge.sync(),true);assert.equal(f.calls.bundle,2);
});
test('all reward displays reuse five authored sprite nodes when the amount changes', () => {
const objects = JSON.parse(fs.readFileSync('assets/cloud_rise_home/prefab/CloudRise.prefab'));
const renderers = objects.filter(o => o.digits && o.content && o.coin);
assert.equal(renderers.length, 1);
for (const renderer of renderers) {
const children = objects[renderer.content.__id__]._children.map(r => objects[r.__id__]);
assert.deepEqual(children.map(n => n._name), ['Digit0', 'Digit1', 'Digit2', 'Digit3', 'Digit4']);
assert(children.every(n => n._children.length === 0 && n._components.some(r => objects[r.__id__].__type__ === 'cc.Sprite')));
}
const builtin = {};
const cc = { _decorator: { ccclass: c => c, executeInEditMode: c => c, property: (...args) => args.length >= 2 ? undefined : () => {} },
Component: class {}, SpriteFrame: class {}, Material: class { static getBuiltinMaterial() { return builtin; } }, Node: class {},
Sprite: { SizeMode: { CUSTOM: 0 } }, Color: { WHITE: {} }, v2: (x,y) => ({x,y}), Vec4: class { constructor(x,y,z,w) {Object.assign(this,{x,y,z,w});} } };
const exports = {};
vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/cloud_rise_home/scripts/CloudRiseNumberToImage.ts','utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017, experimentalDecorators: true }
}).outputText, { cc, exports });
const display = new exports.default();
display.node = { width: 400 }; display.coin = { width: 60 }; display.glyphMaterial = {};
display.digits = Array.from({length:10}, (_,digit) => ({ digit, getRect: () => ({x:digit*30,y:0,width:25,height:29}), getTexture: () => ({width:256,height:256}) }));
const slots = Array.from({length:5}, () => {
const sprite = { setMaterial(index, material) { this.material = material; }, getMaterial: () => ({ setProperty() {} }) };
return { getComponent: () => sprite, setContentSize() {}, setPosition(x,y) { this.x=x;this.y=y; }, sprite };
});
display.content = {children: slots};
for (const amount of [10000, 250, 0, 99999, 15000]) {
display.setValue(amount);
assert.equal(display.content.children, slots);
assert.equal(slots.filter(n => n.active).map(n => n.sprite.spriteFrame.digit).join(''), String(amount));
assert(slots.filter(n => n.active).every((n,i,active) => !i || n.x > active[i-1].x));
}
display.sourceArt = true; display.update();
assert(slots.every(n => n.sprite.material === builtin), 'original art replaces the old outline material');
const replacement = { getRect: () => ({width:23,height:51}) };
display.digits[1] = replacement; display.update();
assert.equal(slots[0].sprite.spriteFrame, replacement, 'changing frames refreshes an unchanged amount');
display.sourceArt = false; display.glyphMaterial = null; display.update();
assert(slots.every(n => n.sprite.material === builtin));
for (const renderer of renderers.filter(r => r.sourceArt)) {
for (const ref of objects[renderer.content.__id__]._children) {
const sprite = objects[ref.__id__]._components.map(r => objects[r.__id__]).find(o => o.__type__ === 'cc.Sprite');
assert.equal(sprite._materials[0].__uuid__, 'eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432');
}
}
});