e0a9332498
Lands the bun + Playwright + headless Chromium harness for the T16 cross-stack interop suite, parallel to the existing Rust hang-listen tier. New top-level `nestsClient-browser-interop/` directory with `@moq/lite` + `@moq/hang` 0.2.x pinned, a bun static + WebSocket back-channel server, and a Playwright runner that opens `listen.html` against the same `NativeMoqRelayHarness` moq-relay subprocess the Rust scenarios use. Kotlin side: `PlaywrightDriver` shells out to `bun x playwright test`, forwards the relay URL + leaf-cert SHA-256 (captured via a custom `CertCapturingValidator` during the speaker's QUIC handshake), and reads back Float32 LE PCM frames from a tempfile the bun WS server appends to. `BrowserInteropTest` ships I1 forward — Amethyst Kotlin speaker → Chromium `@moq/lite` listener, asserting FFT 440 Hz on the captured tail. Why pin via `serverCertificateHashes` instead of `--ignore-certificate-errors`: Chromium's flag does NOT bypass QUIC cert validation (crbug.com/1190655). `serverCertificateHashes` is the supported path; moq-relay's `--tls-generate` produces a 14-day ECDSA P-256 cert that satisfies the spec. Two Gradle tasks added: `interopBuildBrowserHarness` (bun install + bun build → dist/) and `interopInstallPlaywrightChromium` (skipped when `PLAYWRIGHT_BROWSERS_PATH` already has a chromium build, as on the agent runner). Verification: - `./gradlew :nestsClient:jvmTest --tests com.vitorpamplona.nestsclient.interop.native.BrowserInteropTest -DnestsHangInterop=true -DnestsBrowserInterop=true` green. - I1 forward asserts ≥ 1 s of decoded PCM with 440 Hz FFT peak. Looser sample-count bound than the hang-tier I1 because Chromium cold-launch + WebTransport handshake (3–5 s) + the publisher's `framesPerGroup = 5` per-subscriber cache cliff means the page captures only the broadcast tail. Phase 4.C (I2/I3/I4/I13/I14/I15) and 4.D (CI) are separate follow-up commits per the plan's per-scenario commit guidance. See: nestsClient/plans/2026-05-06-phase4-browser-harness.md https://claude.ai/code/session_01ERJPUYfdLPwZ99pr5EcEcV
57 lines
2.2 KiB
TypeScript
57 lines
2.2 KiB
TypeScript
import { defineConfig } from "@playwright/test";
|
|
|
|
// Phase 4 (T16) browser-interop Playwright config.
|
|
//
|
|
// One-off Chromium spawn per Kotlin test. We disable the default test
|
|
// projects + reporters (the runner is invoked headlessly from the
|
|
// PlaywrightDriver Kotlin shim with `--reporter list` for stdout
|
|
// streaming).
|
|
//
|
|
// Chromium flags:
|
|
// --enable-quic — required for WebTransport.
|
|
// --ignore-certificate-errors — accept the self-signed
|
|
// cert moq-relay generates
|
|
// with --tls-generate.
|
|
// --enable-features=AutoplayPolicy=NoUserGestureRequired
|
|
// — let AudioContext.resume()
|
|
// succeed without a user
|
|
// gesture (we're headless).
|
|
// --enable-blink-features=WebTransport
|
|
// — defensively re-enable in
|
|
// case the build disables
|
|
// the blink feature flag
|
|
// by default.
|
|
|
|
export default defineConfig({
|
|
testDir: "./tests",
|
|
fullyParallel: false,
|
|
workers: 1,
|
|
forbidOnly: !!process.env.CI,
|
|
retries: 0,
|
|
reporter: process.env.PLAYWRIGHT_REPORTER ?? "list",
|
|
timeout: 120_000,
|
|
use: {
|
|
headless: true,
|
|
trace: "off",
|
|
video: "off",
|
|
screenshot: "off",
|
|
launchOptions: {
|
|
args: [
|
|
"--enable-quic",
|
|
"--ignore-certificate-errors",
|
|
"--enable-features=AutoplayPolicy=NoUserGestureRequired",
|
|
"--enable-blink-features=WebTransport",
|
|
// Disable network sandbox so WebTransport over loopback
|
|
// doesn't trip the network service sandbox in headless.
|
|
"--disable-features=IsolateOrigins,site-per-process",
|
|
],
|
|
},
|
|
},
|
|
projects: [
|
|
{
|
|
name: "chromium",
|
|
use: { browserName: "chromium" },
|
|
},
|
|
],
|
|
});
|