// TYPESCRIPT_PATH may point to the TypeScript bundled with Cocos Creator. const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const ts = require(process.env.TYPESCRIPT_PATH || 'typescript'); const blockFile = 'assets/Script/Block.ts'; const collideFile = 'assets/Script/lq_collide_system/lq_collide.ts'; function method(file, name, globals = {}) { const source = ts.createSourceFile(file, fs.readFileSync(path.join(__dirname, '..', file), 'utf8'), ts.ScriptTarget.Latest, true); const cls = source.statements.find(node => ts.isClassDeclaration(node)); const member = cls.members.find(node => node.name && node.name.getText(source) === name); assert.ok(member, name); const code = ts.transpileModule('class Subject {' + member.getText(source) + '}\nSubject;', { compilerOptions: { target: ts.ScriptTarget.ES2017 } }).outputText; return vm.runInNewContext(code, { console, ...globals }).prototype[name]; } const refresh = method(blockFile, 'refreshOldRainbowVisual'); const sameColorTile = method(blockFile, 'canPassSameColorSimpleBlock'); const BlockType = { 普通块: 0, 叠加块上: 10, 粘合块: 9, 变色块: 20, 单色地块: 22, 问号块: 16 }; function makeBlock(oldRainbow = true, color = 5, type = 2) { const sprite = {}; const children = { icon: { active: true, getComponent: () => sprite }, rainbow_old: { active: false }, key: { active: true }, flowerSend: { active: true }, }; return { oldRainbow, color, type, block_Info: { block: 5, type, color, flowerSend: 4 }, node: { getChildByName: name => children[name], children: Object.values(children) }, refreshOldRainbowVisual: refresh, children, sprite }; } test('old rainbow visuals preserve real color, type and attachments through color refresh and thaw', () => { const globals = { cc: { Sprite: {} }, BlockType, MapConroler: { _instance: {} }, BlockAssetManager: { instance: { getFrame: name => name } }, blockTexturePath: (color, shape) => `${color}color${shape}` }; const initColor = method(blockFile, 'initColor', globals); const resetFreeze = method(blockFile, 'resetFreeze', globals); const b = makeBlock(); initColor.call(b, false); assert.equal(b.color, 5); assert.equal(b.type, 2); assert.equal(b.sprite.spriteFrame, '5color5'); assert.equal(b.children.icon.active, false); assert.equal(b.children.rainbow_old.active, true); assert.equal(b.children.key.active, true); assert.equal(b.children.flowerSend.active, true); b.type = b.block_Info.type = 4; b.children.rainbow_old.active = false; b.children.icon.active = true; resetFreeze.call(b); assert.equal(b.type, 0); assert.equal(b.color, 5); assert.equal(b.oldRainbow, true); assert.equal(b.children.rainbow_old.active, true); assert.equal(b.children.icon.active, false); const ordinary = makeBlock(false); refresh.call(ordinary); assert.equal(ordinary.children.icon.active, true); }); test('initializing a reused block removes old rainbow capability and overlay', () => { const b = makeBlock(); let destroyed = false; b.children.rainbow_old.removeFromParent = () => { delete b.children.rainbow_old; }; b.children.rainbow_old.destroy = () => { destroyed = true; }; b.node.on = () => {}; b.node._touchListener = { setSwallowTouches() {} }; Object.assign(b, { jsonDeepClone: v => JSON.parse(JSON.stringify(v)), initColor() {}, initType() {}, initBlocks() {}, flowerType: 0 }); const init = method(blockFile, 'init', { cc: { Node: { EventType: {} } }, BlockType, MapConroler: { _instance: { mapInfo: [] } }, setTimeout() {} }); init.call(b, { type: 0, color: 7, block: 5, id: 9 }); assert.equal(destroyed, true); assert.equal(b.oldRainbow, false); assert.equal(b.color, 7); assert.equal(b.type, 0); assert.equal(b.children.icon.active, true); }); test('opening conversion blocks input before reading a touch event', () => { const touchStart = method(blockFile, 'touchStart', { MapConroler: { _instance: { oldRainbowPreparing: true } } }); touchStart.call({}, { getLocation() { throw Error('opening conversion accepted a touch'); } }); }); test('old rainbow passes both kinds of monochrome tile, but not walls, vines or composite restrictions', () => { const map = { riseFallBlock: [] }; const disable = method(collideFile, 'disableCollider', { MapConroler: { _instance: map } }); let id = 0; const make = (color, type = 0, oldRainbow = false, info = {}) => { const block = { color, type, oldRainbow, block_Info: info, canPassSameColorSimpleBlock: sameColorTile }; return { block, node: { name: 'left', parent: { uuid: ++id, getComponent: () => block } } }; }; for (let color = 1; color <= 10; color++) { const rainbow = make(5, 2, true), tile = make(color, 22); assert.equal(disable.call(rainbow, tile), true); assert.equal(disable.call(tile, rainbow), true); assert.equal(disable.call(rainbow, make(color)), false); assert.equal(disable.call(make(5, 0, true, { floorMove: true }), tile), false); const covered = make(5, 0, true); covered.block.flowerType = 2; assert.equal(disable.call(covered, tile), false); const ordinary = make(color === 5 ? 6 : 5); assert.equal(disable.call(ordinary, tile), false); } const rainbow = make(5, 0, true); const fixed = { node: { name: 'rise', parent: { uuid: ++id, getComponent: () => null } } }; map.riseFallBlock = [fixed.node.parent]; assert.equal(disable.call(rainbow, fixed), true); map.riseFallBlock = []; assert.equal(disable.call(rainbow, fixed), false, 'unregistered rise includes vines and poles'); fixed.node.name = 'wall'; assert.equal(disable.call(rainbow, fixed), false); assert.equal(rainbow.block.color, 5); });