game-harness 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/AGENTS.md +140 -0
  2. package/CHANGELOG.md +69 -0
  3. package/LICENSE +21 -0
  4. package/README.md +568 -0
  5. package/bin/test-harness-visual-battery.mjs +6 -0
  6. package/dist/cjs/bin/visual-battery.js +43 -0
  7. package/dist/cjs/browser-config.js +83 -0
  8. package/dist/cjs/chromium-launch.js +37 -0
  9. package/dist/cjs/index.js +10 -0
  10. package/dist/cjs/lighthouse.js +73 -0
  11. package/dist/cjs/package.json +3 -0
  12. package/dist/cjs/playwright-config.js +303 -0
  13. package/dist/cjs/production-runtime.js +315 -0
  14. package/dist/cjs/release-ladder.js +40 -0
  15. package/dist/cjs/silent-qa.js +75 -0
  16. package/dist/cjs/visual-battery.js +247 -0
  17. package/dist/esm/bin/visual-battery.js +41 -0
  18. package/dist/esm/browser-config.js +80 -0
  19. package/dist/esm/chromium-launch.js +34 -0
  20. package/dist/esm/index.js +3 -0
  21. package/dist/esm/lighthouse.js +70 -0
  22. package/dist/esm/playwright-config.js +292 -0
  23. package/dist/esm/production-runtime.js +304 -0
  24. package/dist/esm/release-ladder.js +37 -0
  25. package/dist/esm/silent-qa.js +67 -0
  26. package/dist/esm/visual-battery.js +239 -0
  27. package/dist/types/bin/visual-battery.d.cts +2 -0
  28. package/dist/types/bin/visual-battery.d.ts +2 -0
  29. package/dist/types/browser-config.d.cts +100 -0
  30. package/dist/types/browser-config.d.ts +100 -0
  31. package/dist/types/chromium-launch.d.cts +27 -0
  32. package/dist/types/chromium-launch.d.ts +27 -0
  33. package/dist/types/index.d.cts +3 -0
  34. package/dist/types/index.d.ts +3 -0
  35. package/dist/types/lighthouse.d.cts +44 -0
  36. package/dist/types/lighthouse.d.ts +44 -0
  37. package/dist/types/playwright-config.d.cts +122 -0
  38. package/dist/types/playwright-config.d.ts +122 -0
  39. package/dist/types/production-runtime.d.cts +105 -0
  40. package/dist/types/production-runtime.d.ts +105 -0
  41. package/dist/types/release-ladder.d.cts +38 -0
  42. package/dist/types/release-ladder.d.ts +38 -0
  43. package/dist/types/silent-qa.d.cts +47 -0
  44. package/dist/types/silent-qa.d.ts +47 -0
  45. package/dist/types/visual-battery.d.cts +69 -0
  46. package/dist/types/visual-battery.d.ts +69 -0
  47. package/docs/404.md +17 -0
  48. package/docs/agent-guide.md +10 -0
  49. package/docs/architecture.md +81 -0
  50. package/docs/assets/game-harness-hero.webp +0 -0
  51. package/docs/changelog.md +9 -0
  52. package/docs/contributing.md +21 -0
  53. package/docs/entry-points.md +41 -0
  54. package/docs/getting-started.md +42 -0
  55. package/docs/guides/chromium-and-silent-qa.md +72 -0
  56. package/docs/guides/lighthouse-and-release-ladder.md +62 -0
  57. package/docs/guides/playwright.md +58 -0
  58. package/docs/guides/production-runtime.md +61 -0
  59. package/docs/guides/visual-battery.md +58 -0
  60. package/docs/guides/vitest.md +41 -0
  61. package/docs/introduction.md +29 -0
  62. package/docs/package.json +13 -0
  63. package/docs/quick-start.md +47 -0
  64. package/docs/reference/troubleshooting.md +33 -0
  65. package/docs/security.md +15 -0
  66. package/docs/sourcey.config.ts +79 -0
  67. package/llms.txt +32 -0
  68. package/maestro/smoke.template.yaml +11 -0
  69. package/package.json +196 -0
