123 lines
5.7 KiB
JavaScript
123 lines
5.7 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const { loadMembers, withNewPlayerConfig } = require('./new-player-ab-test-support.cjs');
|
|
|
|
const imageNames = ['icon', 'Introduction', 'title'];
|
|
const readTable = name => JSON.parse(fs.readFileSync(`assets/resources/Json/${name}.json`, 'utf8')).NEW_LEVEL;
|
|
|
|
function setup(group = 'A') {
|
|
const config = withNewPlayerConfig({ GM_INFO: {
|
|
level: 6, abTestAssignments: { layer_1: { name: 'new1_50', group, enabled: true } },
|
|
} });
|
|
config.defaultNewLevel = readTable('NEW_LEVEL');
|
|
config.newPlayerNewLevel = readTable('NEW_LEVEL2');
|
|
const requests = [], errors = [];
|
|
const sprites = Object.fromEntries(imageNames.map(name => [name, { spriteFrame: { stale: true } }]));
|
|
const nodes = Object.fromEntries(imageNames.map(name => [name, {
|
|
width: 200, height: 100, scale: 0.75,
|
|
getComponent: () => sprites[name],
|
|
}]));
|
|
// Only the replacement image nodes exist; removed light/daoju children must not be required.
|
|
const newMode = { getChildByName: name => nodes[name] || null };
|
|
const cc = {
|
|
Sprite: class {}, SpriteFrame: class {}, fx: { GameConfig: config },
|
|
isValid: object => !!object && object.valid !== false,
|
|
error: (...args) => errors.push(args),
|
|
resources: { load(path, type, done) {
|
|
assert.equal(type, cc.SpriteFrame);
|
|
requests.push({ path, done });
|
|
} },
|
|
};
|
|
const Controller = loadMembers('assets/NewMode/script/NewMode.ts', [
|
|
'propName', 'imageLoadVersion', 'onEnable', 'onDisable',
|
|
], { cc });
|
|
const panel = Object.assign(new Controller(), {
|
|
node: { getChildByName: name => name === 'newmode' ? newMode : null },
|
|
});
|
|
return { config, requests, errors, sprites, nodes, panel };
|
|
}
|
|
|
|
for (const group of ['A', 'B']) {
|
|
test(`${group} uses each real unlock entry's image number, including 0, 33 and reversed 21/22`, () => {
|
|
const { panel, config, requests, sprites } = setup(group);
|
|
for (const entry of config.NEW_LEVEL) {
|
|
config.GM_INFO.level = entry.level - 1;
|
|
requests.length = 0;
|
|
panel.onEnable();
|
|
const number = entry.name.slice('daoju'.length);
|
|
assert.deepEqual(requests.map(request => request.path),
|
|
imageNames.map(name => `Window_Prop/${name}${number}`));
|
|
assert.ok(Object.values(sprites).every(sprite => sprite.spriteFrame === null));
|
|
for (const request of requests) request.done(null, request.path);
|
|
assert.deepEqual(Object.values(sprites).map(sprite => sprite.spriteFrame),
|
|
imageNames.map(name => `Window_Prop/${name}${number}`));
|
|
}
|
|
config.GM_INFO.level = group === 'B' ? 5 : 6;
|
|
requests.length = 0;
|
|
panel.onEnable();
|
|
assert.equal(requests[0].path, group === 'B' ? 'Window_Prop/icon2' : 'Window_Prop/icon0');
|
|
});
|
|
}
|
|
|
|
test('reopening refreshes all images and ignores a previous opening that finishes later', () => {
|
|
const { panel, config, requests, sprites } = setup();
|
|
panel.onEnable();
|
|
const first = requests.splice(0);
|
|
first[0].done(null, 'old icon');
|
|
panel.onDisable();
|
|
config.GM_INFO.level = 14;
|
|
panel.onEnable();
|
|
assert.ok(Object.values(sprites).every(sprite => sprite.spriteFrame === null));
|
|
for (const request of requests) request.done(null, request.path);
|
|
for (const request of first) request.done(null, 'late old image');
|
|
assert.deepEqual(Object.values(sprites).map(sprite => sprite.spriteFrame),
|
|
imageNames.map(name => `Window_Prop/${name}1`));
|
|
});
|
|
|
|
for (const [name, invalidate] of [
|
|
['closed panel', ({ panel }) => panel.onDisable()],
|
|
['destroyed panel', ({ panel }) => { panel.valid = false; }],
|
|
['destroyed sprites', ({ sprites }) => Object.values(sprites).forEach(sprite => { sprite.valid = false; })],
|
|
]) {
|
|
test(`pending images cannot be assigned to a ${name}`, () => {
|
|
const context = setup();
|
|
context.panel.onEnable();
|
|
invalidate(context);
|
|
for (const request of context.requests) request.done(null, 'late image');
|
|
assert.ok(Object.values(context.sprites).every(sprite => sprite.spriteFrame === null));
|
|
});
|
|
}
|
|
|
|
test('one failed image stays clear while successful images are displayed', () => {
|
|
const { panel, requests, errors, sprites } = setup();
|
|
panel.onEnable();
|
|
requests[0].done(new Error('image unavailable'), 'invalid image');
|
|
requests[1].done(null, 'introduction image');
|
|
requests[2].done(null, 'title image');
|
|
assert.equal(sprites.icon.spriteFrame, null);
|
|
assert.equal(sprites.Introduction.spriteFrame, 'introduction image');
|
|
assert.equal(sprites.title.spriteFrame, 'title image');
|
|
assert.equal(errors.length, 1);
|
|
});
|
|
|
|
test('unmatched and invalid config entries clear stale images without requesting assets', () => {
|
|
for (const name of [null, 'daoju34', 'daoju-1', 'daoju1.5', 'other2', 'daoju2extra']) {
|
|
const { panel, config, requests, sprites } = setup();
|
|
config.defaultNewLevel = name === null ? [] : [{ level: 7, name }];
|
|
panel.onEnable();
|
|
assert.equal(requests.length, 0, String(name));
|
|
assert.ok(Object.values(sprites).every(sprite => sprite.spriteFrame === null), String(name));
|
|
}
|
|
});
|
|
|
|
test('missing image children are tolerated and existing nodes keep their layout', () => {
|
|
const { panel, nodes, sprites, requests } = setup();
|
|
delete nodes.Introduction;
|
|
delete sprites.title;
|
|
panel.onEnable();
|
|
assert.deepEqual(requests.map(request => request.path), ['Window_Prop/icon0']);
|
|
requests[0].done(null, 'icon image');
|
|
assert.deepEqual([nodes.icon.width, nodes.icon.height, nodes.icon.scale], [200, 100, 0.75]);
|
|
});
|