@pcamarajr/scout 0.12.0 → 0.14.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/README.md +1 -1
- package/dist/config.d.ts +17 -1
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -1
- package/dist/engine.js +12 -7
- package/dist/engine.js.map +1 -1
- package/dist/runner/agent-tools.js +75 -1
- package/dist/runner/agent-tools.js.map +1 -1
- package/dist/runner/ai-runner.js +21 -1
- package/dist/runner/ai-runner.js.map +1 -1
- package/dist/runner/browser.d.ts +62 -1
- package/dist/runner/browser.js +153 -0
- package/dist/runner/browser.js.map +1 -1
- package/dist/runner/script-runner.js +14 -0
- package/dist/runner/script-runner.js.map +1 -1
- package/dist/specs.js +9 -0
- package/dist/specs.js.map +1 -1
- package/dist/storage.d.ts +23 -0
- package/dist/storage.js +132 -0
- package/dist/storage.js.map +1 -0
- package/dist/store.js +17 -4
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +68 -1
- package/package.json +1 -1
- package/templates/AGENTS.md +79 -2
- package/templates/SKILL.md +2 -1
- package/templates/scout.mdc +4 -3
package/dist/store.js
CHANGED
|
@@ -95,10 +95,13 @@ feature: Example
|
|
|
95
95
|
# How to write a scout spec
|
|
96
96
|
|
|
97
97
|
One file per feature/component. File-level frontmatter sets defaults
|
|
98
|
-
(\`feature\`, \`profile\`, \`tags\`, \`viewports\`). Each \`##\`
|
|
99
|
-
optional \`profile\`/\`notes\`/\`tags\`/\`viewports
|
|
100
|
-
before the prose. A
|
|
101
|
-
|
|
98
|
+
(\`feature\`, \`profile\`, \`tags\`, \`viewports\`, \`cookies\`, \`storage\`). Each \`##\`
|
|
99
|
+
heading is one scenario; optional \`profile\`/\`notes\`/\`tags\`/\`viewports\`/
|
|
100
|
+
\`cookies\`/\`storage\` override lines may follow a heading before the prose. A
|
|
101
|
+
scenario's \`viewports\` list (built-ins: mobile/desktop/tablet) REPLACES the
|
|
102
|
+
file-level one and fans the scenario out into one run per viewport.
|
|
103
|
+
\`cookies:\`/\`storage:\` seed browser preconditions before the app loads (never
|
|
104
|
+
recorded steps); a \`value\` may use a \`$ENV:VAR\` placeholder resolved at launch.
|
|
102
105
|
Write the flow + expected behavior in plain language — no selectors, no code.
|
|
103
106
|
|
|
104
107
|
Copy the block below into a new \`*.scout.md\` file (outside the fence) to start:
|
|
@@ -109,6 +112,14 @@ feature: Paywall
|
|
|
109
112
|
profile: anon
|
|
110
113
|
tags: [monetization]
|
|
111
114
|
viewports: [mobile]
|
|
115
|
+
cookies: # list of objects (attributes go here)
|
|
116
|
+
- name: hn_checkout_variant
|
|
117
|
+
value: A
|
|
118
|
+
storage: # object with local / session / remove
|
|
119
|
+
local:
|
|
120
|
+
hn_app_open_count: "2"
|
|
121
|
+
remove:
|
|
122
|
+
- hn_pwa_prompt_dismissed
|
|
112
123
|
---
|
|
113
124
|
|
|
114
125
|
## Free user hits paywall on ep 3
|
|
@@ -117,6 +128,8 @@ Open ep 3 of series X without login; paywall appears with a signup CTA.
|
|
|
117
128
|
## Subscriber bypasses paywall
|
|
118
129
|
profile: qa
|
|
119
130
|
viewports: [mobile, desktop]
|
|
131
|
+
cookies: hn_checkout_variant=C # inline override: name=value[, n2=v2]
|
|
132
|
+
storage: local.hn_app_open_count=3, remove=flag # inline: local./session. keys, remove=key
|
|
120
133
|
|
|
121
134
|
Logged-in subscriber opens ep 3; plays with no paywall.
|
|
122
135
|
\`\`\`
|
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
|
@@ -5,9 +5,42 @@ export interface Target {
|
|
|
5
5
|
name?: string;
|
|
6
6
|
/** CSS path fallback when role+name is not unique */
|
|
7
7
|
css?: string;
|
|
8
|
+
/**
|
|
9
|
+
* data-testid — the location strategy for elements OUTSIDE the accessibility
|
|
10
|
+
* tree (a gesture layer, an overlay `<div>` with no role/name). Preferred over
|
|
11
|
+
* `css` when present: stable across DOM refactors and resolved via Playwright's
|
|
12
|
+
* `getByTestId`. Populated by selector clicks / state assertions.
|
|
13
|
+
*/
|
|
14
|
+
testId?: string;
|
|
8
15
|
/** Human-readable label for reports */
|
|
9
16
|
description: string;
|
|
10
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Matcher for a state assertion on an element found by a {@link Target}. Asserts
|
|
20
|
+
* VISUAL/structural state that text- and URL-based checks can't reach: a class
|
|
21
|
+
* token, an attribute, or a computed style. Its reason for being is the opacity
|
|
22
|
+
* toggle pattern — a control kept in the DOM but hidden with `opacity:0`, which
|
|
23
|
+
* Playwright's visibility (and therefore `assertNotVisible`) still counts as
|
|
24
|
+
* VISIBLE. Assert `opacity-0`/computed `opacity === "0"` instead. Every provided
|
|
25
|
+
* check must hold; polled until they do or the timeout elapses, so it is
|
|
26
|
+
* deterministic on replay.
|
|
27
|
+
*/
|
|
28
|
+
export interface ElementStateMatcher {
|
|
29
|
+
/** A class token that MUST be present (e.g. "opacity-0"). Membership, not full-string equality. */
|
|
30
|
+
hasClass?: string;
|
|
31
|
+
/** A class token that must NOT be present (e.g. "opacity-100"). */
|
|
32
|
+
notHasClass?: string;
|
|
33
|
+
/** An attribute that must be present; with `value`, it must equal `value`. */
|
|
34
|
+
attribute?: {
|
|
35
|
+
name: string;
|
|
36
|
+
value?: string;
|
|
37
|
+
};
|
|
38
|
+
/** A computed style that must equal `value` (e.g. property "opacity", value "0"). */
|
|
39
|
+
computedStyle?: {
|
|
40
|
+
property: string;
|
|
41
|
+
value: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
11
44
|
/** HTTP status family used by network assertions when an exact code is too strict. */
|
|
12
45
|
export type StatusClass = "2xx" | "3xx" | "4xx" | "5xx";
|
|
13
46
|
/**
|
|
@@ -71,6 +104,28 @@ export interface ScenarioCookie {
|
|
|
71
104
|
secure?: boolean;
|
|
72
105
|
sameSite?: "Strict" | "Lax" | "None";
|
|
73
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Web storage seeded into the browser context before the app loads, resolved
|
|
109
|
+
* from a scenario's spec (profile default + file frontmatter + per-section
|
|
110
|
+
* override, merged per key per namespace; `remove` concatenated). It is a
|
|
111
|
+
* context-creation parameter — re-resolved fresh from the spec each run and
|
|
112
|
+
* applied via an init-script that runs before any page script, so the seed is
|
|
113
|
+
* in place before the app reads it. It is NEVER a recorded/replayable Step and
|
|
114
|
+
* never lands in the committed script JSON (like storageState/cookies).
|
|
115
|
+
*
|
|
116
|
+
* Use it to make verifiable a feature whose trigger lives in storage: a "seen"
|
|
117
|
+
* flag, an open-count threshold, a dismissed prompt. Values may carry a
|
|
118
|
+
* `$ENV:VAR` placeholder, resolved only at launch. `remove` clears keys from
|
|
119
|
+
* BOTH localStorage and sessionStorage, guaranteeing a clean precondition.
|
|
120
|
+
*/
|
|
121
|
+
export interface ScenarioStorage {
|
|
122
|
+
/** localStorage key→value pairs to seed (value may be a `$ENV:VAR`). */
|
|
123
|
+
local?: Record<string, string>;
|
|
124
|
+
/** sessionStorage key→value pairs to seed (value may be a `$ENV:VAR`). */
|
|
125
|
+
session?: Record<string, string>;
|
|
126
|
+
/** Keys removed from both localStorage and sessionStorage before the seed. */
|
|
127
|
+
remove?: string[];
|
|
128
|
+
}
|
|
74
129
|
/**
|
|
75
130
|
* Browser permission policy resolved from a scenario's spec (file frontmatter
|
|
76
131
|
* is the default, per-section keys override it, merged per-axis). Three states
|
|
@@ -135,7 +190,11 @@ export type Step = {
|
|
|
135
190
|
kind: "assertNotVisible";
|
|
136
191
|
text: string;
|
|
137
192
|
timeout?: number;
|
|
138
|
-
} | {
|
|
193
|
+
} | ({
|
|
194
|
+
kind: "assertState";
|
|
195
|
+
target: Target;
|
|
196
|
+
timeout?: number;
|
|
197
|
+
} & ElementStateMatcher) | {
|
|
139
198
|
kind: "assertUrl";
|
|
140
199
|
pattern: string;
|
|
141
200
|
timeout?: number;
|
|
@@ -192,6 +251,14 @@ export interface Scenario {
|
|
|
192
251
|
* these. Carries raw `$ENV:VAR` placeholders — resolved only at launch.
|
|
193
252
|
*/
|
|
194
253
|
cookies?: ScenarioCookie[];
|
|
254
|
+
/**
|
|
255
|
+
* Web storage seeded on the scenario (profile default + file frontmatter +
|
|
256
|
+
* per-section override, merged per key per namespace). A context-creation
|
|
257
|
+
* parameter re-resolved each run, applied before the app loads — never baked
|
|
258
|
+
* into the recorded script. Carries raw `$ENV:VAR` placeholders — resolved
|
|
259
|
+
* only at launch.
|
|
260
|
+
*/
|
|
261
|
+
storage?: ScenarioStorage;
|
|
195
262
|
/** Source spec file, relative to the project root */
|
|
196
263
|
file: string;
|
|
197
264
|
}
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -53,9 +53,9 @@ Logged-in subscriber opens ep 3; the episode plays with no paywall.
|
|
|
53
53
|
|
|
54
54
|
Rules that matter when you author:
|
|
55
55
|
|
|
56
|
-
- **Frontmatter** (YAML, optional): `feature` (defaults to the filename), `profile` (default auth profile), `tags`, `viewports`.
|
|
56
|
+
- **Frontmatter** (YAML, optional): `feature` (defaults to the filename), `profile` (default auth profile), `tags`, `viewports`, `cookies`, `storage`.
|
|
57
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.
|
|
58
|
-
- **Per-scenario overrides:** immediately under a heading you may place `profile:`, `notes:`, `tags:`, `viewports:`, `grantPermissions:`, `denyPermissions:`, and `
|
|
58
|
+
- **Per-scenario overrides:** immediately under a heading you may place `profile:`, `notes:`, `tags:`, `viewports:`, `grantPermissions:`, `denyPermissions:`, `geolocation:`, `cookies:`, and `storage:` lines (before the prose) to override the file-level defaults.
|
|
59
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.
|
|
60
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).
|
|
61
61
|
|
|
@@ -127,6 +127,75 @@ Notes:
|
|
|
127
127
|
- **Grant wins over deny:** a scenario that grants a permission overrides a file-level deny of the same permission (as in the example above).
|
|
128
128
|
- `denyPermissions` matters mainly in **headed** runs (headless already denies silently). What changes behavior in CI is `grantPermissions` and a granted `geolocation` with coordinates.
|
|
129
129
|
|
|
130
|
+
## Cookies
|
|
131
|
+
|
|
132
|
+
When a flow's precondition is a cookie — forcing a server-side experiment variant, a feature flag, a consent state — declare it and Scout seeds it into the browser **before the first navigation**, for both the AI run and deterministic replay. Like `storageState`, it's a context-creation parameter, never a recorded step, so replay stays deterministic and the agent never needs a "set cookie" tool.
|
|
133
|
+
|
|
134
|
+
Two forms. In the **frontmatter** (file default) or a **profile** (shared base in `scout.config.json`), `cookies:` is a list of objects — the place for attributes:
|
|
135
|
+
|
|
136
|
+
```markdown
|
|
137
|
+
---
|
|
138
|
+
feature: Checkout
|
|
139
|
+
cookies:
|
|
140
|
+
- name: hn_checkout_variant
|
|
141
|
+
value: A # file default: variant A
|
|
142
|
+
- name: consent
|
|
143
|
+
value: "yes"
|
|
144
|
+
httpOnly: true
|
|
145
|
+
sameSite: Lax # Strict | Lax | None
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Default variant
|
|
149
|
+
Open checkout; the file-level variant A is in effect.
|
|
150
|
+
|
|
151
|
+
## Force variant C
|
|
152
|
+
cookies: hn_checkout_variant=C # per-scenario override: inline name=value
|
|
153
|
+
|
|
154
|
+
Open checkout; variant C is forced.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Notes:
|
|
158
|
+
|
|
159
|
+
- **Merge by name:** a profile's cookies are the base; the file frontmatter and then the per-scenario override win, keyed by cookie name. The per-`##` override is the terse inline `name=value[, n2=v2]` form (no attributes — those belong in the frontmatter/profile).
|
|
160
|
+
- **`name` and `value` are required;** `domain`, `path`, `expires` (unix seconds), `httpOnly`, `secure`, `sameSite` are optional. `domain`/`path` default to the host of `baseUrl` and `/`.
|
|
161
|
+
- **Secrets:** a `value` may use the `$ENV:VAR` placeholder (e.g. `cookies: session=$ENV:SESSION_TOKEN`) — resolved at launch, so the secret never lands in the committed spec or the agent's context. Only `value` is resolved.
|
|
162
|
+
- **Fail-fast:** an unknown field, a bad `sameSite`, or a missing env var is a hard error — a silently skipped cookie precondition would produce a misleading verdict.
|
|
163
|
+
|
|
164
|
+
## Local & session storage
|
|
165
|
+
|
|
166
|
+
Some features are gated not by a cookie or the server, but by the browser's own **web storage** — an open-count threshold, a "you've seen this" flag, a dismissed prompt. The agent can't set storage (it has no `page.evaluate` tool), so declare a `storage:` seed and Scout applies it **before the app loads**, for both the AI run and deterministic replay. Like `cookies`/`storageState`, it's a context-creation parameter, never a recorded step — replay re-resolves it fresh from the spec, so it stays deterministic and the agent never needs a "set storage" tool.
|
|
167
|
+
|
|
168
|
+
It seeds **both** `localStorage` and `sessionStorage` (the latter is something `storageState` can't carry) via an init-script that runs before any page script, and it can `remove` keys to guarantee a clean precondition.
|
|
169
|
+
|
|
170
|
+
Two forms. In the **frontmatter** (file default) or a **profile** (shared base in `scout.config.json`), `storage:` is an object with `local`, `session` and/or `remove`:
|
|
171
|
+
|
|
172
|
+
```markdown
|
|
173
|
+
---
|
|
174
|
+
feature: PWA install prompt
|
|
175
|
+
storage:
|
|
176
|
+
local:
|
|
177
|
+
hn_app_open_count: "2" # file default: two prior opens
|
|
178
|
+
remove:
|
|
179
|
+
- hn_pwa_prompt_dismissed # start from a non-dismissed state
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Below the threshold — no prompt yet
|
|
183
|
+
Open the app; the install prompt does not appear.
|
|
184
|
+
|
|
185
|
+
## At the threshold — prompt appears
|
|
186
|
+
storage: local.hn_app_open_count=3, remove=hn_other_flag
|
|
187
|
+
|
|
188
|
+
Open the app; the install prompt appears.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Notes:
|
|
192
|
+
|
|
193
|
+
- **Inline override form:** the per-`##` override is a single line of comma-separated tokens — `local.<key>=<value>`, `session.<key>=<value>`, or `remove=<key>`. The namespace is spelled inline because a heading override line can't carry a nested YAML object; nested objects belong in the frontmatter/profile.
|
|
194
|
+
- **Merge:** a profile's storage is the base; the file frontmatter and then the per-scenario override win, **per key per namespace**. `remove` lists **concatenate and dedupe** across all levels.
|
|
195
|
+
- **`remove` clears both namespaces:** a removed key is dropped from `localStorage` **and** `sessionStorage`. Removals apply before the seed, so a declared value always wins over a removal of the same key.
|
|
196
|
+
- **Secrets:** a value may use the `$ENV:VAR` placeholder — resolved at launch, so the secret never lands in the committed spec or the agent's context.
|
|
197
|
+
- **Fail-fast:** an unknown field (only `local`/`session`/`remove` are allowed), a non-string value, or a malformed inline token is a hard error — a silently skipped storage precondition would produce a misleading verdict.
|
|
198
|
+
|
|
130
199
|
## New tabs / popups
|
|
131
200
|
|
|
132
201
|
When a click opens a new tab (a `target="_blank"` link or `window.open`), Scout's `browser_click` result flags it. The agent then calls **`browser_switch_tab`** to move control to that tab; with no argument it switches to the newest tab, or pass a `urlGlob` (e.g. `**/booking**`) to target a specific one. The switch waits for the tab to finish loading, then becomes a recorded `switchTab` step that replays deterministically.
|
|
@@ -137,6 +206,14 @@ Console and network observers are **per-tab**: assertions (`browser_assert_no_co
|
|
|
137
206
|
|
|
138
207
|
Beyond *absence* of errors, you can assert a **specific log was emitted** (e.g. a `DEBUG:[...]` line gated behind a debug flag). `browser_assert_console_message` requires a message on the **active tab** that contains **all** of the given substrings **within a single message**, optionally constrained to a `type` (`log`, `debug`, `error`, …). Inspect first (`browser_inspect_logs` now also lists `log`/`debug`/`info` messages), then assert on a **stable** substring — a prefix like `DEBUG:[FEATURE/x]`, never a volatile value — so the check tolerates unrelated console noise. It becomes a recorded step that fails the deterministic replay if the log goes missing.
|
|
139
208
|
|
|
209
|
+
## Clicking role-less elements (by `data-testid`)
|
|
210
|
+
|
|
211
|
+
`browser_click` only takes a numbered `[ref]` from the accessibility snapshot, so an element with **no ARIA role or name** — a gesture/tap layer, an overlay `<div data-testid="…">`, a purely visual control — never gets a `[ref]` and can't be clicked that way. When you hit one, use **`browser_click_selector`**: it clicks by `data-testid` (**preferred** — stabler than a CSS path) or, if there is none, a `css` selector. It records as a plain deterministic `click` step, so replay needs no LLM. Reach for it only for elements the snapshot can't reference; a normal button still goes through `browser_click`.
|
|
212
|
+
|
|
213
|
+
## Asserting visual/structural state (opacity, class, attribute)
|
|
214
|
+
|
|
215
|
+
`browser_assert` covers text and URL, but it **can't confirm an element is hidden by `opacity:0`** — Playwright counts an opacity-hidden node (still in the DOM, still laid out) as *visible*, so a `notVisibleText`-style check would false-pass. For show/hide toggles and other purely visual state, use **`browser_assert_state`**: locate the element by `data-testid` (preferred) or `css`, then assert one or more of a class token (`hasClass` / `notHasClass`, e.g. `opacity-0` vs `opacity-100`), an `attribute` (present, or equal to a value like `aria-expanded=true`), or a `computedStyle` (e.g. `opacity` = `0`). It polls until every check holds or the timeout, then records a deterministic `assertState` step. This is what lets a scenario verify a reveal/hide control that stays mounted — describe the toggle in plain prose ("the drawer becomes hidden") and the agent records the class/style check.
|
|
216
|
+
|
|
140
217
|
## Base URL and secrets
|
|
141
218
|
|
|
142
219
|
There is a **default base URL** set at `scout init` (in `scout.config.json`). It can be **overridden per run** without editing the file:
|
package/templates/SKILL.md
CHANGED
|
@@ -20,6 +20,7 @@ Your job is to **co-author `.scout.md` scenarios** with QA, **verify** them with
|
|
|
20
20
|
without running it.
|
|
21
21
|
|
|
22
22
|
**Read `AGENTS.md` at the repo root — it is the canonical, always-current guide**
|
|
23
|
-
to the authoring loop, the `.scout.md` format,
|
|
23
|
+
to the authoring loop, the `.scout.md` format, per-scenario overrides (viewports,
|
|
24
|
+
permissions, `cookies:` and `storage:` preconditions), base-URL/secret handling,
|
|
24
25
|
verdicts, and failure triage. Follow it. For commands and flags, run
|
|
25
26
|
`scout --help` / `scout <command> --help`.
|
package/templates/scout.mdc
CHANGED
|
@@ -16,6 +16,7 @@ Co-author `.scout.md` scenarios with QA, verify them with `scout go`, and report
|
|
|
16
16
|
the **real** verdict — never claim a scenario passes without running it.
|
|
17
17
|
|
|
18
18
|
**Read `AGENTS.md` at the repo root** — it is the canonical, always-current
|
|
19
|
-
guide to the authoring loop, the `.scout.md` format,
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
guide to the authoring loop, the `.scout.md` format, per-scenario overrides
|
|
20
|
+
(viewports, permissions, `cookies:` and `storage:` preconditions),
|
|
21
|
+
base-URL/secret handling, verdicts, and failure triage. For commands and flags,
|
|
22
|
+
run `scout --help` / `scout <command> --help`.
|