@@ -0,0 +1,100 @@
1
+ import { type PlaywrightProviderOptions } from '@vitest/browser-playwright';
2
+ import type { TestUserConfig } from 'vitest/node';
3
+ import { type ChromiumGpuMode } from './chromium-launch.js';
4
+ /**
5
+ * A single browser instance entry, as accepted by Vitest Browser Mode's
6
+ * `test.browser.instances` array (one per browser/viewport combination).
7
+ */
8
+ export interface BrowserInstance {
9
+ browser: 'chromium' | 'firefox' | 'webkit';
10
+ [key: string]: unknown;
11
+ }
12
+ export interface BrowserTestConfigOptions {
13
+ /**
14
+ * Playwright browser-context options. A device scale factor of 1 is the
15
+ * default so headed and headless Chromium produce the same deterministic
16
+ * screenshot dimensions; callers can override it explicitly when a test
17
+ * needs high-DPI rendering.
18
+ */
19
+ contextOptions?: PlaywrightProviderOptions['contextOptions'];
20
+ /**
21
+ * Extra Chromium launch args merged after the selected renderer profile.
22
+ * Forwarded to the Playwright provider's `launchOptions.args`.
23
+ */
24
+ gpuArgs?: string[];
25
+ /** Renderer profile. Defaults to `auto`; software rendering is always explicit. */
26
+ gpuMode?: ChromiumGpuMode;
27
+ /**
28
+ * `true` — always headless. `false` (default) — always headed.
29
+ * `'ci-only'` — headed locally, headless when `process.env.CI` is set.
30
+ * CI should normally retain the headed default and supply Xvfb.
31
+ */
32
+ headless?: boolean | 'ci-only';
33
+ /**
34
+ * Show Vitest's interactive browser UI. Defaults to false so headed runs use
35
+ * a fixed Playwright viewport and deterministic device scale. The Chromium
36
+ * window remains visible whenever `headless` is false.
37
+ */
38
+ ui?: boolean;
39
+ /** Browser instances for the `browser.instances` array. Defaults to a single chromium instance. */
40
+ instances?: BrowserInstance[];
41
+ /**
42
+ * Extra module specifiers to add to `optimizeDeps.include`. Useful for
43
+ * deep-import paths (e.g. `three/examples/jsm/utils/SkeletonUtils.js`)
44
+ * that Vite's dependency scanner doesn't discover on its own and that
45
+ * would otherwise trigger a mid-run re-bundle (and a duplicate module
46
+ * instance) the first time a browser test imports them. Read this field
47
+ * off the return value (`__optimizeDepsInclude`) and merge it into your
48
+ * `vite.config.ts`'s own `optimizeDeps.include` — Vitest's `test` fragment
49
+ * has no `optimizeDeps` field of its own, that lives at the top-level
50
+ * Vite config.
51
+ */
52
+ optimizeDeps?: string[];
53
+ /** Passed through verbatim to `test.setupFiles`. */
54
+ setupFiles?: string[];
55
+ /** Project name. Defaults to `'browser'`. */
56
+ name?: string;
57
+ /** Test file glob(s). Defaults to `['tests/browser/**\/*.browser.test.{ts,tsx}']`. */
58
+ include?: string[];
59
+ /**
60
+ * Disable file-level parallelism. A shared Playwright Chromium pool
61
+ * flakes under parallel browser-test load (independent specs racing for
62
+ * the same browser context can time out). Defaults to `true` (serialized)
63
+ * — flip off only once you've
64
+ * verified your suite tolerates concurrent browser contexts.
65
+ */
66
+ fileParallelism?: boolean;
67
+ }
68
+ /**
69
+ * Builds a `test` fragment for a Vitest Browser Mode project, wired for
70
+ * real-Chromium (or other Playwright-driven browser) test execution.
71
+ *
72
+ * Encodes a reviewed pattern: headed by default both locally and in
73
+ * CI, silent at the Chromium boundary, and native renderer selection unless a
74
+ * consumer explicitly requests software or the proven Linux Vulkan profile.
75
+ *
76
+ * The returned object is meant to be spread into a Vitest `projects[]`
77
+ * entry's `test` field (or merged into a top-level `test` block for
78
+ * single-project setups):
79
+ *
80
+ * ```ts
81
+ * import { defineBrowserTestConfig } from 'game-harness/vitest';
82
+ *
83
+ * export default defineConfig({
84
+ * test: {
85
+ * projects: [
86
+ * { extends: true, test: { name: 'unit', environment: 'node', include: [...] } },
87
+ * { extends: true, test: defineBrowserTestConfig({
88
+ * optimizeDeps: ['three/examples/jsm/utils/SkeletonUtils.js'],
89
+ * }) },
90
+ * ],
91
+ * },
92
+ * });
93
+ * ```
94
+ *
95
+ * NOTE: a headed browser needs a display server. On Linux CI images without
96
+ * one, wrap the test command in `xvfb-run` rather than forcing headless here.
97
+ */
98
+ export declare function defineBrowserTestConfig(opts?: BrowserTestConfigOptions): TestUserConfig & {
99
+ __optimizeDepsInclude?: string[];
100
+ };
@@ -0,0 +1,100 @@
1
+ import { type PlaywrightProviderOptions } from '@vitest/browser-playwright';
2
+ import type { TestUserConfig } from 'vitest/node';
3
+ import { type ChromiumGpuMode } from './chromium-launch.js';
4
+ /**
5
+ * A single browser instance entry, as accepted by Vitest Browser Mode's
6
+ * `test.browser.instances` array (one per browser/viewport combination).
7
+ */
8
+ export interface BrowserInstance {
9
+ browser: 'chromium' | 'firefox' | 'webkit';
10
+ [key: string]: unknown;
11
+ }
12
+ export interface BrowserTestConfigOptions {
13
+ /**
14
+ * Playwright browser-context options. A device scale factor of 1 is the
15
+ * default so headed and headless Chromium produce the same deterministic
16
+ * screenshot dimensions; callers can override it explicitly when a test
17
+ * needs high-DPI rendering.
18
+ */
19
+ contextOptions?: PlaywrightProviderOptions['contextOptions'];
20
+ /**
21
+ * Extra Chromium launch args merged after the selected renderer profile.
22
+ * Forwarded to the Playwright provider's `launchOptions.args`.
23
+ */
24
+ gpuArgs?: string[];
25
+ /** Renderer profile. Defaults to `auto`; software rendering is always explicit. */
26
+ gpuMode?: ChromiumGpuMode;
27
+ /**
28
+ * `true` — always headless. `false` (default) — always headed.
29
+ * `'ci-only'` — headed locally, headless when `process.env.CI` is set.
30
+ * CI should normally retain the headed default and supply Xvfb.
31
+ */
32
+ headless?: boolean | 'ci-only';
33
+ /**
34
+ * Show Vitest's interactive browser UI. Defaults to false so headed runs use
35
+ * a fixed Playwright viewport and deterministic device scale. The Chromium
36
+ * window remains visible whenever `headless` is false.
37
+ */
38
+ ui?: boolean;
39
+ /** Browser instances for the `browser.instances` array. Defaults to a single chromium instance. */
40
+ instances?: BrowserInstance[];
41
+ /**
42
+ * Extra module specifiers to add to `optimizeDeps.include`. Useful for
43
+ * deep-import paths (e.g. `three/examples/jsm/utils/SkeletonUtils.js`)
44
+ * that Vite's dependency scanner doesn't discover on its own and that
45
+ * would otherwise trigger a mid-run re-bundle (and a duplicate module
46
+ * instance) the first time a browser test imports them. Read this field
47
+ * off the return value (`__optimizeDepsInclude`) and merge it into your
48
+ * `vite.config.ts`'s own `optimizeDeps.include` — Vitest's `test` fragment
49
+ * has no `optimizeDeps` field of its own, that lives at the top-level
50
+ * Vite config.
51
+ */
52
+ optimizeDeps?: string[];
53
+ /** Passed through verbatim to `test.setupFiles`. */
54
+ setupFiles?: string[];
55
+ /** Project name. Defaults to `'browser'`. */
56
+ name?: string;
57
+ /** Test file glob(s). Defaults to `['tests/browser/**\/*.browser.test.{ts,tsx}']`. */
58
+ include?: string[];
59
+ /**
60
+ * Disable file-level parallelism. A shared Playwright Chromium pool
61
+ * flakes under parallel browser-test load (independent specs racing for
62
+ * the same browser context can time out). Defaults to `true` (serialized)
63
+ * — flip off only once you've
64
+ * verified your suite tolerates concurrent browser contexts.
65
+ */
66
+ fileParallelism?: boolean;
67
+ }
68
+ /**
69
+ * Builds a `test` fragment for a Vitest Browser Mode project, wired for
70
+ * real-Chromium (or other Playwright-driven browser) test execution.
71
+ *
72
+ * Encodes a reviewed pattern: headed by default both locally and in
73
+ * CI, silent at the Chromium boundary, and native renderer selection unless a
74
+ * consumer explicitly requests software or the proven Linux Vulkan profile.
75
+ *
76
+ * The returned object is meant to be spread into a Vitest `projects[]`
77
+ * entry's `test` field (or merged into a top-level `test` block for
78
+ * single-project setups):
79
+ *
80
+ * ```ts
81
+ * import { defineBrowserTestConfig } from 'game-harness/vitest';
82
+ *
83
+ * export default defineConfig({
84
+ * test: {
85
+ * projects: [
86
+ * { extends: true, test: { name: 'unit', environment: 'node', include: [...] } },
87
+ * { extends: true, test: defineBrowserTestConfig({
88
+ * optimizeDeps: ['three/examples/jsm/utils/SkeletonUtils.js'],
89
+ * }) },
90
+ * ],
91
+ * },
92
+ * });
93
+ * ```
94
+ *
95
+ * NOTE: a headed browser needs a display server. On Linux CI images without
96
+ * one, wrap the test command in `xvfb-run` rather than forcing headless here.
97
+ */
98
+ export declare function defineBrowserTestConfig(opts?: BrowserTestConfigOptions): TestUserConfig & {
99
+ __optimizeDepsInclude?: string[];
100
+ };
@@ -0,0 +1,27 @@
1
+ /** Browser renderer policy for agent-controlled Chromium sessions. */
2
+ export type ChromiumGpuMode = 'auto' | 'software' | 'linux-hardware-vulkan';
3
+ export type ChromiumEnvironment = Record<string, string | undefined>;
4
+ export interface ChromiumLaunchProfileOptions {
5
+ /**
6
+ * `auto` leaves renderer selection to Chromium (Metal on macOS, the native
7
+ * desktop stack elsewhere). `software` opts into SwiftShader explicitly.
8
+ * `linux-hardware-vulkan` is a proven Mesa/ANGLE profile for a
9
+ * Linux runner with `/dev/dri/renderD128` passed through.
10
+ */
11
+ gpuMode?: ChromiumGpuMode;
12
+ /** Additional Chromium arguments, de-duplicated ahead of `--mute-audio`. */
13
+ args?: readonly string[];
14
+ /** Environment additions. The Linux hardware profile also sets `EGL_PLATFORM=surfaceless`. */
15
+ env?: Readonly<ChromiumEnvironment>;
16
+ }
17
+ export interface ChromiumLaunchProfile {
18
+ /** De-duplicated Chromium launch arguments for the selected `gpuMode`, always ending in `--mute-audio`. */
19
+ args: string[];
20
+ /** Present when `env` overrides were supplied or `gpuMode` is `linux-hardware-vulkan` (which also sets `EGL_PLATFORM`); merged on top of `process.env`. */
21
+ env?: ChromiumEnvironment;
22
+ }
23
+ /**
24
+ * Builds the shared Chromium renderer and silence profile without deciding
25
+ * whether the browser is headed. Callers own that explicit choice.
26
+ */
27
+ export declare function createChromiumLaunchProfile(options?: ChromiumLaunchProfileOptions): ChromiumLaunchProfile;
@@ -0,0 +1,27 @@
1
+ /** Browser renderer policy for agent-controlled Chromium sessions. */
2
+ export type ChromiumGpuMode = 'auto' | 'software' | 'linux-hardware-vulkan';
3
+ export type ChromiumEnvironment = Record<string, string | undefined>;
4
+ export interface ChromiumLaunchProfileOptions {
5
+ /**
6
+ * `auto` leaves renderer selection to Chromium (Metal on macOS, the native
7
+ * desktop stack elsewhere). `software` opts into SwiftShader explicitly.
8
+ * `linux-hardware-vulkan` is a proven Mesa/ANGLE profile for a
9
+ * Linux runner with `/dev/dri/renderD128` passed through.
10
+ */
11
+ gpuMode?: ChromiumGpuMode;
12
+ /** Additional Chromium arguments, de-duplicated ahead of `--mute-audio`. */
13
+ args?: readonly string[];
14
+ /** Environment additions. The Linux hardware profile also sets `EGL_PLATFORM=surfaceless`. */
15
+ env?: Readonly<ChromiumEnvironment>;
16
+ }
17
+ export interface ChromiumLaunchProfile {
18
+ /** De-duplicated Chromium launch arguments for the selected `gpuMode`, always ending in `--mute-audio`. */
19
+ args: string[];
20
+ /** Present when `env` overrides were supplied or `gpuMode` is `linux-hardware-vulkan` (which also sets `EGL_PLATFORM`); merged on top of `process.env`. */
21
+ env?: ChromiumEnvironment;
22
+ }
23
+ /**
24
+ * Builds the shared Chromium renderer and silence profile without deciding
25
+ * whether the browser is headed. Callers own that explicit choice.
26
+ */
27
+ export declare function createChromiumLaunchProfile(options?: ChromiumLaunchProfileOptions): ChromiumLaunchProfile;
@@ -0,0 +1,3 @@
1
+ export { type LighthouseAssertionsOverrides, type LighthouseCiConfig, lighthouseAssertions, } from './lighthouse.js';
2
+ export { type ReleaseLadderResult, type ReleaseLadderStep, type VerifyReleaseLadderOptions, verifyReleaseLadder, } from './release-ladder.js';
3
+ export { runVisualBattery, VisualBatteryError, type VisualBatteryOptions, } from './visual-battery.js';
@@ -0,0 +1,3 @@
1
+ export { type LighthouseAssertionsOverrides, type LighthouseCiConfig, lighthouseAssertions, } from './lighthouse.js';
2
+ export { type ReleaseLadderResult, type ReleaseLadderStep, type VerifyReleaseLadderOptions, verifyReleaseLadder, } from './release-ladder.js';
3
+ export { runVisualBattery, VisualBatteryError, type VisualBatteryOptions, } from './visual-battery.js';
@@ -0,0 +1,44 @@
1
+ export interface LighthouseCiConfig {
2
+ ci: {
3
+ collect: {
4
+ staticDistDir: string;
5
+ url: string[];
6
+ numberOfRuns: number;
7
+ settings: {
8
+ preset: string;
9
+ chromeFlags: string;
10
+ };
11
+ };
12
+ assert: {
13
+ preset: string;
14
+ assertions: Record<string, unknown>;
15
+ };
16
+ upload: {
17
+ target: string;
18
+ };
19
+ };
20
+ }
21
+ export interface LighthouseAssertionsOverrides {
22
+ staticDistDir?: string;
23
+ url?: string[];
24
+ numberOfRuns?: number;
25
+ assertions?: Record<string, unknown>;
26
+ }
27
+ declare const PRESETS: Record<string, LighthouseCiConfig>;
28
+ /**
29
+ * Returns a Lighthouse CI (`lighthouserc.json`) config object for a named
30
+ * preset, with an escape hatch for per-repo overrides merged on top.
31
+ *
32
+ * ```ts
33
+ * // lighthouserc.mjs
34
+ * import { lighthouseAssertions } from 'game-harness';
35
+ * export default lighthouseAssertions('game-default');
36
+ * ```
37
+ *
38
+ * Or, to keep `lighthouserc.json` as static JSON, run this once and paste
39
+ * the output — the factory has no runtime dependency on the consumer's
40
+ * environment beyond the overrides you pass.
41
+ */
42
+ export type LighthousePreset = keyof typeof PRESETS | (string & {});
43
+ export declare function lighthouseAssertions(preset?: LighthousePreset, overrides?: LighthouseAssertionsOverrides): LighthouseCiConfig;
44
+ export {};
@@ -0,0 +1,44 @@
1
+ export interface LighthouseCiConfig {
2
+ ci: {
3
+ collect: {
4
+ staticDistDir: string;
5
+ url: string[];
6
+ numberOfRuns: number;
7
+ settings: {
8
+ preset: string;
9
+ chromeFlags: string;
10
+ };
11
+ };
12
+ assert: {
13
+ preset: string;
14
+ assertions: Record<string, unknown>;
15
+ };
16
+ upload: {
17
+ target: string;
18
+ };
19
+ };
20
+ }
21
+ export interface LighthouseAssertionsOverrides {
22
+ staticDistDir?: string;
23
+ url?: string[];
24
+ numberOfRuns?: number;
25
+ assertions?: Record<string, unknown>;
26
+ }
27
+ declare const PRESETS: Record<string, LighthouseCiConfig>;
28
+ /**
29
+ * Returns a Lighthouse CI (`lighthouserc.json`) config object for a named
30
+ * preset, with an escape hatch for per-repo overrides merged on top.
31
+ *
32
+ * ```ts
33
+ * // lighthouserc.mjs
34
+ * import { lighthouseAssertions } from 'game-harness';
35
+ * export default lighthouseAssertions('game-default');
36
+ * ```
37
+ *
38
+ * Or, to keep `lighthouserc.json` as static JSON, run this once and paste
39
+ * the output — the factory has no runtime dependency on the consumer's
40
+ * environment beyond the overrides you pass.
41
+ */
42
+ export type LighthousePreset = keyof typeof PRESETS | (string & {});
43
+ export declare function lighthouseAssertions(preset?: LighthousePreset, overrides?: LighthouseAssertionsOverrides): LighthouseCiConfig;
44
+ export {};
@@ -0,0 +1,122 @@
1
+ import { type Page, type PlaywrightTestConfig, type Response } from '@playwright/test';
2
+ import { type ChromiumGpuMode } from './chromium-launch.js';
3
+ export { SILENT_QA_MARKER_ATTRIBUTE, SILENT_QA_MARKER_VALUE, SILENT_QA_QUERY_PARAMETER, SILENT_QA_QUERY_VALUE, } from './silent-qa.js';
4
+ export type DeviceTier = 'desktop' | 'mobile' | 'tablet' | 'foldable' | 'ultrawide';
5
+ type Project = NonNullable<PlaywrightTestConfig['projects']>[number];
6
+ export type SilentQueryValue = string | number | boolean;
7
+ export interface SilentTestUrlOptions {
8
+ /** Runtime-only mute query parameter. Defaults to `muted`. */
9
+ muteQueryParameter?: string;
10
+ /** Runtime-only mute query value. Defaults to `1`. */
11
+ muteQueryValue?: string;
12
+ }
13
+ export interface OpenSilentGameOptions extends SilentTestUrlOptions {
14
+ /** DOM selector that owns the mute-ready marker. Defaults to `html`. */
15
+ markerSelector?: string;
16
+ /** Marker attribute set only after runtime audio is muted. */
17
+ markerAttribute?: string;
18
+ /** Required marker value. Defaults to `muted-test`. */
19
+ markerValue?: string;
20
+ /** Maximum time to wait for the application mute marker. Defaults to 5 seconds. */
21
+ markerTimeout?: number;
22
+ /** Options forwarded to `page.goto()`. */
23
+ navigationOptions?: NonNullable<Parameters<Page['goto']>[1]>;
24
+ }
25
+ export interface ResolvePlaywrightPortOptions {
26
+ /** Stable local-development port. Defaults to 4173. */
27
+ localPort?: number;
28
+ /** Injectable environment for deterministic consumers and tests. Defaults to `process.env`. */
29
+ environment?: Readonly<Record<string, string | undefined>>;
30
+ }
31
+ /**
32
+ * Adds a non-persistent mute mode to a relative or absolute URL.
33
+ * Explicit caller parameters replace existing values, and the mute value is
34
+ * applied last so a stale `muted=0` can never make an agent run audible.
35
+ */
36
+ export declare function silentTestUrl(target?: string, parameters?: Readonly<Record<string, SilentQueryValue>>, options?: SilentTestUrlOptions): string;
37
+ /**
38
+ * Navigates to a game in runtime-only mute mode and fails closed until the
39
+ * application confirms that audio was muted before test interaction begins.
40
+ */
41
+ export declare function openSilentGame(page: Page, target?: string, parameters?: Readonly<Record<string, SilentQueryValue>>, options?: OpenSilentGameOptions): Promise<Response | null>;
42
+ export interface PlaywrightConfigOptions {
43
+ /** Playwright `testDir`. Defaults to `'./tests'`. */
44
+ testDir?: string;
45
+ /** Base path the dev/preview server serves from. Defaults to `'/'`. */
46
+ basePath?: string;
47
+ /** Port for the local webServer + baseURL. Defaults to 4173, overridable via `PLAYWRIGHT_PORT`/`PW_PORT`. */
48
+ port?: number;
49
+ /**
50
+ * Builds a custom web-server command with the already-resolved local or
51
+ * CI-isolated port. Prefer this over hard-coding a port in
52
+ * `overrides.webServer.command`.
53
+ */
54
+ webServerCommand?: (port: number) => string;
55
+ /** Renderer profile. Defaults to native Chromium selection (`auto`). */
56
+ gpuMode?: ChromiumGpuMode;
57
+ /**
58
+ * `false` (default) keeps Chromium headed locally and in CI. Use Xvfb on
59
+ * Linux CI. `true` is explicit headless mode; `ci-only` retains the older
60
+ * hosted-runner behavior when a display is genuinely unavailable.
61
+ */
62
+ headless?: boolean | 'ci-only';
63
+ /**
64
+ * Which device-tier Playwright projects to include. `desktop` is always
65
+ * present as the tier-1 CI gate; passing more tiers here is equivalent to
66
+ * the `MULTIVIEW=1` convention already wired below — you don't
67
+ * need to also request `desktop` explicitly.
68
+ * Defaults to `['desktop']` (single project, matching the fast CI gate),
69
+ * expanding to all requested tiers when `MULTIVIEW=1` or `VISUAL=1` is set.
70
+ */
71
+ deviceTiers?: DeviceTier[];
72
+ /** Extra Playwright projects appended after the device-tier projects. */
73
+ extraProjects?: Project[];
74
+ /**
75
+ * Spec globs that only run when `JOURNEY=1` is set (or when `VISUAL=1` is
76
+ * set, since visual runs imply the full journey suite). These are
77
+ * "agent review harness" specs — expensive artefact-producing runs, not
78
+ * part of the fast tier-1 functional gate. Excluded from `testIgnore`
79
+ * otherwise.
80
+ */
81
+ journeySpecs?: string[];
82
+ /**
83
+ * Multiplier applied to the local (non-CI) timeout defaults to derive the
84
+ * CI timeout. CI runners are consistently slower under WebGL/render load;
85
+ * defaults to 4 (45s local → 180s CI action-adjacent test timeout, scaled
86
+ * per-field below).
87
+ */
88
+ ciTimeoutMultiplier?: number;
89
+ /**
90
+ * Overrides merged last, escape-hatch for anything this factory doesn't
91
+ * expose. `use` and `webServer` are merged one level deep on top of the
92
+ * computed defaults (see `definePlaywrightConfig`'s return); every other
93
+ * field fully replaces.
94
+ */
95
+ overrides?: Omit<Partial<PlaywrightTestConfig>, 'use' | 'webServer'> & {
96
+ use?: Partial<NonNullable<PlaywrightTestConfig['use']>>;
97
+ webServer?: Partial<Extract<NonNullable<PlaywrightTestConfig['webServer']>, {
98
+ command?: string;
99
+ }>>;
100
+ };
101
+ }
102
+ /**
103
+ * Resolves one stable Playwright preview port for every config reload in an
104
+ * Actions job. Explicit `PLAYWRIGHT_PORT`/`PW_PORT` values win; local runs use
105
+ * `localPort`; GitHub/Gitea CI hashes repository, run, job, and local port into
106
+ * an isolated port range.
107
+ */
108
+ export declare function resolvePlaywrightPort(options?: ResolvePlaywrightPortOptions): number;
109
+ /**
110
+ * Builds a full Playwright config, encoding a tiered-device +
111
+ * env-gated-suite convention:
112
+ *
113
+ * - `desktop` project always runs; `MULTIVIEW=1` (or `VISUAL=1`) expands to
114
+ * every requested device tier (mobile/tablet/foldable/ultrawide).
115
+ * - `JOURNEY=1` (or `VISUAL=1`) opts into expensive artefact-producing specs
116
+ * that are excluded from the default tier-1 functional gate so CI stays
117
+ * fast.
118
+ * - `VISUAL=1` additionally adds `visualSpecs` to the test match glob.
119
+ * - CI timeouts scale up from local defaults via `ciTimeoutMultiplier`
120
+ * (CI runners run WebGL/render-heavy tests 2-4x slower than local dev).
121
+ */
122
+ export declare function definePlaywrightConfig(opts?: PlaywrightConfigOptions): PlaywrightTestConfig;
@@ -0,0 +1,122 @@
1
+ import { type Page, type PlaywrightTestConfig, type Response } from '@playwright/test';
2
+ import { type ChromiumGpuMode } from './chromium-launch.js';
3
+ export { SILENT_QA_MARKER_ATTRIBUTE, SILENT_QA_MARKER_VALUE, SILENT_QA_QUERY_PARAMETER, SILENT_QA_QUERY_VALUE, } from './silent-qa.js';
4
+ export type DeviceTier = 'desktop' | 'mobile' | 'tablet' | 'foldable' | 'ultrawide';
5
+ type Project = NonNullable<PlaywrightTestConfig['projects']>[number];
6
+ export type SilentQueryValue = string | number | boolean;
7
+ export interface SilentTestUrlOptions {
8
+ /** Runtime-only mute query parameter. Defaults to `muted`. */
9
+ muteQueryParameter?: string;
10
+ /** Runtime-only mute query value. Defaults to `1`. */
11
+ muteQueryValue?: string;
12
+ }
13
+ export interface OpenSilentGameOptions extends SilentTestUrlOptions {
14
+ /** DOM selector that owns the mute-ready marker. Defaults to `html`. */
15
+ markerSelector?: string;
16
+ /** Marker attribute set only after runtime audio is muted. */
17
+ markerAttribute?: string;
18
+ /** Required marker value. Defaults to `muted-test`. */
19
+ markerValue?: string;
20
+ /** Maximum time to wait for the application mute marker. Defaults to 5 seconds. */
21
+ markerTimeout?: number;
22
+ /** Options forwarded to `page.goto()`. */
23
+ navigationOptions?: NonNullable<Parameters<Page['goto']>[1]>;
24
+ }
25
+ export interface ResolvePlaywrightPortOptions {
26
+ /** Stable local-development port. Defaults to 4173. */
27
+ localPort?: number;
28
+ /** Injectable environment for deterministic consumers and tests. Defaults to `process.env`. */
29
+ environment?: Readonly<Record<string, string | undefined>>;
30
+ }
31
+ /**
32
+ * Adds a non-persistent mute mode to a relative or absolute URL.
33
+ * Explicit caller parameters replace existing values, and the mute value is
34
+ * applied last so a stale `muted=0` can never make an agent run audible.
35
+ */
36
+ export declare function silentTestUrl(target?: string, parameters?: Readonly<Record<string, SilentQueryValue>>, options?: SilentTestUrlOptions): string;
37
+ /**
38
+ * Navigates to a game in runtime-only mute mode and fails closed until the
39
+ * application confirms that audio was muted before test interaction begins.
40
+ */
41
+ export declare function openSilentGame(page: Page, target?: string, parameters?: Readonly<Record<string, SilentQueryValue>>, options?: OpenSilentGameOptions): Promise<Response | null>;
42
+ export interface PlaywrightConfigOptions {
43
+ /** Playwright `testDir`. Defaults to `'./tests'`. */
44
+ testDir?: string;
45
+ /** Base path the dev/preview server serves from. Defaults to `'/'`. */
46
+ basePath?: string;
47
+ /** Port for the local webServer + baseURL. Defaults to 4173, overridable via `PLAYWRIGHT_PORT`/`PW_PORT`. */
48
+ port?: number;
49
+ /**
50
+ * Builds a custom web-server command with the already-resolved local or
51
+ * CI-isolated port. Prefer this over hard-coding a port in
52
+ * `overrides.webServer.command`.
53
+ */
54
+ webServerCommand?: (port: number) => string;
55
+ /** Renderer profile. Defaults to native Chromium selection (`auto`). */
56
+ gpuMode?: ChromiumGpuMode;
57
+ /**
58
+ * `false` (default) keeps Chromium headed locally and in CI. Use Xvfb on
59
+ * Linux CI. `true` is explicit headless mode; `ci-only` retains the older
60
+ * hosted-runner behavior when a display is genuinely unavailable.
61
+ */
62
+ headless?: boolean | 'ci-only';
63
+ /**
64
+ * Which device-tier Playwright projects to include. `desktop` is always
65
+ * present as the tier-1 CI gate; passing more tiers here is equivalent to
66
+ * the `MULTIVIEW=1` convention already wired below — you don't
67
+ * need to also request `desktop` explicitly.
68
+ * Defaults to `['desktop']` (single project, matching the fast CI gate),
69
+ * expanding to all requested tiers when `MULTIVIEW=1` or `VISUAL=1` is set.
70
+ */
71
+ deviceTiers?: DeviceTier[];
72
+ /** Extra Playwright projects appended after the device-tier projects. */
73
+ extraProjects?: Project[];
74
+ /**
75
+ * Spec globs that only run when `JOURNEY=1` is set (or when `VISUAL=1` is
76
+ * set, since visual runs imply the full journey suite). These are
77
+ * "agent review harness" specs — expensive artefact-producing runs, not
78
+ * part of the fast tier-1 functional gate. Excluded from `testIgnore`
79
+ * otherwise.
80
+ */
81
+ journeySpecs?: string[];
82
+ /**
83
+ * Multiplier applied to the local (non-CI) timeout defaults to derive the
84
+ * CI timeout. CI runners are consistently slower under WebGL/render load;
85
+ * defaults to 4 (45s local → 180s CI action-adjacent test timeout, scaled
86
+ * per-field below).
87
+ */
88
+ ciTimeoutMultiplier?: number;
89
+ /**
90
+ * Overrides merged last, escape-hatch for anything this factory doesn't
91
+ * expose. `use` and `webServer` are merged one level deep on top of the
92
+ * computed defaults (see `definePlaywrightConfig`'s return); every other
93
+ * field fully replaces.
94
+ */
95
+ overrides?: Omit<Partial<PlaywrightTestConfig>, 'use' | 'webServer'> & {
96
+ use?: Partial<NonNullable<PlaywrightTestConfig['use']>>;
97
+ webServer?: Partial<Extract<NonNullable<PlaywrightTestConfig['webServer']>, {
98
+ command?: string;
99
+ }>>;
100
+ };
101
+ }
102
+ /**
103
+ * Resolves one stable Playwright preview port for every config reload in an
104
+ * Actions job. Explicit `PLAYWRIGHT_PORT`/`PW_PORT` values win; local runs use
105
+ * `localPort`; GitHub/Gitea CI hashes repository, run, job, and local port into
106
+ * an isolated port range.
107
+ */
108
+ export declare function resolvePlaywrightPort(options?: ResolvePlaywrightPortOptions): number;
109
+ /**
110
+ * Builds a full Playwright config, encoding a tiered-device +
111
+ * env-gated-suite convention:
112
+ *
113
+ * - `desktop` project always runs; `MULTIVIEW=1` (or `VISUAL=1`) expands to
114
+ * every requested device tier (mobile/tablet/foldable/ultrawide).
115
+ * - `JOURNEY=1` (or `VISUAL=1`) opts into expensive artefact-producing specs
116
+ * that are excluded from the default tier-1 functional gate so CI stays
117
+ * fast.
118
+ * - `VISUAL=1` additionally adds `visualSpecs` to the test match glob.
119
+ * - CI timeouts scale up from local defaults via `ciTimeoutMultiplier`
120
+ * (CI runners run WebGL/render-heavy tests 2-4x slower than local dev).
121
+ */
122
+ export declare function definePlaywrightConfig(opts?: PlaywrightConfigOptions): PlaywrightTestConfig;