MatchMaster/tools/test-seven-day-stamina.cjs
2026-09-10 10:43:30 +08:00

85 lines
4.2 KiB
JavaScript

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(hp, remaining) {
let now = 1800000000000;
const info = { hp, hp_Max: 5, userPowerTime: remaining ? now / 1000 + remaining : 0 };
const storage = new Map([['userPowerTime', info.userPowerTime]]);
const timers = new Set();
const label = { string: '' };
const children = {
skyLine: { active: remaining > 0, getChildByName: () => ({ getComponent: () => label }) },
man: { active: remaining === 0 && hp === 5 },
add: { active: remaining === 0 && hp < 5 },
time: { opacity: remaining > 0 ? 0 : 255 },
};
const cc = { Label: class {}, fx: {
GameConfig: { GM_INFO: info },
StorageMessage: { getStorage: key => storage.get(key), setStorage: (key, value) => storage.set(key, value) },
}, find: () => ({ getComponent: () => home }) };
const context = vm.createContext({ cc, wx: {}, Date: { now: () => now },
Utils: { setUserPowerTime() {} }, console: { log() {} } });
function run(source) {
vm.runInContext(ts.transpileModule(source, {
compilerOptions: { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.CommonJS },
}).outputText, context);
}
function parse(path) {
return ts.createSourceFile(path, fs.readFileSync(path, 'utf8'), ts.ScriptTarget.Latest, true);
}
const toolSource = parse('assets/Script/module/Tool/GameTool.ts');
let toolObject;
function visit(node) {
if (ts.isVariableDeclaration(node) && node.name.getText(toolSource) === 'GameTool') toolObject = node.initializer;
ts.forEachChild(node, visit);
}
visit(toolSource);
const toolMethods = toolObject.properties.filter(node =>
['setUserPowerTime', 'getUserPowerTime', 'getTimeMargin2'].includes(node.name?.getText(toolSource)));
run('cc.fx.GameTool = {' + toolMethods.map(node => node.getText(toolSource)).join(',\n') + '};');
cc.fx.GameTool.shushu_Track = () => {};
const homeSource = parse('assets/Script/JiaZai.ts');
const homeMethods = homeSource.statements.find(ts.isClassDeclaration).members.filter(node =>
['updatePower', 'startPowerTime', 'stopPowerTime', 'refreshStaminaDisplay'].includes(node.name?.getText(homeSource)));
run('globalThis.Home = class {' + homeMethods.map(node => node.getText(homeSource)).join('\n') + '};');
const home = new context.Home();
home.Stamina = { getChildByName: name => children[name] };
home.schedule = callback => timers.add(callback);
home.unschedule = callback => timers.delete(callback);
home.updateCoin = () => {};
if (remaining) home.updatePower();
context.exports = {};
context.require = () => ({ default: { getUid: () => 'test-user' } });
run(fs.readFileSync('assets/seven_day_gift/SevenDayGiftReward.ts', 'utf8'));
const receipt = { claimId: 'day-one', rewards: [{ type: 'infinite_health', count: 900 }] };
return { info, children, label, timers, grant() {
let completed = false;
context.exports.default.grant(receipt, success => { assert.equal(success, true); completed = true; });
assert.equal(completed, true);
}, tick() { now += 1000; [...timers].forEach(callback => callback()); } };
}
for (const hp of [5, 2]) {
for (const remaining of [0, 600]) {
test(`day-one reward shows infinite stamina: hp=${hp}, remaining=${remaining}`, () => {
const f = fixture(hp, remaining);
f.grant();
assert.equal(f.children.skyLine.active, true, 'infinite stamina becomes visible after claiming');
assert.equal(f.children.man.active, false);
assert.equal(f.children.add.active, false);
assert.equal(f.children.time.opacity, 0);
assert.equal(f.timers.size, 1, 'exactly one infinite stamina timer runs');
f.tick();
assert.equal(f.label.string, remaining ? '24:59' : '14:59');
const expiry = f.info.userPowerTime;
f.grant();
assert.equal(f.info.userPowerTime, expiry, 'repeated receipt does not add time twice');
assert.equal(f.timers.size, 1);
});
}
}