104 lines
7.3 KiB
JavaScript
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');
|
|
}
|
|
}
|
|
});
|