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'); function fixture(cached = false) { const bundles = new Map(), pending = new Map(); let parses = 0; const bundle = (name, deps = []) => ({name, deps, releaseAll() {}, load(path, type, done) { parses++; assert.ok(bundles.has('action_bundle')); assert.ok(bundles.has('UI')); done(null, {}); }}); const gift = bundle('seven_day_gift', ['action_bundle']); if (cached) bundles.set(gift.name, gift); const view = {}; const cc = { _decorator: { ccclass: x => x }, Component: class {}, Prefab: class {}, isValid: n => n && !n.destroyed, assetManager: { getBundle: name => bundles.get(name), removeBundle: b => bundles.delete(b.name), loadBundle: (name, options, cb) => { pending.set(name, typeof options === 'function' ? options : cb); } }, instantiate: () => ({active: false, setPosition() {}, getComponent: () => view}), find: () => ({getComponent: () => home}) }; const parsed = ts.createSourceFile('JiaZai.ts', fs.readFileSync('assets/Script/JiaZai.ts','utf8'),99,true); const members = parsed.statements.find(ts.isClassDeclaration).members.filter(n => ['isHomeBundleReady','loadHomeBundleWithDependencies'].includes(n.name?.getText(parsed))).map(n=>n.getText(parsed)).join('\n'); const ctx = {cc}; vm.runInNewContext(ts.transpileModule('class Home {'+members+'} globalThis.Home=Home;', {compilerOptions:{target:ts.ScriptTarget.ES2020}}).outputText,ctx); const home = new ctx.Home(); const out = {}; vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/Script/seven_day_gift/SevenDayGiftHost.ts','utf8'),{compilerOptions:{target:ts.ScriptTarget.ES2020,module:ts.ModuleKind.CommonJS,experimentalDecorators:true}}).outputText,{cc,exports:out,require:()=>({default:{}})}); const host = new out.default(); host.node = {addChild() {}}; const finish = (name,error) => { const b=name==='seven_day_gift'?gift:bundle(name,name==='action_bundle'?['UI']:[]);if(!error)bundles.set(name,b);const cb=pending.get(name);assert.ok(cb,'missing request '+name);pending.delete(name);cb(error,b); }; return {host, pending, finish, parses:()=>parses}; } for (const cached of [false,true]) test(`seven-day Prefab waits for nested dependencies, cached=${cached}`,()=>{ const f=fixture(cached);let completed=0;f.host.ensureGiftLoaded(error=>{assert.ifError(error);completed++;}); if(!cached)f.finish('seven_day_gift');assert.equal(f.parses(),0); f.finish('action_bundle');assert.equal(f.parses(),0);f.finish('UI');assert.equal(f.parses(),1);assert.equal(completed,1); }); test('dependency failure completes callback and allows retry',()=>{ const f=fixture(true);let error;f.host.ensureGiftLoaded(e=>error=e);f.finish('action_bundle',new Error('offline')); assert.match(error.message,/offline/);assert.equal(f.host.giftLoading,false);assert.equal(f.parses(),0); f.host.ensureGiftLoaded(e=>assert.ifError(e));f.finish('action_bundle');f.finish('UI');assert.equal(f.parses(),1); }); test('destroyed host does not parse the Prefab after dependencies finish',()=>{ const f=fixture(true);f.host.ensureGiftLoaded(()=>assert.fail('stale callback'));f.host.node.destroyed=true; f.finish('action_bundle');f.finish('UI');assert.equal(f.parses(),0); });