MatchMaster/tools/test-rainbow-old-variant.cjs
WIN-GKKD951VVMJ\Administrator 74cf4413b5 增加一版彩虹机制
2026-09-20 17:14:40 +08:00

137 lines
7.1 KiB
JavaScript

// 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 root = path.resolve(__dirname, '..');
const configFile = 'assets/Script/module/Config/GameConfig.ts';
const homeFile = 'assets/Script/JiaZai.ts';
function loadMembers(file, names, globals = {}) {
const source = ts.createSourceFile(file, fs.readFileSync(path.join(root, file), 'utf8'), ts.ScriptTarget.Latest, true);
const declaration = source.statements.find(ts.isClassDeclaration);
const members = names.map(name => {
const member = declaration.members.find(node => node.name && node.name.getText(source) === name);
assert.ok(member, `${file}: ${name}`);
return member.getText(source);
});
const code = ts.transpileModule(`class Subject { ${members.join('\n')} }\nSubject;`, {
compilerOptions: { target: ts.ScriptTarget.ES2017 },
}).outputText;
return vm.runInNewContext(code, globals);
}
function config(variant = 3, overrides = {}) {
const subject = loadMembers(configFile, [
'isNewPlayerB', 'isWinStreakUnlocked', 'isRainbowWinStreak', 'isOldRainbowWinStreak',
'usesRainbowWinStreakUI', 'isRainbowRewardActive', 'isOldRainbowRewardActive',
]);
subject.forceRainbowWinStreakB = false;
subject.forceOldRainbowWinStreakC = false;
subject.GM_INFO = { abTests: { layer_two: variant }, level: 100, otherLevel: 0, winStreak: 10, ...overrides };
subject.isGameplayEntryPreparing = () => false;
return subject;
}
test('A, B and C keep separate gameplay flags while B and C share presentation', () => {
for (const [variant, expected] of [[0, [false, false, false]], [1, [false, false, false]],
[2, [true, false, true]], [3, [false, true, true]], [4, [false, false, false]]]) {
const c = config(variant);
assert.deepEqual([c.isRainbowWinStreak(), c.isOldRainbowWinStreak(), c.usesRainbowWinStreakUI()], expected);
}
for (const info of [null, {}, { abTests: null }]) {
const c = config(); c.GM_INFO = info;
assert.equal(c.isOldRainbowWinStreak(), false);
assert.equal(c.isOldRainbowRewardActive(), false);
assert.equal(c.usesRainbowWinStreakUI(), false);
}
});
test('C uses the existing win-streak, mainline and newcomer unlock requirements', () => {
for (const newPlayerB of [false, true]) for (const level of [0, 49, 50, 100]) {
for (const winStreak of [0, 9, 10, 11]) for (const otherLevel of [0, 25]) {
const info = { level, winStreak, otherLevel, abTestAssignments: newPlayerB
? { layer_1: { name: 'new1_50', group: 'B', enabled: true } } : {} };
const old = config(3, info), current = config(2, info);
const eligible = (!newPlayerB || level >= 50) && winStreak >= 10 && otherLevel === 0;
assert.equal(old.isOldRainbowRewardActive(), eligible);
assert.equal(current.isRainbowRewardActive(), eligible);
assert.equal(old.isRainbowRewardActive(), false);
assert.equal(current.isOldRainbowRewardActive(), false);
}
}
assert.equal(config(1).isOldRainbowRewardActive(), false);
});
test('forced B overrides C without enabling both rewards', () => {
const c = config(3); c.forceRainbowWinStreakB = true;
assert.equal(c.isRainbowWinStreak(), true);
assert.equal(c.isRainbowRewardActive(), true);
assert.equal(c.isOldRainbowWinStreak(), false);
assert.equal(c.isOldRainbowRewardActive(), false);
assert.equal(c.usesRainbowWinStreakUI(), true);
});
test('temporary C defaults on, overrides either group, unlocks early levels and can be disabled', () => {
const Defaults = loadMembers(configFile, ['forceOldRainbowWinStreakC']);
assert.equal(Defaults.forceOldRainbowWinStreakC, true);
for (const group of [1, 2, 3]) {
const c = config(group, { level: 0, abTestAssignments: { layer_1: { name: 'new1_50', group: 'B', enabled: true } } });
c.forceOldRainbowWinStreakC = Defaults.forceOldRainbowWinStreakC;
c.forceRainbowWinStreakB = true;
assert.equal(c.isRainbowWinStreak(), false);
assert.equal(c.isOldRainbowRewardActive(), true);
const unlock = loadMembers(configFile, ['getFeatureUnlockLevel']).getFeatureUnlockLevel;
assert.equal(unlock.call(c, 'winStreak'), 0);
assert.equal(c.GM_INFO.abTests.layer_two, group);
c.forceOldRainbowWinStreakC = false;
c.forceRainbowWinStreakB = false;
assert.equal(c.isOldRainbowWinStreak(), group === 3);
assert.equal(c.isRainbowWinStreak(), group === 2);
assert.equal(c.isWinStreakUnlocked(), false);
assert.equal(unlock.call(c, 'winStreak'), 50);
}
});
test('home preloading and popup opening select the same prefab for each variant', async () => {
for (const variant of [1, 2, 3]) {
const c = config(variant), calls = [], queued = [], cache = {};
const prefabPath = variant === 1 ? 'prefab/winStreak' : 'prefab/winStreak2';
const cacheKey = variant === 1 ? 'cachedWinStreakPrefab' : 'cachedWinStreak2Prefab';
const prefab = {};
let initialized = 0, instantiated;
const cc = { Prefab: {}, fx: { GameConfig: c, GameTool: { shushu_Track() {} } },
instantiate(value) { instantiated = value; return { getComponent: () => ({ init() { initialized++; } }) }; } };
const Home = loadMembers(homeFile, ['preloadHomePopupPrefabs', 'openWinStreak'], { cc, JiaZai: cache });
const home = new Home();
home.queueHomeAutomaticTask = (name, run) => queued.push({ name, run });
home.loadHomePopupPrefab = (...args) => { calls.push(args); return Promise.resolve(); };
home.isHomeRuntimeAlive = () => true;
home.preloadHomePopupPrefabs();
const streakTasks = queued.filter(task => task.name.startsWith('prefab/winStreak'));
assert.equal(streakTasks.length, 1);
streakTasks[0].run();
await Promise.resolve();
assert.deepEqual(calls, [['action_bundle', prefabPath, cacheKey, -10]]);
cache[cacheKey] = prefab;
home.vibrateButtonClick = () => {};
home.node = { addChild() {} };
home.openWinStreak({}, 'winStreak');
assert.equal(instantiated, prefab);
assert.equal(initialized, 1);
}
});
test('settings use the rainbow panel for B and C and keep the A panel', () => {
for (const variant of [1, 2, 3]) for (const hasRainbowPanel of [false, true]) {
const Settings = loadMembers('assets/Script/setUi.ts', ['onLoad'], { cc: { fx: { GameConfig: config(variant) } } });
const settings = new Settings(), original = {}, rainbow = hasRainbowPanel ? {} : null;
Object.assign(settings, { win: original, win2: rainbow, music: { children: [{}, {}] },
effect: { children: [{}, {}] }, vibrate: null });
settings.onLoad();
assert.equal(settings.win, hasRainbowPanel && variant !== 1 ? rainbow : original);
}
});