@pcamarajr/scout 0.9.0 → 0.11.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.
- package/dist/cli.js +139 -52
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +26 -2
- package/dist/config.js +28 -1
- package/dist/config.js.map +1 -1
- package/dist/cookies.d.ts +14 -0
- package/dist/cookies.js +121 -0
- package/dist/cookies.js.map +1 -0
- package/dist/engine.d.ts +22 -9
- package/dist/engine.js +68 -44
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/init.js +3 -0
- package/dist/init.js.map +1 -1
- package/dist/mcp/server.js +51 -29
- package/dist/mcp/server.js.map +1 -1
- package/dist/report.d.ts +10 -4
- package/dist/report.js +33 -26
- package/dist/report.js.map +1 -1
- package/dist/runner/ai-runner.d.ts +3 -2
- package/dist/runner/ai-runner.js +12 -1
- package/dist/runner/ai-runner.js.map +1 -1
- package/dist/runner/browser.d.ts +58 -4
- package/dist/runner/browser.js +146 -3
- package/dist/runner/browser.js.map +1 -1
- package/dist/runner/script-runner.d.ts +9 -5
- package/dist/runner/script-runner.js +29 -7
- package/dist/runner/script-runner.js.map +1 -1
- package/dist/runner/video.d.ts +38 -8
- package/dist/runner/video.js +51 -13
- package/dist/runner/video.js.map +1 -1
- package/dist/specs.d.ts +14 -0
- package/dist/specs.js +59 -1
- package/dist/specs.js.map +1 -1
- package/dist/store.d.ts +14 -9
- package/dist/store.js +25 -16
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +54 -1
- package/dist/viewports.d.ts +54 -0
- package/dist/viewports.js +83 -0
- package/dist/viewports.js.map +1 -0
- package/package.json +1 -1
- package/templates/AGENTS.md +40 -3
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGnE;;;;;;GAMG;AACH,MAAM,OAAO,KAAK;IACP,IAAI,CAAS;IACb,GAAG,CAAS;IAErB,YAAY,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;QAC7B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAED,IAAI;QACF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACrD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;QACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,aAAa;QACX,OAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC3D,CAAC;IAED,yCAAyC;IAEzC,UAAU,CAAC,IAAY;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,44 @@ export interface GeoCoords {
|
|
|
33
33
|
latitude: number;
|
|
34
34
|
longitude: number;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A named viewport descriptor, declared in `scout.config.json` `viewports` (or
|
|
38
|
+
* shipped as a built-in). A `device` Playwright preset is the base; explicit
|
|
39
|
+
* fields override it (`{ device: "iPhone 13", height: 844 }` = the preset with
|
|
40
|
+
* its height pinned). `width`/`height` are required only when there is no
|
|
41
|
+
* `device` to take them from. Resolved into Playwright context options by
|
|
42
|
+
* `resolveViewport` at launch — never baked into the recorded script.
|
|
43
|
+
*/
|
|
44
|
+
export interface Viewport {
|
|
45
|
+
/** Playwright device preset name (e.g. "iPhone 13"); the base for overrides. */
|
|
46
|
+
device?: string;
|
|
47
|
+
width?: number;
|
|
48
|
+
height?: number;
|
|
49
|
+
deviceScaleFactor?: number;
|
|
50
|
+
isMobile?: boolean;
|
|
51
|
+
hasTouch?: boolean;
|
|
52
|
+
userAgent?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* One cookie applied to the browser context before the scenario runs. Resolved
|
|
56
|
+
* fresh from the spec each run (profile default + per-scenario frontmatter /
|
|
57
|
+
* override, merged by name) and set via Playwright `context.addCookies()` —
|
|
58
|
+
* never a replayable Step, like storageState/permissions. `value` may carry a
|
|
59
|
+
* `$ENV:VAR` placeholder, resolved at launch so the secret never lands in the
|
|
60
|
+
* committed spec or the LLM context. `domain`/`path` default to the host of
|
|
61
|
+
* `baseUrl` and `/` when omitted; `expires` is unix seconds (Playwright
|
|
62
|
+
* convention) and omitting it yields a session cookie.
|
|
63
|
+
*/
|
|
64
|
+
export interface ScenarioCookie {
|
|
65
|
+
name: string;
|
|
66
|
+
value: string;
|
|
67
|
+
domain?: string;
|
|
68
|
+
path?: string;
|
|
69
|
+
expires?: number;
|
|
70
|
+
httpOnly?: boolean;
|
|
71
|
+
secure?: boolean;
|
|
72
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
73
|
+
}
|
|
36
74
|
/**
|
|
37
75
|
* Browser permission policy resolved from a scenario's spec (file frontmatter
|
|
38
76
|
* is the default, per-section keys override it, merged per-axis). Three states
|
|
@@ -121,13 +159,28 @@ export interface Scenario {
|
|
|
121
159
|
notes?: string;
|
|
122
160
|
/** Free-form tags (file-level + per-scenario, merged) */
|
|
123
161
|
tags?: string[];
|
|
162
|
+
/**
|
|
163
|
+
* Viewport names this scenario runs in (file frontmatter default, replaced —
|
|
164
|
+
* not merged — by a per-section `viewports:` override). Each name must resolve
|
|
165
|
+
* against the built-in/config registry. Omitted = the config's default
|
|
166
|
+
* viewport. Declaring N names fans the scenario out into N verification units.
|
|
167
|
+
*/
|
|
168
|
+
viewports?: string[];
|
|
124
169
|
/** Browser permission policy (frontmatter default + per-section override). */
|
|
125
170
|
permissions?: PermissionPolicy;
|
|
171
|
+
/**
|
|
172
|
+
* Cookies declared on the scenario (file frontmatter + per-section override,
|
|
173
|
+
* merged by name). The profile's cookies are merged in at launch, under
|
|
174
|
+
* these. Carries raw `$ENV:VAR` placeholders — resolved only at launch.
|
|
175
|
+
*/
|
|
176
|
+
cookies?: ScenarioCookie[];
|
|
126
177
|
/** Source spec file, relative to the project root */
|
|
127
178
|
file: string;
|
|
128
179
|
}
|
|
129
180
|
export interface RunResult {
|
|
130
181
|
slug: string;
|
|
182
|
+
/** Viewport name this run executed in — part of the run identity (`slug@viewport`). */
|
|
183
|
+
viewport: string;
|
|
131
184
|
/** replay = cached deterministic steps; ai = agent-driven run */
|
|
132
185
|
mode: "replay" | "ai";
|
|
133
186
|
verdict: Verdict;
|
|
@@ -139,7 +192,7 @@ export interface RunResult {
|
|
|
139
192
|
durationMs: number;
|
|
140
193
|
screenshots: string[];
|
|
141
194
|
trace?: string;
|
|
142
|
-
/** Paced
|
|
195
|
+
/** Paced demo clip (MP4, or WebM fallback) — only when --demo-video and verified */
|
|
143
196
|
video?: string;
|
|
144
197
|
/** true when this AI run replaced a broken cached script */
|
|
145
198
|
healed?: boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ScoutConfig } from "./config.js";
|
|
2
|
+
import type { Scenario, Viewport } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* A named viewport resolved into the browser-context parameters Playwright
|
|
5
|
+
* needs. Produced by {@link resolveViewport} from a {@link Viewport} descriptor
|
|
6
|
+
* (built-in or from config), spreading a `device` preset and letting explicit
|
|
7
|
+
* fields win on top. `width`/`height` are always present; the rest are omitted
|
|
8
|
+
* when neither the preset nor the descriptor sets them (Playwright defaults).
|
|
9
|
+
*/
|
|
10
|
+
export interface ResolvedViewport {
|
|
11
|
+
/** The registry name — also the script-file token and report key. */
|
|
12
|
+
name: string;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
deviceScaleFactor?: number;
|
|
16
|
+
isMobile?: boolean;
|
|
17
|
+
hasTouch?: boolean;
|
|
18
|
+
userAgent?: string;
|
|
19
|
+
}
|
|
20
|
+
/** The built-in default when neither the scenario nor the config picks one. */
|
|
21
|
+
export declare const DEFAULT_VIEWPORT = "mobile";
|
|
22
|
+
/**
|
|
23
|
+
* Curated viewports available without any config. A same-named entry in
|
|
24
|
+
* `scout.config.json` `viewports` overrides one of these. `mobile` emulates an
|
|
25
|
+
* iPhone 13 (touch + mobile UA + dsf 3) but pins the canonical 390×844 portrait
|
|
26
|
+
* so the demo-video aspect stays stable across runs.
|
|
27
|
+
*/
|
|
28
|
+
export declare const BUILTIN_VIEWPORTS: Record<string, Viewport>;
|
|
29
|
+
export declare function isValidViewportName(name: string): boolean;
|
|
30
|
+
/** The full viewport registry for a config: built-ins, overridden by config. */
|
|
31
|
+
export declare function viewportRegistry(config: ScoutConfig): Record<string, Viewport>;
|
|
32
|
+
/** The default viewport name — config override, else the built-in. */
|
|
33
|
+
export declare function defaultViewportName(config: ScoutConfig): string;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a named viewport into Playwright context options. Fail-loud on an
|
|
36
|
+
* unknown name, a `device` preset Playwright doesn't ship, or a descriptor that
|
|
37
|
+
* provides neither dimensions nor a preset to take them from.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveViewport(name: string, config: ScoutConfig): ResolvedViewport;
|
|
40
|
+
/**
|
|
41
|
+
* The viewports a scenario runs in: the explicit per-invocation `override`
|
|
42
|
+
* (ad-hoc, single), else the scenario's declared list, else the config default.
|
|
43
|
+
* Names only — existence is validated at resolution time ({@link resolveViewport}).
|
|
44
|
+
*/
|
|
45
|
+
export declare function runnableViewports(scenario: Scenario, config: ScoutConfig, override?: string): string[];
|
|
46
|
+
/** One (scenario × viewport) verification unit. */
|
|
47
|
+
export interface ScenarioViewport {
|
|
48
|
+
scenario: Scenario;
|
|
49
|
+
viewport: string;
|
|
50
|
+
}
|
|
51
|
+
/** Expand scenarios into their (scenario × viewport) units, for list/report. */
|
|
52
|
+
export declare function expandScenarios(scenarios: Scenario[], config: ScoutConfig): ScenarioViewport[];
|
|
53
|
+
/** The store key for a (scenario × viewport) run: `<slug>@<viewport>`. */
|
|
54
|
+
export declare function runKey(slug: string, viewport: string): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { devices } from "playwright";
|
|
2
|
+
/** The built-in default when neither the scenario nor the config picks one. */
|
|
3
|
+
export const DEFAULT_VIEWPORT = "mobile";
|
|
4
|
+
/**
|
|
5
|
+
* Curated viewports available without any config. A same-named entry in
|
|
6
|
+
* `scout.config.json` `viewports` overrides one of these. `mobile` emulates an
|
|
7
|
+
* iPhone 13 (touch + mobile UA + dsf 3) but pins the canonical 390×844 portrait
|
|
8
|
+
* so the demo-video aspect stays stable across runs.
|
|
9
|
+
*/
|
|
10
|
+
export const BUILTIN_VIEWPORTS = {
|
|
11
|
+
mobile: { device: "iPhone 13", width: 390, height: 844 },
|
|
12
|
+
desktop: { width: 1280, height: 800 },
|
|
13
|
+
tablet: { device: "iPad Mini" },
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Viewport names double as filesystem tokens (`<slug>@<name>.json`) and report
|
|
17
|
+
* keys, so they are constrained to a safe, stable charset — a typo or a name
|
|
18
|
+
* with a path/`@` character fails loud instead of writing a broken script path.
|
|
19
|
+
*/
|
|
20
|
+
const VIEWPORT_NAME_RE = /^[a-z0-9-]+$/;
|
|
21
|
+
export function isValidViewportName(name) {
|
|
22
|
+
return VIEWPORT_NAME_RE.test(name);
|
|
23
|
+
}
|
|
24
|
+
/** The full viewport registry for a config: built-ins, overridden by config. */
|
|
25
|
+
export function viewportRegistry(config) {
|
|
26
|
+
return { ...BUILTIN_VIEWPORTS, ...(config.viewports ?? {}) };
|
|
27
|
+
}
|
|
28
|
+
/** The default viewport name — config override, else the built-in. */
|
|
29
|
+
export function defaultViewportName(config) {
|
|
30
|
+
return config.defaultViewport ?? DEFAULT_VIEWPORT;
|
|
31
|
+
}
|
|
32
|
+
function knownNames(registry) {
|
|
33
|
+
return Object.keys(registry).sort().join(", ");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve a named viewport into Playwright context options. Fail-loud on an
|
|
37
|
+
* unknown name, a `device` preset Playwright doesn't ship, or a descriptor that
|
|
38
|
+
* provides neither dimensions nor a preset to take them from.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveViewport(name, config) {
|
|
41
|
+
const registry = viewportRegistry(config);
|
|
42
|
+
const vp = registry[name];
|
|
43
|
+
if (!vp) {
|
|
44
|
+
throw new Error(`Unknown viewport "${name}". Known viewports: ${knownNames(registry)}.`);
|
|
45
|
+
}
|
|
46
|
+
const base = vp.device ? devices[vp.device] : undefined;
|
|
47
|
+
if (vp.device && !base) {
|
|
48
|
+
throw new Error(`Viewport "${name}" references unknown Playwright device "${vp.device}". See https://playwright.dev/docs/emulation#devices for valid names.`);
|
|
49
|
+
}
|
|
50
|
+
const width = vp.width ?? base?.viewport.width;
|
|
51
|
+
const height = vp.height ?? base?.viewport.height;
|
|
52
|
+
if (width == null || height == null) {
|
|
53
|
+
throw new Error(`Viewport "${name}" needs explicit width and height (or a "device" preset that provides them).`);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
width,
|
|
58
|
+
height,
|
|
59
|
+
deviceScaleFactor: vp.deviceScaleFactor ?? base?.deviceScaleFactor,
|
|
60
|
+
isMobile: vp.isMobile ?? base?.isMobile,
|
|
61
|
+
hasTouch: vp.hasTouch ?? base?.hasTouch,
|
|
62
|
+
userAgent: vp.userAgent ?? base?.userAgent,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The viewports a scenario runs in: the explicit per-invocation `override`
|
|
67
|
+
* (ad-hoc, single), else the scenario's declared list, else the config default.
|
|
68
|
+
* Names only — existence is validated at resolution time ({@link resolveViewport}).
|
|
69
|
+
*/
|
|
70
|
+
export function runnableViewports(scenario, config, override) {
|
|
71
|
+
if (override)
|
|
72
|
+
return [override];
|
|
73
|
+
return scenario.viewports?.length ? scenario.viewports : [defaultViewportName(config)];
|
|
74
|
+
}
|
|
75
|
+
/** Expand scenarios into their (scenario × viewport) units, for list/report. */
|
|
76
|
+
export function expandScenarios(scenarios, config) {
|
|
77
|
+
return scenarios.flatMap((scenario) => runnableViewports(scenario, config).map((viewport) => ({ scenario, viewport })));
|
|
78
|
+
}
|
|
79
|
+
/** The store key for a (scenario × viewport) run: `<slug>@<viewport>`. */
|
|
80
|
+
export function runKey(slug, viewport) {
|
|
81
|
+
return `${slug}@${viewport}`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=viewports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewports.js","sourceRoot":"","sources":["../src/viewports.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -38,25 +38,62 @@ Format:
|
|
|
38
38
|
feature: Paywall # optional; defaults to the filename
|
|
39
39
|
profile: anon # optional; default auth profile for scenarios below
|
|
40
40
|
tags: [monetization] # optional
|
|
41
|
+
viewports: [mobile] # optional; viewports each scenario below runs in
|
|
41
42
|
---
|
|
42
43
|
|
|
43
44
|
## Free user hits paywall on ep 3
|
|
44
45
|
Open ep 3 of series X without login; the paywall appears with a signup CTA.
|
|
45
46
|
|
|
46
47
|
## Subscriber bypasses paywall
|
|
47
|
-
profile: qa # per-scenario override (also: notes, tags)
|
|
48
|
+
profile: qa # per-scenario override (also: notes, tags, viewports)
|
|
49
|
+
viewports: [mobile, desktop]
|
|
48
50
|
|
|
49
51
|
Logged-in subscriber opens ep 3; the episode plays with no paywall.
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
Rules that matter when you author:
|
|
53
55
|
|
|
54
|
-
- **Frontmatter** (YAML, optional): `feature` (defaults to the filename), `profile` (default auth profile), `tags`.
|
|
56
|
+
- **Frontmatter** (YAML, optional): `feature` (defaults to the filename), `profile` (default auth profile), `tags`, `viewports`.
|
|
55
57
|
- **Each `## heading` is one scenario.** Its logical slug is `<file-slug>/<scenario-slug>` (e.g. `paywall/free-user-hits-paywall-on-ep-3`) and must be unique across the suite. Duplicate headings in a file, or a scenario with no body text, are hard errors.
|
|
56
|
-
- **Per-scenario overrides:** immediately under a heading you may place `profile:`, `notes:`, `tags:`, `grantPermissions:`, `denyPermissions:`, and `geolocation:` lines (before the prose) to override the file-level defaults.
|
|
58
|
+
- **Per-scenario overrides:** immediately under a heading you may place `profile:`, `notes:`, `tags:`, `viewports:`, `grantPermissions:`, `denyPermissions:`, and `geolocation:` lines (before the prose) to override the file-level defaults.
|
|
57
59
|
- **Body = flow + expected behavior, in plain language.** Describe what the user does and what must (or must not) be true. No CSS selectors, no Playwright code — the agent discovers the real elements at run time and records them.
|
|
58
60
|
- A `.scout.md` whose every `##` lives inside a fenced ```` ``` ```` block parses as **zero scenarios** (that is how `example.scout.md` documents the format without polluting the suite).
|
|
59
61
|
|
|
62
|
+
## Viewports (screen sizes)
|
|
63
|
+
|
|
64
|
+
Each scenario runs in one or more **named viewports**. Declaring more than one fans the scenario out into independent verification units — each viewport gets its own recorded script (`<slug>@<viewport>.json`), its own verdict, and its own demo video. This is how you cover responsive behavior (the mobile hamburger vs the desktop nav).
|
|
65
|
+
|
|
66
|
+
Built-in viewports, usable without any config:
|
|
67
|
+
|
|
68
|
+
- **`mobile`** — iPhone 13 emulation (touch + mobile UA), pinned to 390×844.
|
|
69
|
+
- **`desktop`** — 1280×800, no touch.
|
|
70
|
+
- **`tablet`** — iPad Mini (768×1024, touch).
|
|
71
|
+
|
|
72
|
+
Declare them in the **frontmatter** (default for every scenario in the file) and override **per scenario** — a per-scenario `viewports:` list **replaces** the file-level one (it does not merge like tags), so a scenario explicitly chooses its sizes. Omitting `viewports` everywhere uses the config's `defaultViewport` (`mobile`).
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
---
|
|
76
|
+
feature: Navigation
|
|
77
|
+
viewports: [mobile, desktop] # file default: every scenario runs in both
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Primary nav is reachable
|
|
81
|
+
Open the home page and reach the "Pricing" link from the main navigation.
|
|
82
|
+
|
|
83
|
+
## Hamburger menu opens on small screens
|
|
84
|
+
viewports: [mobile] # this one only matters on mobile
|
|
85
|
+
|
|
86
|
+
Open the home page; tapping the menu button reveals the navigation drawer.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Add or override viewports in `scout.config.json` (`viewports`), where each entry is a Playwright `device` preset and/or explicit fields (`width`, `height`, `deviceScaleFactor`, `isMobile`, `hasTouch`, `userAgent`) — they compose, so `{ "device": "iPhone 13", "width": 414 }` is the preset with its width overridden. Set the fallback with `defaultViewport`.
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
|
|
93
|
+
- Viewport names are limited to `[a-z0-9-]` (they become script-file tokens). A name not in the registry, or an unknown `device` preset, fails the run with a clear error.
|
|
94
|
+
- `scout go --viewport <name>` forces one viewport ad-hoc for debugging (must exist in the registry); that run never persists a script.
|
|
95
|
+
- The demo video follows the viewport — a `desktop` scenario records a landscape clip.
|
|
96
|
+
|
|
60
97
|
## Browser permissions
|
|
61
98
|
|
|
62
99
|
Some flows hit native browser permission prompts (geolocation, notifications, camera, microphone) that the agent cannot click — they live outside the page DOM. Declare a permission policy so Scout sets it at browser launch, for both the AI run and deterministic replay. Three states **per permission**:
|