114 lines
9.0 KiB
JavaScript
114 lines
9.0 KiB
JavaScript
const test = require('node:test'), assert = require('node:assert/strict');
|
|
const fs = require('node:fs'), path = require('node:path'), vm = require('node:vm');
|
|
const { pack, names } = require('../packages/cloud-rise-package/pack');
|
|
const runtime = fs.readFileSync('packages/cloud-rise-package/runtime.js', 'utf8');
|
|
const settle = async () => { for (let i = 0; i < 20; i++) await Promise.resolve(); };
|
|
function buildFixture() {
|
|
fs.mkdirSync('temp', {recursive:true});
|
|
const root = fs.mkdtempSync(path.resolve('temp/cloud-rise-package-test-'));
|
|
fs.mkdirSync(path.join(root, 'src'));
|
|
fs.writeFileSync(path.join(root, 'src/settings.js'), 'window._CCSettings=' + JSON.stringify({subpackages:[...names,'shop'],bundleVers:Object.fromEntries(names.map(n=>[n,'abc']))}) + ';');
|
|
fs.writeFileSync(path.join(root, 'game.json'), JSON.stringify({subpackages:[...names.map(name=>({name,root:'subpackages/'+name})),{name:'shop',root:'subpackages/shop'}]}));
|
|
fs.writeFileSync(path.join(root, 'game.js'), "require('./src/settings');\nwindow.boot();");
|
|
for (const name of names) {
|
|
const dir = path.join(root,'subpackages',name);fs.mkdirSync(dir,{recursive:true});
|
|
fs.writeFileSync(path.join(dir,'config.abc.json'),JSON.stringify({name,deps:['cloud_rise_home'],uuids:['unchanged']}));
|
|
fs.writeFileSync(path.join(dir,'game.js'),'/* '+name+' */');
|
|
}
|
|
return root;
|
|
}
|
|
test('Creator GUI mini-game options invoke the hook using actualPlatform; non-WeChat builds are untouched',()=>{
|
|
const hooks={},logs=[],module={exports:{}};
|
|
vm.runInNewContext(fs.readFileSync('packages/cloud-rise-package/main.js','utf8'),{
|
|
module,require:name=>{assert.equal(name,'./pack');return {pack};},
|
|
Editor:{log:message=>logs.push(message),error:error=>{throw error;},Builder:{on:(event,fn)=>hooks[event]=fn,removeListener:(event,fn)=>{assert.equal(hooks[event],fn);delete hooks[event];}}}
|
|
});
|
|
module.exports.load();
|
|
for(const options of [{platform:'mini-game',actualPlatform:'wechatgame'},{platform:'wechatgame'}]){
|
|
const root=buildFixture();let called=0;
|
|
hooks['build-finished']({...options,dest:root},error=>{assert.ifError(error);called++;});
|
|
assert.equal(called,1);assert(fs.existsSync(path.join(root,'subpackages/cloud_rise/game.js')));
|
|
}
|
|
let skipped=0;hooks['build-finished']({platform:'mini-game',actualPlatform:'bytedance'},()=>skipped++);
|
|
assert.equal(skipped,1);assert.equal(logs.length,2);
|
|
module.exports.unload();assert.equal(hooks['build-finished'],undefined);
|
|
});
|
|
test('unprocessed template references Creator output; only a successful hook changes it to the grouped root',()=>{
|
|
const template=JSON.parse(fs.readFileSync('build-templates/wechatgame/game.json'));
|
|
assert.deepEqual(template.subpackages.filter(p=>p.name.startsWith('cloud_rise')),names.map(name=>({name,root:'subpackages/'+name})));
|
|
const root=buildFixture();fs.writeFileSync(path.join(root,'game.json'),JSON.stringify(template));pack(root);
|
|
assert.deepEqual(JSON.parse(fs.readFileSync(path.join(root,'game.json'))).subpackages.filter(p=>p.name.startsWith('cloud_rise')),[{name:'cloud_rise',root:'subpackages/cloud_rise'}]);
|
|
});
|
|
test('build hook groups shared art and four pages, preserves dependencies and other packages, and boots the loader first',()=>{
|
|
const root=buildFixture();pack(root);pack(root);
|
|
const manifest=JSON.parse(fs.readFileSync(path.join(root,'game.json')));
|
|
assert.deepEqual(manifest.subpackages,[{name:'cloud_rise',root:'subpackages/cloud_rise'},{name:'shop',root:'subpackages/shop'}]);
|
|
const settings={window:{}};vm.runInNewContext(fs.readFileSync(path.join(root,'src/settings.js'),'utf8'),settings);
|
|
assert.deepEqual(Array.from(settings.window._CCSettings.subpackages),['shop']);
|
|
const scripts=[];vm.runInNewContext(fs.readFileSync(path.join(root,'subpackages/cloud_rise/game.js'),'utf8'),{require:p=>scripts.push(p)});
|
|
assert.deepEqual(scripts,names.map(n=>'./'+n+'/game.js'));
|
|
for(const name of names){
|
|
assert(!fs.existsSync(path.join(root,'subpackages',name)));
|
|
const data=JSON.parse(fs.readFileSync(path.join(root,'subpackages/cloud_rise',name,'config.abc.json')));
|
|
assert.deepEqual(data,{name,deps:['cloud_rise_home'],uuids:['unchanged']});
|
|
}
|
|
const boot=fs.readFileSync(path.join(root,'game.js'),'utf8');
|
|
assert.equal(boot.split("require('./cloud-rise-package');").length,2);
|
|
assert(boot.indexOf("require('./cloud-rise-package');")<boot.indexOf('window.boot();'));
|
|
// A subsequent Creator build re-emits the standalone directories; the hook replaces its own old output.
|
|
for(const name of names){const dir=path.join(root,'subpackages',name);fs.mkdirSync(dir);fs.writeFileSync(path.join(dir,'config.abc.json'),'{}');fs.writeFileSync(path.join(dir,'game.js'),'/* new build */');}
|
|
pack(root);assert.equal(fs.readFileSync(path.join(root,'subpackages/cloud_rise',names[0],'game.js'),'utf8'),'/* new build */');
|
|
});
|
|
test('incomplete build fails before changing the manifest or moving any bundle',()=>{
|
|
const root=buildFixture(),manifest=fs.readFileSync(path.join(root,'game.json'),'utf8');
|
|
fs.unlinkSync(path.join(root,'subpackages',names[3],'config.abc.json'));
|
|
assert.throws(()=>pack(root),/Missing Cloud Rise bundle/);
|
|
assert.equal(fs.readFileSync(path.join(root,'game.json'),'utf8'),manifest);
|
|
assert(fs.existsSync(path.join(root,'subpackages',names[0])));
|
|
});
|
|
function loaderFixture() {
|
|
const downloads=[],reads=[],other=[],handlers={bundle:(...args)=>other.push(args)};
|
|
const window={fsUtils:{readJson:(file,done)=>{reads.push(file);done(null,{name:path.basename(path.dirname(file)),deps:['cloud_rise_home']});}}};
|
|
const cc={path:{basename:path.basename},assetManager:{downloader:{_downloaders:handlers,bundleVers:Object.fromEntries(names.map(n=>[n,'abc'])),register:(type,handler)=>handlers[type]=handler}}};
|
|
vm.runInNewContext(runtime,{window,cc,wx:{loadSubpackage:options=>downloads.push(options)}});
|
|
const load=(name,options={})=>new Promise((resolve,reject)=>handlers.bundle(name,options,(err,data)=>err?reject(err):resolve(data)));
|
|
return {window,load,downloads,reads,other};
|
|
}
|
|
test('shared art and four pages share one package download and leave other bundle loaders alone',async()=>{
|
|
const f=loaderFixture(),warm=f.window.__prepareCloudRisePackage(),matching=f.load(names[0]);
|
|
assert.equal(f.downloads.length,1);assert.equal(f.downloads[0].name,'cloud_rise');assert.equal(f.reads.length,0);
|
|
f.downloads[0].success();await warm;const data=await matching;
|
|
assert.equal(data.base,'subpackages/cloud_rise/cloud_rise_matching/');assert.equal(f.reads.length,1);
|
|
const stage=await f.load(names[2],{version:'new'});
|
|
assert.equal(stage.base,'subpackages/cloud_rise/cloud_rise_stage2/');assert.equal(f.downloads.length,1);
|
|
assert.equal(f.reads[1],'subpackages/cloud_rise/cloud_rise_stage2/config.new.json');
|
|
const art=await f.load('cloud_rise_art');
|
|
assert.equal(art.base,'subpackages/cloud_rise/cloud_rise_art/');assert.equal(f.downloads.length,1);
|
|
void f.load('cloud_rise_home');assert.equal(f.other[0][0],'cloud_rise_home');
|
|
});
|
|
test('failed unified downloads reject the page request and allow a clean retry',async()=>{
|
|
const f=loaderFixture(),first=f.load(names[0]);f.downloads[0].fail(Error('offline'));
|
|
await assert.rejects(first,/offline/);assert.equal(f.reads.length,0);
|
|
const retry=f.load(names[0]);assert.equal(f.downloads.length,2);f.downloads[1].success();await retry;
|
|
assert.equal(f.reads.length,1);
|
|
});
|
|
function serviceFixture(data) {
|
|
const ts=require('typescript'),calls=[],window={},exports={};
|
|
let ready;window.__prepareCloudRisePackage=()=>{calls.push('download');return new Promise(resolve=>ready=resolve);};
|
|
const cc={fx:{GameConfig:{GM_INFO:{userId:'1',level:200}}},warn(){}};
|
|
vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/Script/module/Config/CloudRiseService.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS}}).outputText,{exports,cc,window,require:()=>({default:{}})});
|
|
const service=exports.default;service.data=()=>data;service.now=()=>100;
|
|
service.pagePrefab=async()=>{calls.push('home');};service.pageFragment=async kind=>{calls.push(kind);};
|
|
return {service,calls,ready:()=>ready()};
|
|
}
|
|
test('eligible activity warms matching after unified download without loading any stage or enrolling',async()=>{
|
|
const f=serviceFixture({available:true,period:{startsAt:0,endsAt:200,unlockLevel:100}});
|
|
f.service.preloadActivity();assert.deepEqual(f.calls,['home','download']);f.ready();await settle();
|
|
assert.deepEqual(f.calls,['home','download','matching']);
|
|
});
|
|
test('closed activity does not preload; personal runs beyond signup closing still prepare the package',async()=>{
|
|
const closed=serviceFixture({available:false,period:{startsAt:0,endsAt:50}});closed.service.preloadActivity();assert.deepEqual(closed.calls,[]);
|
|
const active=serviceFixture({available:false,run:{status:'playing',expiresAt:200,stages:[]},period:{startsAt:0,endsAt:50}});
|
|
active.service.preloadActivity();active.ready();await settle();assert.deepEqual(active.calls,['home','download']);
|
|
});
|