feedback2 WL: 干员调配 shows 局内数值 — the chosen skill / module's stats, 攻击范围, 特性 and 天赋, 精锐 first with a 普通 toggle, drawn by the card's own block (GitHub #64)
This commit is contained in:
1 parent
41bfdb5f96
commit
4c78ab94e6
5 files changed
+470
-4
No files matched your search
@@ -273,4 +273,112 @@ describe('干员调配 overlay (real server, headless Chrome)', { skip: !ENABLED
|
||||
assert.deepEqual(problems, []);
|
||||
await ctx.close();
|
||||
});
|
||||
|
||||
// ---- 局内数值 (GitHub issue #64) -------------------------------------------------------------------------------------
|
||||
const CHESS = () => JSON.parse(readFileSync(path.join(ROOT, 'data/chess.json'), 'utf8'));
|
||||
const fmt = (n) => Math.round(n).toLocaleString('en-US');
|
||||
/** The section's eight stats: label → shown value. */
|
||||
const shownStats = (page) => page.$$eval('.lo-sec--stats .dstat', (els) => Object.fromEntries(els.map((e) => [e.querySelector('.dstat__k').textContent, e.querySelector('.dstat__v').textContent])));
|
||||
const rangeTiles = (page) => page.$$eval('.lo-sec--stats .rgrid i.on', (els) => els.length);
|
||||
async function pickChess(page, name) {
|
||||
await page.$eval('.lo-search input', (el) => { el.focus(); el.select(); });
|
||||
await page.keyboard.press('Backspace');
|
||||
await page.type('.lo-search input', name);
|
||||
await page.waitForFunction((n) => document.querySelectorAll('.lo-card').length === 1 && document.querySelector('.lo-card .lo-card__name')?.textContent === n, { timeout: 5000 }, name);
|
||||
await page.click('.lo-card');
|
||||
await page.waitForSelector('.lo-sec--stats .dstat', { visible: true });
|
||||
}
|
||||
|
||||
test('局内数值 (desktop): 隐现\'s stats follow the module and the 普通 / 精锐 toggle, the range follows 信仰搅拌机\'s SPT-Y', async () => {
|
||||
const chess = CHESS();
|
||||
const base = chess[INSIDE];
|
||||
const golden = chess[base.goldenId];
|
||||
const mod = golden.modules.find((m) => m.isDefault);
|
||||
const { ctx, page, problems } = await open();
|
||||
await clickSel(page, '.lobby-screen [data-testid="loadout-open"]');
|
||||
await page.waitForSelector('.lo .lo-card', { visible: true, timeout: 15000 });
|
||||
await pickChess(page, '隐现');
|
||||
// between the skills and the modules, 精锐 shown first: the default module's numbers, the 3 × 4 range
|
||||
assert.deepEqual(await page.$$eval('.lo-detail__body > .lo-sec > header h3', (els) => els.map((e) => e.firstChild.textContent)), ['技能', '局内数值', '模组']);
|
||||
const stats = await shownStats(page);
|
||||
assert.deepEqual(Object.keys(stats), ['生命上限', '攻击', '防御', '法术抗性', '攻击间隔', '阻挡数', '部署费用', '再部署']);
|
||||
assert.equal(stats['生命上限'], fmt(golden.statsBase.maxHp + mod.attr.maxHp));
|
||||
assert.equal(stats['攻击'], fmt(golden.statsBase.atk + mod.attr.atk));
|
||||
assert.equal(await rangeTiles(page), base.rangeGrid.length);
|
||||
assert.equal(await page.$eval('.lo-sec--stats', (el) => el.dataset.variant), 'elite');
|
||||
await page.screenshot({ path: path.join(OUT, 'loadout-stats-desktop.png') });
|
||||
// 不装备 → the base numbers; the skill choice changes nothing; 普通 → the normal chess; both toggles are independent
|
||||
await page.click('.lo-detail .lo-mod[data-module="none"]');
|
||||
await page.waitForFunction((v) => document.querySelector('.lo-sec--stats .dstat__v')?.textContent === v, {}, fmt(golden.statsBase.maxHp));
|
||||
assert.equal((await shownStats(page))['攻击'], fmt(golden.statsBase.atk));
|
||||
await page.click('.lo-detail .lo-skill[data-skill="0"]');
|
||||
assert.equal((await shownStats(page))['攻击'], fmt(golden.statsBase.atk), 'a skill is not a stat');
|
||||
await page.click('.lo-sec--stats .lo-seg button[data-variant="normal"]');
|
||||
await page.waitForFunction((v) => document.querySelector('.lo-sec--stats .dstat__v')?.textContent === v, {}, fmt(base.stats.maxHp));
|
||||
assert.equal(await page.$eval('.lo-sec--stats', (el) => el.dataset.variant), 'normal');
|
||||
assert.equal(await page.$eval('.lo-skill.is-on', (el) => el.dataset.skill), '0');
|
||||
assert.equal(await page.$eval('.lo-seg button.is-on', (el) => el.textContent.startsWith('普通')), true, 'the skill level toggle is its own');
|
||||
// the layout: no sideways overflow, the range box beside the numbers
|
||||
const box = await page.evaluate(() => {
|
||||
const body = document.querySelector('.lo-detail__body');
|
||||
const grid = document.querySelector('.lo-sec--stats .dstats').getBoundingClientRect();
|
||||
const range = document.querySelector('.lo-sec--stats .drange').getBoundingClientRect();
|
||||
return { over: body.scrollWidth - body.clientWidth, beside: range.left >= grid.right - 1 && Math.abs(range.top - grid.top) < 2 };
|
||||
});
|
||||
assert.ok(box.over <= 1 && box.beside, JSON.stringify(box));
|
||||
// 信仰搅拌机: SPT-Y's "攻击距离+1" draws one more tile in the 精锐 view (the toggle stays where it was left: 普通 has no
|
||||
// module, and says so); 不装备 goes back
|
||||
await pickChess(page, '信仰搅拌机');
|
||||
assert.match(await page.$eval('.lo-stats__cap', (el) => el.textContent), /普通干员没有模组/);
|
||||
await page.click('.lo-sec--stats .lo-seg button[data-variant="elite"]');
|
||||
const t0 = await rangeTiles(page);
|
||||
await page.click('.lo-detail .lo-mod[data-module="uniequip_003_rmixer"]');
|
||||
await page.waitForFunction((n) => document.querySelectorAll('.lo-sec--stats .rgrid i.on').length === n, {}, t0 + 1);
|
||||
await page.click('.lo-detail .lo-mod[data-module="none"]');
|
||||
await page.waitForFunction((n) => document.querySelectorAll('.lo-sec--stats .rgrid i.on').length === n, {}, t0);
|
||||
assert.deepEqual(problems, []);
|
||||
await ctx.close();
|
||||
});
|
||||
|
||||
test('局内数值 (phone 844×390, touch): the numbers, the range box and the 特性 / 天赋 card stay inside the narrow detail, text keeps its floor', async () => {
|
||||
const chess = CHESS();
|
||||
const golden = chess[chess[INSIDE].goldenId];
|
||||
const { ctx, page, problems } = await open({ w: 844, h: 390, touch: true });
|
||||
await page.tap('.lobby-screen [data-testid="loadout-open"]');
|
||||
await page.waitForSelector('.lo .lo-card', { visible: true, timeout: 15000 });
|
||||
await page.type('.lo-search input', '隐现');
|
||||
await page.waitForFunction(() => document.querySelectorAll('.lo-card').length === 1, { timeout: 5000 });
|
||||
await page.tap('.lo-card');
|
||||
await page.waitForSelector('.lo-sec--stats .dstat', { visible: true });
|
||||
await page.$eval('.lo-sec--stats', (el) => el.scrollIntoView({ block: 'start' }));
|
||||
const m = await page.evaluate(() => {
|
||||
const bodyEl = document.querySelector('.lo-detail__body');
|
||||
const body = bodyEl.getBoundingClientRect();
|
||||
const inside = (el) => { const b = el.getBoundingClientRect(); return b.left >= body.left - 1 && b.right <= body.right + 1 && b.width > 0; };
|
||||
const fs = (sel) => parseFloat(getComputedStyle(document.querySelector(sel)).fontSize);
|
||||
const grid = document.querySelector('.lo-sec--stats .dstats').getBoundingClientRect();
|
||||
const range = document.querySelector('.lo-sec--stats .drange').getBoundingClientRect();
|
||||
return {
|
||||
over: bodyEl.scrollWidth - bodyEl.clientWidth,
|
||||
inside: [...document.querySelectorAll('.lo-sec--stats .dstat, .lo-sec--stats .drange, .lo-sec--stats .lo-minfo--kit')].every(inside),
|
||||
// beside the numbers when the column is wide enough, else under them — never over them
|
||||
laidOut: range.left >= grid.right - 1 || range.top >= grid.bottom - 1,
|
||||
k: fs('.lo-sec--stats .dstat__k'), v: fs('.lo-sec--stats .dstat__v'), cap: fs('.lo-stats__cap'), row: fs('.lo-sec--stats .lo-minfo__v'),
|
||||
tab: Math.round(document.querySelector('.lo-sec--stats .lo-seg button').getBoundingClientRect().height),
|
||||
cell: Math.min(...[...document.querySelectorAll('.lo-sec--stats .dstat')].map((c) => Math.round(c.getBoundingClientRect().width))),
|
||||
clipped: [...document.querySelectorAll('.lo-sec--stats .dstat__k')].filter((e) => e.scrollWidth > e.clientWidth).map((e) => e.textContent),
|
||||
};
|
||||
});
|
||||
assert.ok(m.over <= 1 && m.inside && m.laidOut, JSON.stringify(m));
|
||||
assert.ok(m.k >= 9 && m.v >= 12 && m.cap >= 9 && m.row >= 10, `text floors ${JSON.stringify(m)}`);
|
||||
assert.ok(m.tab >= 22, `the toggle stays tappable ${JSON.stringify(m)}`);
|
||||
assert.deepEqual(m.clipped, [], `no stat label is cut off ${JSON.stringify(m)}`);
|
||||
await page.screenshot({ path: path.join(OUT, 'loadout-stats-phone.png') });
|
||||
// a choice made further down updates it (touch: the module card, 不装备)
|
||||
await page.tap('.lo-detail .lo-mod[data-module="none"]');
|
||||
await page.waitForFunction((v) => document.querySelector('.lo-sec--stats .dstat__v')?.textContent === v, {}, fmt(golden.statsBase.maxHp));
|
||||
assert.equal((await shownStats(page))['攻击'], fmt(golden.statsBase.atk));
|
||||
assert.deepEqual(problems, []);
|
||||
await ctx.close();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user