Dev: puppeteer-core 25 (browser tests adapted)

- puppeteer-core ^23.11.1 -> ^25.12.0 (dev only); npm audit: 7 high (basic-ftp via
  @puppeteer/browsers / proxy-agent, extract-zip) -> 0; production audit stays clean
- puppeteer-core 25 is ESM-only and needs Node >= 22.12: the render / sim browser suites
  load it with await import() instead of createRequire
- headless: 'new' -> headless: true (same new headless mode; 'new' is no longer a typed value)
- fieldview CPU throttle: page.emulateCPUThrottling() instead of the deprecated page.target() CDP session
- THIRD-PARTY-NOTICES version, DESIGN test note
This commit is contained in:
sganggs committed 2026-10-02 17:58:35 +08:00
1 parent bb5c792a4d
commit 7f134737ce
27 files changed
+193 -948

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ them (with `node_modules/`, which keeps each package's own licence file).
| [htm](https://github.com/developit/htm) | 3.1.1 | Apache-2.0 | UI templates — `public/vendor/htm.module.js` | no (npm) | yes | | [htm](https://github.com/developit/htm) | 3.1.1 | Apache-2.0 | UI templates — `public/vendor/htm.module.js` | no (npm) | yes |
| [three.js](https://github.com/mrdoob/three.js) | 0.186.1 | MIT | official 3D board — `public/vendor/three.core.js`, `three.module.js` | no (npm) | yes | | [three.js](https://github.com/mrdoob/three.js) | 0.186.1 | MIT | official 3D board — `public/vendor/three.core.js`, `three.module.js` | no (npm) | yes |
| [ws](https://github.com/websockets/ws) | 8.22.0 | MIT | WebSocket server (`server/`) | no (npm) | yes (`node_modules/`) | | [ws](https://github.com/websockets/ws) | 8.22.0 | MIT | WebSocket server (`server/`) | no (npm) | yes (`node_modules/`) |
| [puppeteer-core](https://github.com/puppeteer/puppeteer) | 23.11.1 | Apache-2.0 | optional browser tests (dev dependency) | no (npm) | no | | [puppeteer-core](https://github.com/puppeteer/puppeteer) | 25.12.0 | Apache-2.0 | optional browser tests (dev dependency) | no (npm) | no |
| [Ark-Unpacker](https://github.com/isHarryh/Ark-Unpacker) LZ4AK decoder | — | BSD-3-Clause | `tools/local-extract/aklz4.py` (optional local extraction) | **yes** — keeps its notice; full text also in `tools/local-extract/LICENSE-Ark-Unpacker.txt` | yes | | [Ark-Unpacker](https://github.com/isHarryh/Ark-Unpacker) LZ4AK decoder | — | BSD-3-Clause | `tools/local-extract/aklz4.py` (optional local extraction) | **yes** — keeps its notice; full text also in `tools/local-extract/LICENSE-Ark-Unpacker.txt` | yes |
| [UnityPy](https://github.com/K0lb3/UnityPy) (via MooncellWiki/UnityPy), [lz4](https://github.com/python-lz4/python-lz4), [Pillow](https://github.com/python-pillow/Pillow) | see `tools/local-extract/requirements.txt` | MIT / BSD-3-Clause / MIT-CMU | optional local extraction; installed by pip into `.venv-extract` only when the host opts in | no | no | | [UnityPy](https://github.com/K0lb3/UnityPy) (via MooncellWiki/UnityPy), [lz4](https://github.com/python-lz4/python-lz4), [Pillow](https://github.com/python-pillow/Pillow) | see `tools/local-extract/requirements.txt` | MIT / BSD-3-Clause / MIT-CMU | optional local extraction; installed by pip into `.venv-extract` only when the host opts in | no | no |
+1 -1
View File
@@ -31,7 +31,7 @@ Out of scope v1: matchmaking queue, training/tutorial, DIY (甄选) slots (the 4
- **Server-authoritative simulation.** Clients send *intents*; the server validates, mutates state and pushes state/snapshots. - **Server-authoritative simulation.** Clients send *intents*; the server validates, mutates state and pushes state/snapshots.
- **Client:** static files, native ES modules. Vendored libs in `public/vendor/`: `pixi.min.js` (PixiJS **7.4.2** UMD, global `PIXI`), `pixi-spine.js` (**4.0.6** UMD, `PIXI.spine`), `preact.module.js` + `hooks.module.js` + `htm.module.js` (Preact 10 + htm, no build step). No CDN at runtime (LAN play must work offline). - **Client:** static files, native ES modules. Vendored libs in `public/vendor/`: `pixi.min.js` (PixiJS **7.4.2** UMD, global `PIXI`), `pixi-spine.js` (**4.0.6** UMD, `PIXI.spine`), `preact.module.js` + `hooks.module.js` + `htm.module.js` (Preact 10 + htm, no build step). No CDN at runtime (LAN play must work offline).
- **Shared code** in `shared/` is imported by both server and browser (pure ESM, no Node APIs). - **Shared code** in `shared/` is imported by both server and browser (pure ESM, no Node APIs).
- Tests: `node --test` (`test/**/*.test.js`). Browser E2E: `puppeteer-core` driving the system Chrome (dev dependency, optional). - Tests: `node --test` (`test/**/*.test.js`). Browser E2E: `puppeteer-core` driving the system Chrome (dev dependency, optional; puppeteer-core 25 is ESM-only and needs Node ≥ 22.12, so the suites load it with `await import`).
Run: `npm install && npm run assets && npm start` → `http://localhost:3000`. Friends on LAN use `http://<host-ip>:3000`. Internet play: a tunnel (e.g. `cloudflared tunnel --url http://localhost:3000`) or any Node host (Dockerfile provided). Run: `npm install && npm run assets && npm start` → `http://localhost:3000`. Friends on LAN use `http://<host-ip>:3000`. Internet play: a tunnel (e.g. `cloudflared tunnel --url http://localhost:3000`) or any Node host (Dockerfile provided).
+150 -878
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -46,6 +46,6 @@
"ws": "^8.22.0" "ws": "^8.22.0"
}, },
"devDependencies": { "devDependencies": {
"puppeteer-core": "^23.11.1" "puppeteer-core": "^25.12.0"
} }
} }
+1 -1
View File
@@ -95,7 +95,7 @@ export class Client {
async open(query = '') { async open(query = '') {
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
this.browser = await this.puppeteer.launch({ executablePath: CHROME, headless: 'new', args: CHROME_ARGS, protocolTimeout: 90000 }); this.browser = await this.puppeteer.launch({ executablePath: CHROME, headless: true, args: CHROME_ARGS, protocolTimeout: 90000 });
const [first] = await this.browser.pages(); const [first] = await this.browser.pages();
const page = first || await this.browser.newPage(); const page = first || await this.browser.newPage();
this.page = page; this.page = page;
+2 -4
View File
@@ -12,7 +12,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'; import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -31,11 +30,10 @@ describe('3D board in headless Chrome', { skip }, () => {
let srv, browser; let srv, browser;
const perf = { generated: new Date().toISOString(), views: [] }; const perf = { generated: new Date().toISOString(), views: [] };
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run', '--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run', '--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -12,7 +12,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -46,11 +45,10 @@ function installBoss() {
describe('a huge boss is picked anywhere on its hit area (headless Chrome)', { skip }, () => { describe('a huge boss is picked anywhere on its hit area (headless Chrome)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -10,7 +10,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -21,11 +20,10 @@ const skip = enabled ? false : 'set RENDER_E2E=1 (needs Chrome and downloaded as
describe('render engine in headless Chrome', { skip }, () => { describe('render engine in headless Chrome', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -13,7 +13,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -54,11 +53,10 @@ function installFeed() {
describe('knocked-out operators and element gauges in headless Chrome', { skip }, () => { describe('knocked-out operators and element gauges in headless Chrome', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -15,7 +15,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { makeBattle } from '../helpers/battleHarness.js'; import { makeBattle } from '../helpers/battleHarness.js';
import { ELEMENT_ORDER } from '../../server/sim/constants.js'; import { ELEMENT_ORDER } from '../../server/sim/constants.js';
import { ELEMENT_RING } from '../../public/js/render/textures.js'; import { ELEMENT_RING } from '../../public/js/render/textures.js';
@@ -59,11 +58,10 @@ function readGauge(id) {
describe('element gauge row in headless Chrome (user playtest #6)', { skip }, () => { describe('element gauge row in headless Chrome (user playtest #6)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+4 -7
View File
@@ -13,7 +13,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -26,11 +25,10 @@ describe('field view features in headless Chrome', { skip }, () => {
let srv, browser; let srv, browser;
const perf = { generated: new Date().toISOString(), throttle: 4, views: [] }; const perf = { generated: new Date().toISOString(), throttle: 4, views: [] };
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run', '--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run', '--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
@@ -297,8 +295,7 @@ describe('field view features in headless Chrome', { skip }, () => {
for (const [name, q] of PERF) { for (const [name, q] of PERF) {
const { page, problems } = await open(`${q}&board=${board}`, 1920, 1080); const { page, problems } = await open(`${q}&board=${board}`, 1920, 1080);
await wait(2500); await wait(2500);
const cdp = await page.target().createCDPSession(); await page.emulateCPUThrottling(4);
await cdp.send('Emulation.setCPUThrottlingRate', { rate: 4 });
await wait(1500); await wait(1500);
const r = await page.evaluate(async () => { const r = await page.evaluate(async () => {
const times = []; const times = [];
@@ -310,7 +307,7 @@ describe('field view features in headless Chrome', { skip }, () => {
const st = window.__demo.stats(); const st = window.__demo.stats();
return { fps: Math.round((1000 / mean) * 10) / 10, cpuMs: st.cpuMs, renderMs: st.renderMs, units: st.units, pen: st.pen, lod: st.lod, culled: st.culled }; return { fps: Math.round((1000 / mean) * 10) / 10, cpuMs: st.cpuMs, renderMs: st.renderMs, units: st.units, pen: st.pen, lod: st.lod, culled: st.culled };
}); });
await cdp.send('Emulation.setCPUThrottlingRate', { rate: 1 }); await page.emulateCPUThrottling(null);
await page.close(); await page.close();
assert.deepEqual(problems, []); assert.deepEqual(problems, []);
perf.views.push({ name, board, ...r }); perf.views.push({ name, board, ...r });
+2 -4
View File
@@ -15,7 +15,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -27,11 +26,10 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
describe('user playtest #4 item 13: no screen-sized UI flash (mock battle, headless Chrome)', { skip }, () => { describe('user playtest #4 item 13: no screen-sized UI flash (mock battle, headless Chrome)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run', '--force-device-scale-factor=1'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run', '--force-device-scale-factor=1'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -12,7 +12,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { buildBattleSpec } from '../../server/sim/spec.js'; import { buildBattleSpec } from '../../server/sim/spec.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
@@ -32,11 +31,10 @@ const SPEC = buildBattleSpec({
describe('掠海漂移体 drops to 爬行模式: its model crawls (headless Chrome, real sim)', { skip }, () => { describe('掠海漂移体 drops to 爬行模式: its model crawls (headless Chrome, real sim)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -23,7 +23,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const OUT = path.join(ROOT, 'test/e2e/out'); const OUT = path.join(ROOT, 'test/e2e/out');
@@ -96,11 +95,10 @@ function installHelpers() {
describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip }, () => { describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run', '--force-device-scale-factor=1'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run', '--force-device-scale-factor=1'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
@@ -12,7 +12,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { buildBattleSpec } from '../../server/sim/spec.js'; import { buildBattleSpec } from '../../server/sim/spec.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
@@ -37,11 +36,10 @@ const SPEC = buildBattleSpec({
describe('enemy model sizes and the 萨科塔 flutter (headless Chrome, real sim + real models)', { skip }, () => { describe('enemy model sizes and the 萨科塔 flutter (headless Chrome, real sim + real models)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -13,7 +13,6 @@ import assert from 'node:assert/strict';
import { existsSync, mkdirSync } from 'node:fs'; import { existsSync, mkdirSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { buildBattleSpec } from '../../server/sim/spec.js'; import { buildBattleSpec } from '../../server/sim/spec.js';
import { GEO } from '../../shared/constants.js'; import { GEO } from '../../shared/constants.js';
@@ -78,11 +77,10 @@ async function installSim(spec) {
describe('联防: an operator knocked out in its own combat enters down (headless Chrome, real sim)', { skip }, () => { describe('联防: an operator knocked out in its own combat enters down (headless Chrome, real sim)', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+2 -4
View File
@@ -15,7 +15,6 @@ import assert from 'node:assert/strict';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { PHASE, BOND_LAYER_CAP } from '../../shared/constants.js'; import { PHASE, BOND_LAYER_CAP } from '../../shared/constants.js';
import { createBattleFromSpec, resultDigest } from '../../server/sim/spec.js'; import { createBattleFromSpec, resultDigest } from '../../server/sim/spec.js';
import { DataSource } from '../../server/sim/simdata.js'; import { DataSource } from '../../server/sim/simdata.js';
@@ -86,11 +85,10 @@ function runField(S, ds, spec, maxSeconds) {
describe('Final Assault / Hidden Core fields in the browser sim', { skip }, () => { describe('Final Assault / Hidden Core fields in the browser sim', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
}); });
after(async () => { after(async () => {
await browser?.close(); await browser?.close();
+2 -4
View File
@@ -11,7 +11,6 @@ import assert from 'node:assert/strict';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { buildBattleSpec, createBattleFromSpec, resultDigest } from '../../server/sim/spec.js'; import { buildBattleSpec, createBattleFromSpec, resultDigest } from '../../server/sim/spec.js';
import { DataSource, getDefaultSource } from '../../server/sim/simdata.js'; import { DataSource, getDefaultSource } from '../../server/sim/simdata.js';
@@ -23,11 +22,10 @@ const skip = enabled ? false : 'set RENDER_E2E=1 or SIM_E2E=1 (needs Chrome)';
describe('operator loadouts in the browser sim', { skip }, () => { describe('operator loadouts in the browser sim', { skip }, () => {
let srv, browser; let srv, browser;
before(async () => { before(async () => {
const require = createRequire(path.join(ROOT, 'package.json')); const puppeteer = (await import('puppeteer-core')).default;
const puppeteer = require('puppeteer-core');
const { startServer } = await import('../../server/index.js'); const { startServer } = await import('../../server/index.js');
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-first-run'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-first-run'] });
}); });
after(async () => { after(async () => {
await browser?.close(); await browser?.close();
+3 -3
View File
@@ -48,7 +48,7 @@ describe('multi-device (Chrome device emulation)', { skip: !ENABLED && 'set SP_E
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
@@ -338,7 +338,7 @@ describe('multi-device (Chrome device emulation)', { skip: !ENABLED && 'set SP_E
mk(path.join(prof, 'Default'), { recursive: true }); mk(path.join(prof, 'Default'), { recursive: true });
// Chrome / Edge in Chinese locales clamp text to 12 px (webkit.webprefs.minimum_font_size) // Chrome / Edge in Chinese locales clamp text to 12 px (webkit.webprefs.minimum_font_size)
writeFileSync(path.join(prof, 'Default', 'Preferences'), JSON.stringify({ webkit: { webprefs: { minimum_font_size: 12, minimum_logical_font_size: 12 } } })); writeFileSync(path.join(prof, 'Default', 'Preferences'), JSON.stringify({ webkit: { webprefs: { minimum_font_size: 12, minimum_logical_font_size: 12 } } }));
const b12 = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'], userDataDir: prof }); const b12 = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'], userDataDir: prof });
try { try {
for (const b of [browser, b12]) { for (const b of [browser, b12]) {
for (const [w, h, touch] of [[1280, 720], [1366, 657], [1920, 1080], [2560, 1440], [844, 390, true], [640, 360, true]]) { for (const [w, h, touch] of [[1280, 720], [1366, 657], [1920, 1080], [2560, 1440], [844, 390, true], [640, 360, true]]) {
@@ -390,7 +390,7 @@ describe('multi-device (Chrome device emulation)', { skip: !ENABLED && 'set SP_E
test('graceful degradation: no WebGL → the DOM view (with a notice); no local-client art → CSS look-alikes', async () => { test('graceful degradation: no WebGL → the DOM view (with a notice); no local-client art → CSS look-alikes', async () => {
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
const nogl = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--disable-webgl', '--disable-webgl2', '--disable-3d-apis'] }); const nogl = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--disable-webgl', '--disable-webgl2', '--disable-3d-apis'] });
try { try {
const { page, problems } = await open('ipad', 'phase=PREP', { b: nogl }); const { page, problems } = await open('ipad', 'phase=PREP', { b: nogl });
await page.waitForFunction(() => globalThis.__SP_VIEW__?.kind === 'fallback', { timeout: 20000 }); await page.waitForFunction(() => globalThis.__SP_VIEW__?.kind === 'fallback', { timeout: 20000 });
+1 -1
View File
@@ -31,7 +31,7 @@ describe('official emotes in the browser', { skip: !ENABLED && 'set SP_E2E=1 (an
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--force-device-scale-factor=1'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--force-device-scale-factor=1'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { after(async () => {
+1 -1
View File
@@ -42,7 +42,7 @@ describe('client leftovers — mock harness', { skip: !ENABLED && 'set SP_E2E=1
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--force-device-scale-factor=1'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--force-device-scale-factor=1'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { await browser?.close(); await srv?.close(); }); after(async () => { await browser?.close(); await srv?.close(); });
+1 -1
View File
@@ -36,7 +36,7 @@ describe('干员调配 overlay (real server, headless Chrome)', { skip: !ENABLED
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
after(async () => { await browser?.close(); await srv?.close(); }); after(async () => { await browser?.close(); await srv?.close(); });
+1 -1
View File
@@ -47,7 +47,7 @@ describe('in-match UI (mock harness, headless Chrome)', { skip: !ENABLED && 'set
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
+1 -1
View File
@@ -47,7 +47,7 @@ describe('user playtest #2 — UI fixes (mock harness, headless Chrome)', { skip
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--force-device-scale-factor=1'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--force-device-scale-factor=1'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
+1 -1
View File
@@ -53,7 +53,7 @@ describe('user playtest #5 — UI items 8 / 9 (mock harness, headless Chrome)',
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
+1 -1
View File
@@ -50,7 +50,7 @@ describe('user playtest #6 — UI items 6 / 10 (mock harness, headless Chrome)',
const puppeteer = (await import('puppeteer-core')).default; const puppeteer = (await import('puppeteer-core')).default;
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true }); srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
base = `http://127.0.0.1:${srv.port}`; base = `http://127.0.0.1:${srv.port}`;
browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox'] }); browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox'] });
mkdirSync(OUT, { recursive: true }); mkdirSync(OUT, { recursive: true });
}); });
+1 -1
View File
@@ -42,7 +42,7 @@ class Client {
} }
async open(query = '') { async open(query = '') {
this.browser = await this.puppeteer.launch({ executablePath: CHROME, headless: 'new', args: CHROME_ARGS, protocolTimeout: 60000 }); this.browser = await this.puppeteer.launch({ executablePath: CHROME, headless: true, args: CHROME_ARGS, protocolTimeout: 60000 });
const [first] = await this.browser.pages(); const [first] = await this.browser.pages();
const page = first || await this.browser.newPage(); const page = first || await this.browser.newPage();
this.page = page; this.page = page;