@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.
Files changed (45) hide show
  1. package/dist/cli.js +139 -52
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts +26 -2
  4. package/dist/config.js +28 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/cookies.d.ts +14 -0
  7. package/dist/cookies.js +121 -0
  8. package/dist/cookies.js.map +1 -0
  9. package/dist/engine.d.ts +22 -9
  10. package/dist/engine.js +68 -44
  11. package/dist/engine.js.map +1 -1
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +2 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/init.js +3 -0
  16. package/dist/init.js.map +1 -1
  17. package/dist/mcp/server.js +51 -29
  18. package/dist/mcp/server.js.map +1 -1
  19. package/dist/report.d.ts +10 -4
  20. package/dist/report.js +33 -26
  21. package/dist/report.js.map +1 -1
  22. package/dist/runner/ai-runner.d.ts +3 -2
  23. package/dist/runner/ai-runner.js +12 -1
  24. package/dist/runner/ai-runner.js.map +1 -1
  25. package/dist/runner/browser.d.ts +58 -4
  26. package/dist/runner/browser.js +146 -3
  27. package/dist/runner/browser.js.map +1 -1
  28. package/dist/runner/script-runner.d.ts +9 -5
  29. package/dist/runner/script-runner.js +29 -7
  30. package/dist/runner/script-runner.js.map +1 -1
  31. package/dist/runner/video.d.ts +38 -8
  32. package/dist/runner/video.js +51 -13
  33. package/dist/runner/video.js.map +1 -1
  34. package/dist/specs.d.ts +14 -0
  35. package/dist/specs.js +59 -1
  36. package/dist/specs.js.map +1 -1
  37. package/dist/store.d.ts +14 -9
  38. package/dist/store.js +25 -16
  39. package/dist/store.js.map +1 -1
  40. package/dist/types.d.ts +54 -1
  41. package/dist/viewports.d.ts +54 -0
  42. package/dist/viewports.js +83 -0
  43. package/dist/viewports.js.map +1 -0
  44. package/package.json +1 -1
  45. 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":"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"}
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 preview clip (MP4, or WebM fallback) — only when --record-video and verified */
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pcamarajr/scout",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Self-healing browser QA: natural-language scenarios verified by an AI agent, replayed deterministically in CI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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**: