freezmo/tests/run-all.mjs
Martin Dvorak 6b6a6ea158 Ice scene: the glossy orbs become rising bubbles, ~38% more transparent
- Orbs rise from below the bottom edge to above the top edge, each with
  its own 36-68s pass (slower on phones), a negative delay (scene already
  populated on load), own horizontal position, a gentle sway and a slight
  scale drift; opacity fades in/out at the edges so the loop never pops.
- Only transform + opacity animate, each orb on its own composited layer
  (will-change: transform); no filter/backdrop-filter, nothing animates
  size/offsets/shadow. The glacier blobs are untouched.
- Opacity lowered ~38% in both themes: 0.55->0.34 light, 0.8->0.5 dark,
  phone overrides 0.32->0.2 / 0.42->0.26.
- prefers-reduced-motion: rise removed, orbs return to the old static
  placements (a paused-from-start CSS animation is not applied in Chrome
  and would park them below the fold); tab hidden: a visibility listener
  parks the whole scene (.ice-paused), blob drift included.
- Measured with a CDP perf trace on a 4x throttled phone profile: p95
  33.4ms, 0 frames over 34ms, no long tasks - identical to the pre-change
  baseline; ~0.6% extra main-thread CPU. Desktop stays compositor-bound
  from the pre-existing blob drift, as before. tests/bubble-perf.mjs
  keeps it repeatable.
- New bubbles suite (34 checks) in the repo harness; the axe contrast
  matrix is now 16 runs (360/390/1280 x dark/light x EN/CS) and passes.
- SW cache -> freezmo-v31; README decision 46.

Full suite: 11/11 suites, 424 checks.
2026-10-11 15:50:17 +00:00

45 lines
1.3 KiB
JavaScript

/**
* Runs every Freezmo browser suite in order.
*
* npm run test:e2e # everything
* npm run test:e2e -- phase1 # only suites whose name contains "phase1"
*
* Each suite is a standalone script that talks to the running app on :3207
* (FREEZMO_URL overrides). Suites that need a stub Ollama or extra server
* instance start their own on their own port and clean up after themselves.
*/
import { spawn } from "node:child_process";
import path from "node:path";
const SUITES = [
"regression",
"bubbles-test",
"mobile-test",
"wizard-test",
"native-test",
"i18n-test",
"freeze-loading-test",
"phase1-test",
"phase2-test",
"phase3-test",
"review-test",
];
const filters = process.argv.slice(2);
const list = filters.length ? SUITES.filter((name) => filters.some((f) => name.includes(f))) : SUITES;
const here = path.dirname(new URL(import.meta.url).pathname);
const repo = path.resolve(here, "..");
let failed = 0;
for (const suite of list) {
console.log(`\n===== ${suite} =====`);
const code = await new Promise((resolve) => {
const child = spawn(process.execPath, [path.join(here, `${suite}.mjs`)], { cwd: repo, stdio: "inherit" });
child.on("close", resolve);
});
if (code !== 0) failed += 1;
}
console.log(`\n${list.length - failed}/${list.length} suites passed`);
process.exit(failed ? 1 : 0);