Files
MatchMaster/tools/test-level-reward.cjs
2026-11-02 18:56:47 +08:00

104 lines
5.1 KiB
JavaScript

// Run: node --test tools/test-level-reward.cjs
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const { loadMembers, resolveFile } = require('./new-player-ab-test-support.cjs');
const read = file => JSON.parse(fs.readFileSync(resolveFile(file), 'utf8'));
const configFile = 'assets/Script/module/Config/GameConfig.ts';
test('win display uses the awarded amount with shared digits and stays centered', () => {
const rendered = [], symbol = { active: true }, background = { x: 55 };
const root = { getChildByName: name => name === 'New Sprite' ? symbol : background };
const cc = { isValid: node => !!node };
const Manager = loadMembers('assets/Script/SceneManager.ts', ['updateWinCoin'], {
cc, console: { log() {} },
NumberToImage: { rewardTextToImages: (text, node, height) => rendered.push({ text, node, height }) },
});
const manager = new Manager();
manager.winCoin = { parent: root, x: 0, active: false };
manager.updateWinCoinIconState = () => {};
for (const amount of [40, 120, 480]) {
manager.updateWinCoin(amount);
assert.deepEqual(rendered.at(-1), { text: String(amount), node: manager.winCoin, height: 56 });
assert.equal(manager.winCoin.active, true);
assert.equal(manager.winCoin.x, background.x);
assert.equal(symbol.active, false);
}
manager.updateWinCoin(0);
assert.equal(manager.winCoin.active, false);
assert.equal(rendered.length, 3);
});
function fixture() {
const credits = [], events = [], callbacks = [];
const cc = { resources: { load: (path, done) => { assert.equal(path, 'Json/PASS_RATE'); callbacks.push(done); } }, fx: {} };
const Config = loadMembers(configFile, ['defaultPassRate', 'PASS_RATE', 'passRateReady', 'passRateLoading',
'ensurePassRateLoaded', 'getLevelPassRate', 'getLevelRewardMultiplier', 'getLoadedLevelNumber', 'applyLevelData'],
{ cc, console: { log() {} }, LEVEL_TIME_BONUS: {} });
Config.defaultPassRate = read('assets/resources/Json/PASS_RATE.json');
Config.GM_INFO = { level: 0, doubleCoin: 1, rewardAdCoin: 1 };
cc.fx = { GameConfig: Config, GameTool: {
changeCoin: amount => credits.push(amount), shushu_Track: (name, data) => events.push({ name, ...data }),
} };
const Map = loadMembers('assets/Script/Map.ts', ['initLevelReward', 'grantWinCoinReward'], { cc });
return { Config, map: new Map(), credits, events, callbacks };
}
test('every configured hard level pays 120; ordinary levels pay 40; both extra multipliers stack', () => {
const { Config, map, credits, events } = fixture();
for (const level of [1, 20, 22, ...Config.PASS_RATE.map(row => row.level)]) {
for (const doubleCoin of [1, 2]) for (const rewardAdCoin of [1, 2]) {
Config.loadedLevelNumber = level;
Object.assign(Config.GM_INFO, { doubleCoin, rewardAdCoin });
map.initLevelReward(); map.gameWin = true; map.grantWinCoinReward();
const expected = ([1, 20, 22].includes(level) ? 40 : 120) * doubleCoin * rewardAdCoin;
assert.equal(map.winRewardCoin, expected, `level ${level}`);
assert.equal(credits.at(-1), expected);
assert.equal(events.at(-1).num, expected);
}
}
});
test('loaded file number wins over stale JSON id, progress and later endless/help level changes', () => {
const { Config, map, credits } = fixture();
for (const level of [21, 22, 21]) {
const data = { LEVEL_INFO: [{ id: '1' }], BLOCK_INFO: [[]], WALL_INFO: [[]] };
Config.applyLevelData(data, false, 0, level);
map.initLevelReward();
assert.equal(map.enteredLevelNumber, level);
Config.GM_INFO.level = 999;
Config.GM_INFO.randomLevel = 22;
Config.GM_INFO.otherLevel = 22;
Config.loadedLevelNumber = 22;
map.gameWin = true; map.grantWinCoinReward();
assert.equal(credits.at(-1), level === 21 ? 120 : 40);
}
});
test('failed attempts pay nothing; repeated wins and changed bonuses cannot pay a second time', () => {
const { Config, map, credits, events } = fixture();
Config.loadedLevelNumber = 21; map.initLevelReward();
map.gameWin = false; map.grantWinCoinReward();
assert.equal(credits.length, 0);
map.gameWin = true; map.grantWinCoinReward();
Config.GM_INFO.doubleCoin = 2; map.grantWinCoinReward();
assert.deepEqual(credits, [120]);
assert.equal(events.length, 1);
});
test('difficulty load is shared, retries failures and retains a successful empty table', async () => {
const { Config, callbacks } = fixture();
const first = Config.ensurePassRateLoaded();
assert.equal(Config.ensurePassRateLoaded(), first);
assert.equal(callbacks.length, 1);
callbacks[0](new Error('load failed'));
await assert.rejects(first, /load failed/);
const retry = Config.ensurePassRateLoaded();
assert.equal(callbacks.length, 2);
callbacks[1](null, { json: [] });
await retry;
await Config.ensurePassRateLoaded();
assert.equal(callbacks.length, 2);
assert.equal(Config.getLevelRewardMultiplier(21), 1);
});