const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const { loadMembers } = require('./new-player-ab-test-support.cjs'); function fixture(legacy = false) { const data = JSON.parse(fs.readFileSync('assets/Scene/GameScene.fire', 'utf8')); class Widget { static AlignMode = { ON_WINDOW_RESIZE: 1 }; updateAlignment() { const node = this.node; node.y = node.parent.height * (1 - node.parent.anchorY) - this.top - node.height * (1 - node.anchorY) * node.scaleY; } } const nodes = data.map(n => n.__type__ === 'cc.Node' ? { name: n._name, children: [], components: [], x: n._trs.array[0], y: n._trs.array[1], scaleY: n._trs.array[8], height: n._contentSize.height, anchorY: n._anchorPoint.y, get parent() { return this._parent; }, set parent(value) { if (this._parent) this._parent.children = this._parent.children.filter(n => n !== this); this._parent = value; value.children.push(this); }, getChildByName(name) { return this.children.find(n => n.name === name) || null; }, getComponent(type) { return this.components.find(c => c instanceof type) || null; }, addComponent(type) { const c = new type(); c.node = this; this.components.push(c); return c; }, setPosition(p) { this.x = p.x; this.y = p.y; }, convertToWorldSpaceAR(p) { const result = { x: p.x + this.x, y: p.y + this.y }; return this.parent ? this.parent.convertToWorldSpaceAR(result) : result; }, convertToNodeSpaceAR(p) { const origin = this.convertToWorldSpaceAR({ x: 0, y: 0 }); return { x: p.x - origin.x, y: p.y - origin.y }; }, } : null); nodes.forEach((node, i) => { if (!node) return; if (nodes[data[i]._parent?.__id__]) node.parent = nodes[data[i]._parent.__id__]; for (const ref of data[i]._components) { const c = data[ref.__id__]; if (c.__type__ === 'cc.Widget') Object.assign(node.addComponent(Widget), { top: c._top }); } }); const canvas = nodes.find(n => n?.name === 'Canvas'); const game = canvas.getChildByName('Game'); const gameNode = game.getChildByName('GameNode'); const top = gameNode.getChildByName('Top'); const shop = game.getChildByName('shop'); top.getComponent(Widget).updateAlignment(); if (legacy) { shop.parent = top; shop.y = -67.741; shop.components = []; } else if (shop.getComponent(Widget)) shop.getComponent(Widget).updateAlignment(); const cc = { Widget, macro: { MAX_ZINDEX: 32767 }, isValid: node => !!node, v2: (x, y) => ({ x, y }), find: (path, root) => path.split('/').reduce((node, name) => node && node.getChildByName(name), root), }; const Manager = loadMembers('assets/Script/SceneManager.ts', ['ensureGameShop', 'getWinCoinFlyTarget'], { cc }); return { manager: Object.assign(new Manager(), { node: canvas }), game, top, shop, Widget }; } for (const legacy of [false, true]) { test(`${legacy ? 'old' : 'new'} scene keeps Shop above Lose with valid coin children and unchanged position`, () => { for (const height of [1280, 1920, 2400]) { const { manager, game, shop, top, Widget } = fixture(legacy); const before = shop.convertToWorldSpaceAR({ x: 0, y: 0 }); assert.equal(manager.ensureGameShop(), shop); assert.equal(shop.parent, game); assert.equal(top.getChildByName('shop'), null); assert.equal(shop.zIndex, 32766); shop.getComponent(Widget).updateAlignment(); assert.deepEqual(shop.convertToWorldSpaceAR({ x: 0, y: 0 }), before); assert.ok(shop.getChildByName('coin')); assert.equal(manager.getWinCoinFlyTarget(), shop.getChildByName('coins')); assert.equal(manager.ensureGameShop(), shop); assert.equal(shop.components.filter(c => c instanceof Widget).length, 1); assert.equal(game.children.filter(n => n === shop).length, 1); game.height = height; shop.getComponent(Widget).updateAlignment(); assert.ok(Math.abs(shop.y - (height / 2 - 297.741)) < 1e-9); } }); } test('missing Shop returns null instead of throwing while setting zIndex or resolving coins', () => { const { manager, game, shop } = fixture(); game.children = game.children.filter(n => n !== shop); assert.equal(manager.ensureGameShop(), null); assert.equal(manager.getWinCoinFlyTarget(), null); manager.node = null; assert.equal(manager.ensureGameShop(), null); assert.equal(manager.getWinCoinFlyTarget(), null); });