173 lines
9.9 KiB
JavaScript
173 lines
9.9 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { loadMembers, loadModule } = require('./new-player-ab-test-support.cjs');
|
|
|
|
function fixture(sceneName, shopState = 'open') {
|
|
const info = { coin: 100, userPowerTime: 0, hp: 5, hp_Max: 5,
|
|
hammerAmount: 0, freezeAmount: 0, magicAmount: 0, iosStarterOrder: 'starter-order' };
|
|
const events = [], timers = [], pending = {};
|
|
const snapshot = kind => events.push({ kind, coin: info.coin, power: info.userPowerTime,
|
|
hp: info.hp, hammer: info.hammerAmount, freeze: info.freezeAmount, magic: info.magicAmount });
|
|
class JiaZai {}
|
|
const home = Object.assign(new JiaZai(), {
|
|
startStarter_pack() {}, updateCoin() { snapshot('home-coin'); },
|
|
updatePower() { snapshot('home-power'); },
|
|
setHealthInfo() { snapshot('home-health'); }, startTimeCutDown() {},
|
|
});
|
|
const cc = { isValid: value => !!value && !value.destroyed, warn: (...args) => assert.fail(args.join(' ')),
|
|
fx: { GameConfig: { GM_INFO: info }, StorageMessage: { setStorage() {} }, GameTool: {
|
|
shopBuy(id) {
|
|
if (id === 'starter_pack') {
|
|
info.coin += 3000; info.userPowerTime += 1800;
|
|
info.hammerAmount += 3; info.freezeAmount += 3; info.magicAmount += 3;
|
|
} else {
|
|
assert.equal(id, 'month_Card'); info.coin += 6000;
|
|
}
|
|
snapshot('grant');
|
|
},
|
|
getMonthlyCardValidityDays: () => Promise.resolve({ days: 30, time: '2026-09-29T00:00:00Z' }),
|
|
checkExpiration: () => true, setUserHealth: (_, done) => done({}), getHealth() {},
|
|
getPassCheckPorp(id, count) {
|
|
const fields = { 1001: 'coin', 2001: 'freezeAmount', 2002: 'hammerAmount',
|
|
2003: 'magicAmount', 3004: 'userPowerTime' };
|
|
assert.ok(fields[id], `unexpected reward ${id}`);
|
|
info[fields[id]] += count; snapshot('grant');
|
|
},
|
|
} },
|
|
};
|
|
const MapConroler = { _instance: { node: {},
|
|
updateCoin() { snapshot('game-coin'); }, setPropNum() { snapshot('game-props'); } } };
|
|
const SceneManager = loadMembers('assets/Script/SceneManager.ts',
|
|
['refreshJungleRewardDisplay', 'updateCoin'], { cc, MapConroler });
|
|
const game = new SceneManager();
|
|
const NumberToImage = {
|
|
numberToImageNodes() {},
|
|
numberToImageNodesCoin(value) { snapshot('shop-coin'); assert.equal(value, info.coin); },
|
|
};
|
|
const Shop = loadMembers('assets/shop/script/shop.ts', ['refreshJungleRewardDisplay'], { cc, NumberToImage });
|
|
const shop = Object.assign(new Shop(), {
|
|
node: { activeInHierarchy: shopState === 'open' }, coin: {}, currentCoin: 100, coinAnimating: true,
|
|
refreshJungleEntryLayout() {}, refreshActivityEntries() {}, updateIcon() {},
|
|
openShop() { snapshot('shop-open'); }, updatePower() { snapshot('shop-power'); },
|
|
});
|
|
const shopNode = { activeInHierarchy: shopState === 'open', getComponent: name => name === 'shop' ? shop : null };
|
|
const canvas = {
|
|
children: shopState === 'absent' ? [] : [shopNode],
|
|
getChildByName: name => name === 'shop' && shopState !== 'absent' ? shopNode : null,
|
|
getComponent(type) {
|
|
if ((type === JiaZai || type === 'JiaZai') && sceneName === 'HomeScene') return home;
|
|
if ((type === SceneManager || type === 'SceneManager') && sceneName === 'GameScene') return game;
|
|
return null;
|
|
},
|
|
};
|
|
cc.find = name => name === 'Canvas' ? canvas : name === 'Canvas/shop' && shopState !== 'absent' ? shopNode : null;
|
|
const globals = { cc, JiaZai, SceneManager, NumberToImage,
|
|
console: { log() {} }, MiniGameSdk: { API: { yinli_Pay() {}, showToast() {} } },
|
|
setTimeout: (callback, delay) => { timers.push({ callback, delay }); return timers.length; },
|
|
Utils: { setMonthlyCard: (_, callback, order) => { pending.monthly = callback; pending.order = order; } },
|
|
};
|
|
const Gift = loadMembers('assets/action_bundle/script/NewbieGift.ts', ['completeOrder', 'buyStarter_pack'], { ...globals });
|
|
const gift = Object.assign(new Gift(), { logPayment() {}, closeLoad() {},
|
|
closeStarter_pack() { snapshot('gift-close'); } });
|
|
const Monthly = loadMembers('assets/Script/monthlyCard.ts', ['buyMonthCard', 'setReward'], { ...globals });
|
|
const monthly = Object.assign(new Monthly(), { closeLoad() {}, refreshCardState() {}, monthCardTime: {} });
|
|
const Jungle = loadModule('assets/jungle_treasure/script/JungleRewardService.ts', {}, { cc }).default;
|
|
return { info, events, timers, pending, gift, monthly, shop, jungle: new Jungle(), snapshot };
|
|
}
|
|
|
|
const kinds = f => f.events.map(event => event.kind);
|
|
function assertShopShowsLatest(f) {
|
|
const coin = f.events.find(event => event.kind === 'shop-coin');
|
|
const power = f.events.find(event => event.kind === 'shop-power');
|
|
assert.ok(coin, 'shop coin digits refreshed');
|
|
assert.ok(power, 'shop stamina refreshed');
|
|
assert.equal(coin.coin, f.info.coin);
|
|
assert.equal(power.power, f.info.userPowerTime);
|
|
assert.equal(power.hp, f.info.hp);
|
|
assert.equal(f.shop.currentCoin, f.info.coin);
|
|
assert.equal(f.shop.coinAnimating, false, 'old coin animation cannot overwrite the balance');
|
|
}
|
|
|
|
for (const scene of ['HomeScene', 'GameScene']) {
|
|
test(`${scene}: starter purchase refreshes the visible shop after coin and stamina arrive`, () => {
|
|
const f = fixture(scene);
|
|
f.gift.completeOrder('starter-order');
|
|
assert.equal(f.info.coin, 3100); assert.equal(f.info.userPowerTime, 1800);
|
|
assert.equal(f.info.starterPackPurchased, true); assert.equal(f.info.iosStarterOrder, '');
|
|
assertShopShowsLatest(f);
|
|
const sceneCoin = f.events.find(event => event.kind === (scene === 'HomeScene' ? 'home-coin' : 'game-coin'));
|
|
assert.equal(sceneCoin.coin, 3100);
|
|
assert.ok(kinds(f).indexOf('grant') < kinds(f).indexOf('shop-coin'));
|
|
assert.equal(kinds(f).at(-1), 'gift-close');
|
|
if (scene === 'GameScene') assert.equal(f.events.find(event => event.kind === 'game-props').hammer, 3);
|
|
});
|
|
|
|
for (const shopState of ['closed', 'absent']) {
|
|
test(`${scene}: starter confirmation with ${shopState} shop still refreshes the scene safely`, () => {
|
|
const f = fixture(scene, shopState);
|
|
assert.doesNotThrow(() => f.gift.completeOrder('starter-order'));
|
|
assert.equal(f.info.coin, 3100);
|
|
assert.ok(!kinds(f).includes('shop-coin'));
|
|
assert.ok(kinds(f).includes(scene === 'HomeScene' ? 'home-coin' : 'game-coin'));
|
|
});
|
|
}
|
|
|
|
test(`${scene}: monthly rewards and refresh wait for server success`, async () => {
|
|
const f = fixture(scene);
|
|
f.monthly.buyMonthCard('month_Card', 'monthly-order');
|
|
assert.equal(f.pending.order, 'monthly-order');
|
|
assert.equal(f.info.coin, 100); assert.equal(f.events.length, 0);
|
|
assert.equal(f.timers.length, 0);
|
|
f.pending.monthly({ code: 1 });
|
|
assert.equal(f.info.coin, 6100); assert.equal(f.info.hp, 7);
|
|
assertShopShowsLatest(f);
|
|
assert.ok(kinds(f).indexOf('grant') < kinds(f).indexOf('shop-coin'));
|
|
assert.ok(kinds(f).indexOf('shop-open') < kinds(f).indexOf('shop-coin'), 'preserve original shop open flow');
|
|
if (scene === 'HomeScene') {
|
|
assert.ok(!kinds(f).includes('home-coin'), 'home retains its existing delayed refresh');
|
|
assert.equal(f.timers.length, 1); assert.equal(f.timers[0].delay, 300);
|
|
f.timers[0].callback();
|
|
assert.equal(f.events.find(event => event.kind === 'home-coin').coin, 6100);
|
|
assert.equal(f.events.find(event => event.kind === 'home-health').hp, 7);
|
|
} else {
|
|
assert.equal(f.events.find(event => event.kind === 'game-coin').coin, 6100);
|
|
}
|
|
await Promise.resolve();
|
|
});
|
|
|
|
test(`${scene}: unsuccessful monthly activation cannot grant or redraw reward balances`, () => {
|
|
const f = fixture(scene);
|
|
f.monthly.buyMonthCard('month_Card', 'monthly-order');
|
|
f.pending.monthly({ code: 0 });
|
|
assert.equal(f.info.coin, 100); assert.equal(f.events.length, 0); assert.equal(f.timers.length, 0);
|
|
});
|
|
|
|
test(`${scene}: monthly activation tolerates an absent shop`, async () => {
|
|
const f = fixture(scene, 'absent');
|
|
f.monthly.buyMonthCard('month_Card', 'monthly-order');
|
|
assert.doesNotThrow(() => f.pending.monthly({ code: 1 }));
|
|
f.timers.forEach(timer => timer.callback());
|
|
assert.equal(f.info.coin, 6100); assert.ok(!kinds(f).includes('shop-coin'));
|
|
assert.equal(f.events.find(event => event.kind === (scene === 'HomeScene' ? 'home-coin' : 'game-coin')).coin, 6100);
|
|
await Promise.resolve();
|
|
});
|
|
|
|
test(`${scene}: Jungle grants all reward types before refreshing shop and scene`, () => {
|
|
const f = fixture(scene);
|
|
const rewards = [{ type: 'coin', count: 1500 }, { type: 'hammer', count: 2 },
|
|
{ type: 'freeze', count: 3 }, { type: 'magic_wand', count: 4 }, { type: 'infinite_health', count: 1800 }];
|
|
f.jungle.grant({ emit: name => { assert.equal(name, 'jungle-reward-granted'); f.snapshot('jungle-event'); } }, rewards);
|
|
assert.equal(f.info.coin, 1600); assert.equal(f.info.userPowerTime, 1800);
|
|
assertShopShowsLatest(f);
|
|
assert.deepEqual(kinds(f).slice(0, rewards.length), Array(rewards.length).fill('grant'));
|
|
const sceneCoin = f.events.find(event => event.kind === (scene === 'HomeScene' ? 'home-coin' : 'game-coin'));
|
|
assert.equal(sceneCoin.coin, 1600);
|
|
if (scene === 'HomeScene') assert.equal(f.events.find(event => event.kind === 'home-power').power, 1800);
|
|
else {
|
|
const props = f.events.find(event => event.kind === 'game-props');
|
|
assert.deepEqual([props.hammer, props.freeze, props.magic], [2, 3, 4]);
|
|
}
|
|
assert.equal(kinds(f).at(-1), 'jungle-event');
|
|
});
|
|
}
|