Chromium launch profile & Silent QA
import { chromium } from '@playwright/test';
import { createChromiumLaunchProfile } from 'game-harness/chromium';
const { args, env } = createChromiumLaunchProfile({
gpuMode: process.platform === 'linux' && process.env.CI ? 'linux-hardware-vulkan' : 'auto',
});
const browser = await chromium.launch({ args, env, headless: false });createChromiumLaunchProfile() is the peer-free primitive behind both
definePlaywrightConfig() and defineBrowserTestConfig() — use it directly
when driving Chromium yourself (a custom launch script, production-runtime's
own internals, or a non-Playwright automation layer). It resolves one of
three renderer policies (auto leaves selection to Chromium; software
opts into SwiftShader explicitly; linux-hardware-vulkan applies the
reviewed Mesa/ANGLE flags plus EGL_PLATFORM=surfaceless for a runner
exposing /dev/dri/renderD128) and always de-duplicates and appends
--mute-audio last, so a caller-supplied arg list can never accidentally
drop the silence guard.
Every browser launched by definePlaywrightConfig() or
defineBrowserTestConfig() receives Chromium's --mute-audio argument as a
defense-in-depth guard, including projects with custom launch options. The
application must also expose a non-persistent mute mode so tests fail closed
before interacting with it:
import { activateSilentQa } from 'game-harness/silent-qa';
import { Howler } from 'howler';
// Evaluate before the rest of the application/audio graph.
export const silentQaActive = activateSilentQa(() => Howler.mute(true));activateSilentQa() detects ?muted by presence, invokes the consumer-owned
audio-engine callback, and only then publishes
<html data-audio-mode="muted-test">. It never reads or writes saved audio
preferences. Consumers use its boolean return value or isSilentQaActive()
to prevent later preference restoration from overriding the page-lifetime
mute. If the audio engine mutes asynchronously, await
activateSilentQaAsync(); passing a promise-returning callback to the
synchronous function throws instead of publishing a premature readiness
marker.
import { openSilentGame } from 'game-harness/playwright';
test('starts a game without audible QA', async ({ page }) => {
await openSilentGame(page, '/my-game/', { scenario: 'new-game' });
// The helper returns only after html[data-audio-mode="muted-test"] exists.
});openSilentGame() adds ?muted=1 (preserving other query parameters and the
fragment), navigates, and requires the application to set
data-audio-mode="muted-test" on <html>. The mute mode is runtime-only: it
must mute audio before any sound objects can play and must never write the
player's saved audio preference. Use silentTestUrl() when a test needs the
URL without navigating. Custom marker/query names are supported for legacy
adapters, but new games use these defaults. There is no audible-debug
exception: verify audio behavior through programmatic state, mocks, or
analyser assertions while agent-controlled playback remains muted.
Keep reuseExistingServer false for release evidence and assert the game
identity before exercising a journey. A process from another repository on a
familiar port must never be accepted as proof.
