feedback7 UIBACK: 准备就绪 folds the shop bar and cancelling the ready unfolds it again (GitHub #138 by blackyang40-cmd; the owner's decision of 2026-10-07 — dragging an operator by its body is not part of it): gameLogic readyShopFold over the confirmed m.private.ready, only a change inside one prep round counts (not a reconnect's first state, the new round's reset, a phase outside the prep); the enemy pen keeps the state it returns to; test/ui/ready-fold.test.js + ready-fold.e2e.test.js; golden: unchanged (client only)
This commit is contained in:
1 parent
911e4bff2b
commit
7fb41f4ff6
6 files changed
+153
-4
No files matched your search
@@ -0,0 +1,89 @@
|
||||
// Browser check of the shop bar's fold on 准备就绪 (GitHub #138; opt-in, ~10 s) on the in-match mock harness:
|
||||
// SP_E2E=1 CHROME_PATH=… node --test test/ui/ready-fold.e2e.test.js
|
||||
//
|
||||
// In prep, 准备就绪 folds the shop bar (the 展开商店 tab, the screen's `is-collapsed`), 取消准备 unfolds it again; a fold made
|
||||
// by hand before the press is not undone by it going the other way round (the player folds, readies, cancels: unfolded
|
||||
// — the owner's wording is 「取消准备时再展开」); the bar can still be unfolded by hand while ready. The decision itself
|
||||
// (only a change inside one prep round counts) is unit-tested in test/ui/ready-fold.test.js.
|
||||
|
||||
import { test, describe, before, after } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync } from 'node:fs';
|
||||
|
||||
const CHROME = process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
||||
const ENABLED = process.env.SP_E2E === '1' && existsSync(CHROME);
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
describe('the shop bar folds on 准备就绪', { skip: !ENABLED && 'set SP_E2E=1 (and have Chrome) to run' }, () => {
|
||||
let srv;
|
||||
let browser;
|
||||
let base;
|
||||
before(async () => {
|
||||
const { startServer } = await import('../../server/index.js');
|
||||
const puppeteer = (await import('puppeteer-core')).default;
|
||||
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
|
||||
base = `http://127.0.0.1:${srv.port}`;
|
||||
browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--force-device-scale-factor=1'] });
|
||||
});
|
||||
after(async () => {
|
||||
await browser?.close();
|
||||
await srv?.close();
|
||||
});
|
||||
|
||||
const state = (page) => page.evaluate(() => ({
|
||||
folded: !!document.querySelector('.shopbar-tab'),
|
||||
cls: document.querySelector('.gm')?.classList.contains('is-collapsed') ?? null,
|
||||
ready: document.querySelector('.readybtn')?.getAttribute('aria-pressed') === 'true',
|
||||
label: document.querySelector('.readybtn__label')?.textContent ?? null,
|
||||
}));
|
||||
const press = (page, sel) => page.evaluate((s) => document.querySelector(s)?.click(), sel);
|
||||
/** 准备就绪 with funds left asks first (gameLogic readyFundsPrompt): answer it; 取消准备 asks nothing. */
|
||||
const ready = async (page) => {
|
||||
await press(page, '.readybtn');
|
||||
await sleep(250);
|
||||
await page.evaluate(() => [...document.querySelectorAll('.modal button')].find((b) => /准备就绪/.test(b.textContent))?.click());
|
||||
};
|
||||
|
||||
test('ready folds the shop bar, cancelling unfolds it; the tab still unfolds it by hand', async () => {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1600, height: 900 });
|
||||
const problems = [];
|
||||
page.on('console', (m) => { if (m.type() === 'error') problems.push(`console: ${m.text()}`); });
|
||||
page.on('pageerror', (e) => problems.push(`pageerror: ${e.message}`));
|
||||
await page.goto(`${base}/dev/game-mock.html?phase=PREP&render=fallback`, { waitUntil: 'networkidle0', timeout: 60000 });
|
||||
await page.waitForSelector('.readybtn', { timeout: 15000 });
|
||||
await sleep(500);
|
||||
|
||||
let s = await state(page);
|
||||
assert.deepEqual([s.folded, s.cls, s.ready], [false, false, false], 'the shop starts open');
|
||||
await ready(page);
|
||||
await page.waitForSelector('.shopbar-tab', { timeout: 5000 });
|
||||
s = await state(page);
|
||||
assert.deepEqual([s.folded, s.cls, s.ready, s.label], [true, true, true, '取消准备'], '准备就绪 folded the shop bar');
|
||||
|
||||
// the player unfolds by hand while ready: it stays as they left it
|
||||
await press(page, '.shopbar-tab__btn');
|
||||
await sleep(300);
|
||||
s = await state(page);
|
||||
assert.deepEqual([s.folded, s.ready], [false, true]);
|
||||
|
||||
await ready(page); // 取消准备 with the bar already open: it stays open
|
||||
await sleep(300);
|
||||
s = await state(page);
|
||||
assert.deepEqual([s.folded, s.ready], [false, false]);
|
||||
|
||||
// fold by hand, ready, cancel: unfolded again (「取消准备时再展开」)
|
||||
await press(page, '.funds__collapse');
|
||||
await page.waitForSelector('.shopbar-tab', { timeout: 5000 });
|
||||
await ready(page);
|
||||
await sleep(400);
|
||||
s = await state(page);
|
||||
assert.deepEqual([s.folded, s.ready], [true, true], 'still folded after the press');
|
||||
await ready(page);
|
||||
await sleep(400);
|
||||
s = await state(page);
|
||||
assert.deepEqual([s.folded, s.ready], [false, false], 'cancelling unfolds it');
|
||||
assert.deepEqual(problems, []);
|
||||
await page.close();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
// GitHub #138 (the owner's decision of 2026-10-07: 「点准备后商店自动收起,取消准备时再展开」): pressing 准备就绪 folds the shop
|
||||
// bar in prep, cancelling the ready unfolds it. The decision is gameLogic readyShopFold(prev, cur) over the own CONFIRMED
|
||||
// ready state (m.private.ready): only a change inside one prep round counts. The screen's effect applies it
|
||||
// (screens/game.js; the browser check is test/ui/ready-fold.e2e.test.js).
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const { readyShopFold } = await import('../../public/js/ui/gameLogic.js');
|
||||
|
||||
test('readyShopFold: ready folds, cancelling unfolds — inside one prep round', () => {
|
||||
assert.equal(readyShopFold({ round: 6, ready: false }, { round: 6, ready: true }), 'fold');
|
||||
assert.equal(readyShopFold({ round: 6, ready: true }, { round: 6, ready: false }), 'unfold');
|
||||
assert.equal(readyShopFold({ round: 6, ready: false }, { round: 6, ready: false }), null, 'nothing changed');
|
||||
assert.equal(readyShopFold({ round: 6, ready: true }, { round: 6, ready: true }), null, 'nothing changed');
|
||||
// a truthy / falsy flag counts like a boolean (m.private carries true / false, a missing one is not ready)
|
||||
assert.equal(readyShopFold({ round: 6, ready: undefined }, { round: 6, ready: 1 }), 'fold');
|
||||
assert.equal(readyShopFold({ round: 6, ready: 1 }, { round: 6, ready: 0 }), 'unfold');
|
||||
});
|
||||
|
||||
test('readyShopFold: the first state seen, a new round\'s reset and anything outside a prep are no press', () => {
|
||||
// a reconnect in the middle of a prep (or the prep's start): no state before it
|
||||
assert.equal(readyShopFold(null, { round: 6, ready: true }), null);
|
||||
assert.equal(readyShopFold(undefined, { round: 6, ready: false }), null);
|
||||
// the next round starts not ready (the server resets it): not a 取消准备
|
||||
assert.equal(readyShopFold({ round: 6, ready: true }, { round: 7, ready: false }), null);
|
||||
assert.equal(readyShopFold({ round: 6, ready: false }, { round: 7, ready: true }), null, 'a new round never reads as a press either');
|
||||
// leaving the prep (combat, the draft, the observers): the screen passes null
|
||||
assert.equal(readyShopFold({ round: 6, ready: true }, null), null);
|
||||
assert.equal(readyShopFold(null, null), null);
|
||||
assert.equal(readyShopFold('x', { round: 6, ready: true }), null);
|
||||
});
|
||||
Reference in new issue
Block a user