storebound/tests/site-overflow.test.ts
CRT a5295db539 browser tests: deterministic under load
The afterAll hook of tests/console-fill.test.ts timed out (30 s) in 2 of 3
runs with 10 CPU hogs. Cause: Google Chrome (the fallback when playwright's
Chromium is not downloaded) hangs in its own shutdown after loading http
pages until its 10 s teardown watchdog fires; under load that passed 30 s.

- tests/helpers/browser.ts: one shared launch per file as a browser server
  with a 60 s launch timeout, killed at the end (throwaway profile, nothing
  to flush); a launch timeout fails instead of silently skipping; contexts
  and engine runs tracked per test and closed in afterEach (capped);
  event-driven waits for engine log lines
- console-fill: explicit test timeouts, slow tests marked, waits expected
  to succeed get a long cap (they return at once), sign-in / watching
  navTimeoutMs 1.5 s -> 5 s, persistent-profile Chromium closed in finally,
  temp account dirs removed
- site-overflow, console-session: same plumbing (site-overflow's afterAll
  had bun's 5 s default)
- console session: launchTimeoutMs option (tests only)
2026-10-12 04:54:07 +08:00

50 lines
2.9 KiB
TypeScript

// The site (site/index.html) never scrolls sideways: at every viewport width from 320 to 1440 px (10 px steps), in the
// light and the dark theme, document.documentElement.scrollWidth stays within innerWidth. On a failure it names the
// elements whose right edge passes the viewport outside any scroll container (pseudo-elements show as their owner).
// Uses the bundled playwright-core with its Chromium (or Google Chrome); skips when neither is installed (a launch that
// times out on a busy machine fails instead). One launch, the context closed right after each test, capped closes
// (tests/helpers/browser.ts). SLOW on purpose: 113 widths x 2 themes, each a resize and a layout read.
import { afterAll, afterEach, describe, expect, test } from 'bun:test';
import { join } from 'node:path';
import { AFTER_ALL_MS, AFTER_EACH_MS, launchTestBrowser, testScope } from './helpers/browser';
const page = 'file://' + join(import.meta.dir, '..', 'site', 'index.html');
const shared = await launchTestBrowser('playwright-core');
const { browser, skipWhy } = shared;
if (!browser) console.warn(`site overflow test skipped: no Chromium installed (${skipWhy}). Run \`bunx playwright install chromium\`.`);
const scope = testScope(() => browser);
// bounded: one context per test, each close capped (bun's 5 s default hook timeout was too tight for Chrome's slow shutdown)
afterEach(() => scope.settle(), AFTER_EACH_MS);
afterAll(() => shared.close(), AFTER_ALL_MS);
describe.skipIf(!browser)('site: no horizontal overflow', () => {
for (const scheme of ['light', 'dark'] as const) {
test(`320-1440 px, ${scheme}`, async () => {
const p = await scope.newPage({ colorScheme: scheme, viewport: { width: 1440, height: 900 } });
await p.goto(page, { waitUntil: 'load' });
const bad: string[] = [];
for (let w = 320; w <= 1440; w += 10) {
await p.setViewportSize({ width: w, height: 900 });
const r = await p.evaluate(() => {
const iw = innerWidth, sw = document.documentElement.scrollWidth;
if (sw <= iw) return null;
const els: string[] = [];
for (const el of document.querySelectorAll('body *')) {
const rc = el.getBoundingClientRect();
if (rc.width === 0 || rc.right <= iw + 0.5) continue;
let clipped = false;
for (let a = el.parentElement; a && a !== document.body; a = a.parentElement) {
if (getComputedStyle(a).overflowX !== 'visible') { clipped = true; break; }
}
if (!clipped) els.push(`${el.tagName.toLowerCase()}${el.id ? '#' + el.id : ''}${[...el.classList].map(c => '.' + c).join('')} right=${Math.round(rc.right)}`);
}
return `${iw}px: page ${sw - iw}px too wide${els.length ? ' (' + els.slice(0, 5).join(', ') + ')' : ' (a pseudo-element or transform)'}`;
});
if (r) bad.push(r);
}
expect(bad).toEqual([]);
}, 120_000);
}
});