diff --git a/assets/jungle_treasure/script/JungleView.ts b/assets/jungle_treasure/script/JungleView.ts index 94e7efcf..2b6b5ecd 100644 --- a/assets/jungle_treasure/script/JungleView.ts +++ b/assets/jungle_treasure/script/JungleView.ts @@ -9,6 +9,7 @@ import { JungleViewRefs, } from "./JungleTypes"; import JungleTitleShine from "./JungleTitleShine"; +import PopupBackdropSource from "../../Script/PopupBackdropSource"; /** 负责 Jungle Treasure 的节点创建、布局和静态渲染。 */ export default class JungleView { @@ -42,6 +43,7 @@ export default class JungleView { shadeGraphics.fillColor = cc.color(35, 25, 20, 205); shadeGraphics.rect(-1600, -2200, 3200, 4400); shadeGraphics.fill(); + shade.addComponent(PopupBackdropSource); this.contentNode = new cc.Node("content"); this.contentNode.parent = this.root; diff --git a/tools/test-shared-popup-backdrop.cjs b/tools/test-shared-popup-backdrop.cjs index 2d9ca25e..bfa0f7d2 100644 --- a/tools/test-shared-popup-backdrop.cjs +++ b/tools/test-shared-popup-backdrop.cjs @@ -311,6 +311,48 @@ test('bridge reuses authored cat loading content without its mask and stops anim assert.equal(f.runningTweens.has(cat), false); }); +test('the dynamically built jungle panel dismisses queue loading and hands the backdrop back on close', () => { + const f = fixture(), nodes = [], owner = {}; + f.cc.Node = class extends f.Node { + constructor(name) { super(name); nodes.push(this); } + setScale(value) { this.scaleX = this.scaleY = value; } + setPosition() {} + }; + f.cc.Graphics.prototype.rect = function () {}; + const View = f.load('assets/jungle_treasure/script/JungleView.ts', { + '../../Script/NumberToImage': { default: class {} }, + './JungleTitleShine': { default: class {} }, + '../../Script/PopupBackdropSource': { default: f.Source } + }).default; + const root = new f.cc.Node('jungle'); f.canvas.addChild(root); + const view = new View(root, {}, {}, () => {}, () => {}); + // Keep the production build path and mask creation; unrelated card artwork is stubbed. + view.stretchToParent = () => {}; + view.createSpriteNode = name => new f.cc.Node(name); + view.createCloseButton = () => new f.cc.Node('close'); + view.createCard = () => new f.cc.Node('card'); + view.createLabel = node => ({ node }); + f.manager.hold(owner, () => f.manager.release(owner)); + f.manager.lateUpdate(11); + assert.equal(f.manager.message.active, true); + assert.equal(f.manager.cancelButton.active, true); + view.build(); + const shade = nodes.find(node => node.name === 'shade'); + const source = shade.getComponent(f.Source); + assert.ok(source, 'the runtime-created main mask must register as a visible popup'); + // The fixture invokes the component lifecycle that Creator runs on activation. + source.onLoad(); source.onEnable(); + f.manager.setWaiting(owner, true); f.manager.lateUpdate(11); + assert.equal(f.manager.bridge.active, false); + assert.equal(f.manager.message.active, false); + assert.equal(shade.getComponent(f.cc.Graphics).enabled, false, 'avoid two dim masks'); + assert.equal(source.source.visual.active, true); + root.active = false; source.onDisable(); f.manager.lateUpdate(.9); + assert.equal(f.manager.message.active, true, 'the next queued popup can still show loading'); + f.manager.release(owner); f.manager.lateUpdate(.2); + assert.equal(f.manager.bridge.active, false); +}); + test('scene cleanup drops leases and visuals; another scene gets a fresh manager', () => { const f = fixture(); f.manager.hold({}, () => assert.fail('cleanup is not user cancellation')); const p = f.popup('A');