craftdriver 1.7.0 → 1.9.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/CHANGELOG.md +25 -12
- package/README.md +16 -6
- package/dist/cli/agentSession.d.ts +62 -0
- package/dist/cli/agentSession.d.ts.map +1 -0
- package/dist/cli/agentSession.js +100 -0
- package/dist/cli/agentSession.js.map +1 -0
- package/dist/cli/artifactPaths.d.ts +42 -0
- package/dist/cli/artifactPaths.d.ts.map +1 -0
- package/dist/cli/artifactPaths.js +144 -0
- package/dist/cli/artifactPaths.js.map +1 -0
- package/dist/cli/bounds.d.ts +88 -0
- package/dist/cli/bounds.d.ts.map +1 -0
- package/dist/cli/bounds.js +128 -0
- package/dist/cli/bounds.js.map +1 -0
- package/dist/cli/client.d.ts +3 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +24 -8
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +152 -39
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/defaults.d.ts +23 -3
- package/dist/cli/defaults.d.ts.map +1 -1
- package/dist/cli/defaults.js +50 -5
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/dispatcher.d.ts +54 -11
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +851 -54
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +305 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +14 -14
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +328 -180
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/journal.d.ts +153 -0
- package/dist/cli/journal.d.ts.map +1 -0
- package/dist/cli/journal.js +252 -0
- package/dist/cli/journal.js.map +1 -0
- package/dist/cli/lineReader.d.ts +33 -0
- package/dist/cli/lineReader.d.ts.map +1 -0
- package/dist/cli/lineReader.js +66 -0
- package/dist/cli/lineReader.js.map +1 -0
- package/dist/cli/locatorCandidates.d.ts +44 -0
- package/dist/cli/locatorCandidates.d.ts.map +1 -0
- package/dist/cli/locatorCandidates.js +190 -0
- package/dist/cli/locatorCandidates.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +39 -1
- package/dist/cli/mcp/artifacts.d.ts.map +1 -1
- package/dist/cli/mcp/artifacts.js +104 -9
- package/dist/cli/mcp/artifacts.js.map +1 -1
- package/dist/cli/mcp/bounds.d.ts +65 -0
- package/dist/cli/mcp/bounds.d.ts.map +1 -0
- package/dist/cli/mcp/bounds.js +124 -0
- package/dist/cli/mcp/bounds.js.map +1 -0
- package/dist/cli/mcp/params.d.ts +38 -0
- package/dist/cli/mcp/params.d.ts.map +1 -0
- package/dist/cli/mcp/params.js +174 -0
- package/dist/cli/mcp/params.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +94 -16
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +304 -150
- package/dist/cli/mcp/server.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +62 -17
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +373 -158
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/cli/pageSemantics.d.ts +6 -0
- package/dist/cli/pageSemantics.d.ts.map +1 -0
- package/dist/cli/pageSemantics.js +6 -0
- package/dist/cli/pageSemantics.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +6 -18
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +689 -45
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/cli/protocol.d.ts +8 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/selector.d.ts +3 -3
- package/dist/cli/selector.d.ts.map +1 -1
- package/dist/cli/selector.js +6 -9
- package/dist/cli/selector.js.map +1 -1
- package/dist/cli/sessionRegistry.d.ts +42 -0
- package/dist/cli/sessionRegistry.d.ts.map +1 -0
- package/dist/cli/sessionRegistry.js +99 -0
- package/dist/cli/sessionRegistry.js.map +1 -0
- package/dist/cli/snapshot.d.ts +100 -19
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +295 -108
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/stateStore.d.ts +50 -0
- package/dist/cli/stateStore.d.ts.map +1 -0
- package/dist/cli/stateStore.js +122 -0
- package/dist/cli/stateStore.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/a11y.d.ts +6 -2
- package/dist/lib/a11y.d.ts.map +1 -1
- package/dist/lib/a11y.js +3 -1
- package/dist/lib/a11y.js.map +1 -1
- package/dist/lib/bidi/evaluate.d.ts +11 -0
- package/dist/lib/bidi/evaluate.d.ts.map +1 -0
- package/dist/lib/bidi/evaluate.js +25 -0
- package/dist/lib/bidi/evaluate.js.map +1 -0
- package/dist/lib/bidi/index.d.ts +1 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +2 -2
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +3 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -0
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +14 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +45 -7
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +30 -6
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +190 -45
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +9 -2
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +7 -4
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +186 -65
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +58 -24
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +300 -99
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +68 -30
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +54 -23
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/clearFastPath.d.ts.map +1 -1
- package/dist/lib/clearFastPath.js +2 -0
- package/dist/lib/clearFastPath.js.map +1 -1
- package/dist/lib/clickFastPath.d.ts.map +1 -1
- package/dist/lib/clickFastPath.js +2 -0
- package/dist/lib/clickFastPath.js.map +1 -1
- package/dist/lib/driver.d.ts +26 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +123 -4
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/elementHandle.d.ts +13 -10
- package/dist/lib/elementHandle.d.ts.map +1 -1
- package/dist/lib/elementHandle.js +119 -50
- package/dist/lib/elementHandle.js.map +1 -1
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +12 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/expect.d.ts +10 -0
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +76 -49
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/fillFastPath.d.ts.map +1 -1
- package/dist/lib/fillFastPath.js +2 -0
- package/dist/lib/fillFastPath.js.map +1 -1
- package/dist/lib/frame.d.ts.map +1 -1
- package/dist/lib/frame.js +29 -17
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/initScript.d.ts +9 -0
- package/dist/lib/initScript.d.ts.map +1 -0
- package/dist/lib/initScript.js +14 -0
- package/dist/lib/initScript.js.map +1 -0
- package/dist/lib/locator.d.ts +21 -54
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +165 -208
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/page.d.ts +22 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +71 -13
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/pageSemantics.d.ts +42 -0
- package/dist/lib/pageSemantics.d.ts.map +1 -0
- package/dist/lib/pageSemantics.js +216 -0
- package/dist/lib/pageSemantics.js.map +1 -0
- package/dist/lib/query.d.ts +68 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +590 -0
- package/dist/lib/query.js.map +1 -0
- package/dist/lib/secureFile.d.ts +8 -0
- package/dist/lib/secureFile.d.ts.map +1 -0
- package/dist/lib/secureFile.js +67 -0
- package/dist/lib/secureFile.js.map +1 -0
- package/dist/lib/sessionStateValidation.d.ts +35 -0
- package/dist/lib/sessionStateValidation.d.ts.map +1 -0
- package/dist/lib/sessionStateValidation.js +223 -0
- package/dist/lib/sessionStateValidation.js.map +1 -0
- package/dist/lib/shadowRoot.d.ts +21 -0
- package/dist/lib/shadowRoot.d.ts.map +1 -0
- package/dist/lib/shadowRoot.js +29 -0
- package/dist/lib/shadowRoot.js.map +1 -0
- package/dist/lib/shadowRootLocator.d.ts +31 -0
- package/dist/lib/shadowRootLocator.d.ts.map +1 -0
- package/dist/lib/shadowRootLocator.js +55 -0
- package/dist/lib/shadowRootLocator.js.map +1 -0
- package/dist/lib/timing.d.ts +2 -2
- package/dist/lib/timing.js +2 -2
- package/dist/lib/tracing.d.ts +2 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +12 -1
- package/dist/lib/tracing.js.map +1 -1
- package/docs/accessibility.md +9 -1
- package/docs/agents.md +56 -61
- package/docs/api-reference.md +5 -1
- package/docs/browser-api.md +27 -7
- package/docs/browser-context.md +21 -15
- package/docs/cli.md +353 -30
- package/docs/error-codes.md +7 -4
- package/docs/getting-started.md +6 -1
- package/docs/index.md +1 -1
- package/docs/mcp.md +136 -65
- package/docs/public/examples/agent-actions.html +48 -0
- package/docs/public/examples/agent-debug.html +86 -0
- package/docs/public/examples/agent-late-mutation.html +18 -0
- package/docs/public/examples/agent-ref-shift.html +33 -0
- package/docs/public/examples/iframe-child.html +12 -1
- package/docs/public/examples/shadow-dom.html +137 -0
- package/docs/recipes/login-once-reuse-session.md +33 -5
- package/docs/selectors.md +61 -0
- package/docs/session-management.md +80 -12
- package/docs/why-craftdriver.md +1 -1
- package/package.json +5 -3
- package/skills/craftdriver/SKILL.md +59 -75
- package/skills/craftdriver/agents/openai.yaml +4 -0
- package/skills/craftdriver/cheatsheet.md +18 -4
- package/skills/craftdriver/cli.md +194 -68
- package/skills/craftdriver/patterns.md +17 -8
- package/skills/craftdriver/workflow.md +149 -0
|
@@ -1,104 +1,230 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CraftDriver CLI for agent exploration
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Use the CLI to learn the live page and exercise a flow. Use the TypeScript
|
|
4
|
+
library for committed tests.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Safe setup
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
npm install craftdriver
|
|
9
|
+
npm install --save-dev craftdriver
|
|
10
|
+
npx craftdriver init codex
|
|
10
11
|
npx craftdriver --help
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
The init command installs `.agents/skills/craftdriver/`. It never reads or
|
|
15
|
+
changes `AGENTS.md`, `CLAUDE.md`, other assistant instructions, or
|
|
16
|
+
`.codex/config.toml`. `--dry-run` previews the installation. `--mcp` only prints
|
|
17
|
+
an optional project-pinned MCP snippet.
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
cookies, storage) survives between calls.
|
|
19
|
+
## Persistent browser
|
|
17
20
|
|
|
18
21
|
```bash
|
|
19
22
|
npx craftdriver daemon start
|
|
20
23
|
npx craftdriver go http://127.0.0.1:8080/login.html
|
|
21
|
-
npx craftdriver
|
|
24
|
+
npx craftdriver snapshot
|
|
22
25
|
npx craftdriver daemon stop
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
The first ordinary command auto-starts the daemon when needed. State, cookies,
|
|
29
|
+
and the active page survive between commands.
|
|
30
|
+
|
|
31
|
+
The daemon uses a Unix socket and is not available on Windows. On Windows, use
|
|
32
|
+
the configured MCP server for a persistent agent session, or send the whole
|
|
33
|
+
flow in one `--ephemeral` script.
|
|
34
|
+
|
|
35
|
+
## Current commands
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
go <url>
|
|
39
|
+
find <selector> [--all] [--limit N] [--offset N]
|
|
40
|
+
exists <selector>
|
|
41
|
+
click <selector>
|
|
42
|
+
dblclick <selector>
|
|
43
|
+
fill <selector> <value>
|
|
44
|
+
type <text>
|
|
45
|
+
clear <selector>
|
|
46
|
+
check <selector> | uncheck <selector>
|
|
47
|
+
select <selector> <value>
|
|
48
|
+
focus <selector> | scroll <selector>
|
|
49
|
+
press <key> [selector]
|
|
50
|
+
hover <selector>
|
|
51
|
+
key press|down|up <key> [--selector S]
|
|
52
|
+
mouse move|click [selector] [--x N --y N] [--button left|middle|right]
|
|
53
|
+
mouse down|up [--button left|middle|right]
|
|
54
|
+
mouse wheel [selector] [--delta-x N] [--delta-y N]
|
|
55
|
+
dialog inspect | dialog accept [text] | dialog dismiss
|
|
56
|
+
upload <selector> <file> [file...]
|
|
57
|
+
text [selector] [--limit N]
|
|
58
|
+
attr <selector> <name>
|
|
59
|
+
value <selector>
|
|
60
|
+
is visible|enabled|checked <selector>
|
|
61
|
+
wait <selector> [--state visible|hidden|attached|detached] [--timeout ms]
|
|
62
|
+
wait load [--state load|domcontentloaded|networkidle]
|
|
63
|
+
pages
|
|
64
|
+
page list | open [url] | select <index|id> | close <index|id>
|
|
65
|
+
snapshot
|
|
66
|
+
locators <selector>
|
|
67
|
+
screenshot [-o out.png] [--full-page] [--selector selector]
|
|
68
|
+
eval <javascript>
|
|
69
|
+
back | forward | reload | status | quit
|
|
70
|
+
daemon start | status | stop
|
|
71
|
+
session list | close [name]
|
|
72
|
+
state save <name> [--session-storage] | load <name> | list
|
|
73
|
+
logs [list] [--kind console,error,request,response] [--level l]
|
|
74
|
+
[--contains s] [--since N] [--limit N] | wait ... | clear
|
|
75
|
+
mock add <pattern> [--status N] [--body S] | block <pattern>
|
|
76
|
+
| list | remove <id> | clear
|
|
77
|
+
trace start [name] [--no-screenshots] | stop [--zip] | status
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Named sessions
|
|
28
81
|
|
|
29
|
-
|
|
82
|
+
Add `--session <name>` to any command to run it against its own browser,
|
|
83
|
+
cookies, page selection, snapshot baseline and refs. Omitting the flag means
|
|
84
|
+
the session named `default`.
|
|
30
85
|
|
|
31
86
|
```bash
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
' | npx craftdriver --ephemeral
|
|
87
|
+
npx craftdriver go http://127.0.0.1:8080/login.html --session shopper
|
|
88
|
+
npx craftdriver go http://127.0.0.1:8080/admin.html --session admin
|
|
89
|
+
npx craftdriver session list
|
|
90
|
+
npx craftdriver session close admin
|
|
37
91
|
```
|
|
38
92
|
|
|
39
|
-
|
|
93
|
+
Use this when one workflow needs two logged-in identities at once, not as a
|
|
94
|
+
default habit — each session is a real browser process and only 8 may be open
|
|
95
|
+
at a time. Sessions start on first use and run independently, so a slow
|
|
96
|
+
command in one does not block another.
|
|
40
97
|
|
|
41
|
-
|
|
98
|
+
**Refs are per-session.** `e4` in one session and `e4` in another are
|
|
99
|
+
different elements, and nothing will warn you if you mix them up. Run
|
|
100
|
+
`snapshot` and the commands that use its refs with the same `--session`.
|
|
42
101
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fill <
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
is
|
|
55
|
-
wait <sel> [--state visible|hidden|attached|detached] [--timeout ms]
|
|
56
|
-
wait load [--state load|domcontentloaded|networkidle]
|
|
57
|
-
pages
|
|
58
|
-
screenshot [-o out.png] [--full-page] [--selector sel]
|
|
59
|
-
eval <js> last resort
|
|
60
|
-
back | forward | reload | status | quit
|
|
61
|
-
daemon start|status|stop
|
|
62
|
-
```
|
|
102
|
+
`--session` is not available with `--ephemeral`, which is a single browser
|
|
103
|
+
that exits with the command.
|
|
104
|
+
|
|
105
|
+
`select` matches an `<option>` by its `value` attribute, not its label.
|
|
106
|
+
|
|
107
|
+
Use `fill <selector> <value>` to put a value in a field — it clears first and
|
|
108
|
+
delivers real key events, so fields with key handlers work. `type <text>` takes
|
|
109
|
+
no selector and types into whatever holds focus, mapping onto
|
|
110
|
+
`browser.keyboard.type()`; reach for `focus <selector>` then `type` only when
|
|
111
|
+
you need to append to existing text. `focus` leaves the caret at the end.
|
|
112
|
+
|
|
113
|
+
`eval` is a last resort for inspection, not a replacement for public test APIs.
|
|
63
114
|
|
|
64
115
|
## Selector syntax
|
|
65
116
|
|
|
66
|
-
CSS
|
|
117
|
+
CSS is the default. Prefix semantic selectors:
|
|
67
118
|
|
|
119
|
+
```text
|
|
120
|
+
role=button[name=Submit] label=Email testid=login-btn
|
|
121
|
+
text=Sign In text*=Sign placeholder=Search…
|
|
122
|
+
alt=Logo title=Help xpath=//div[1]
|
|
123
|
+
id=submit name=email tag=h1
|
|
68
124
|
```
|
|
69
|
-
role=button[name=Submit] text=Sign In text*=Sign
|
|
70
|
-
label=Email placeholder=Search… testid=login-btn
|
|
71
|
-
alt=Logo title=Help xpath=//div[1]
|
|
72
|
-
id=submit name=email tag=h1
|
|
73
|
-
```
|
|
74
125
|
|
|
75
|
-
|
|
76
|
-
|
|
126
|
+
Use `exists` as a zero-wait uniqueness probe and `find --all` to inspect
|
|
127
|
+
ambiguous matches.
|
|
128
|
+
|
|
129
|
+
## Tabs
|
|
130
|
+
|
|
131
|
+
Commands act on the active tab. A tab the application opens by itself is
|
|
132
|
+
listed by `page list` but is not selected, so nothing switches under you —
|
|
133
|
+
use `page select <index|id>` first. An unknown target is an error rather than
|
|
134
|
+
a fallback to the active tab. Switching, opening, or closing a tab clears
|
|
135
|
+
refs, so take a fresh `snapshot` afterwards.
|
|
136
|
+
|
|
137
|
+
## Console and network evidence
|
|
77
138
|
|
|
78
|
-
|
|
139
|
+
`logs` answers what the page logged and requested, after the fact — capture is
|
|
140
|
+
already running before your first command, so you never arrange it in advance
|
|
141
|
+
or re-run a flow to observe it.
|
|
79
142
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- Errors include a stable `code:` line (same codes as the library
|
|
84
|
-
— see `docs/error-codes.md`). Use the code, not the prose.
|
|
143
|
+
Use it to check your work: after a click, `logs --kind error` tells you whether
|
|
144
|
+
the app threw, and `logs --kind request,response --contains /api/` shows what it
|
|
145
|
+
called. This is the evidence to cite when explaining a failure.
|
|
85
146
|
|
|
86
|
-
|
|
147
|
+
Ask for deltas, not dumps: every result carries a `cursor`; pass it back as
|
|
148
|
+
`--since` to get only what happened since. `logs wait --contains <text>` scans
|
|
149
|
+
what already arrived before waiting, so it resolves immediately when the event
|
|
150
|
+
has already happened.
|
|
87
151
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
152
|
+
History is bounded (500 entries / 512 KB, oldest evicted). Check `dropped` and
|
|
153
|
+
`droppedBeforeCursor` before concluding that nothing happened — a non-zero
|
|
154
|
+
`droppedBeforeCursor` means the page you asked for has a hole in it.
|
|
155
|
+
|
|
156
|
+
Network rows carry URL, method, status, MIME type and time only. No bodies,
|
|
157
|
+
cookies or headers — do not report them as if they were a full capture. URLs
|
|
158
|
+
include query strings, so avoid echoing sensitive query tokens into a report.
|
|
159
|
+
|
|
160
|
+
## Traces and mocking
|
|
161
|
+
|
|
162
|
+
`trace start <name>` … `trace stop --zip` records actions, console, network and
|
|
163
|
+
screenshots under `.craftdriver/traces/`. Reach for it when a failure is not
|
|
164
|
+
explicable from a snapshot — not by default, since it costs size and time.
|
|
165
|
+
One trace at a time; a second `start` is an error.
|
|
166
|
+
|
|
167
|
+
`mock add <pattern> --status N --body S` serves a fixed response and
|
|
168
|
+
`mock block <pattern>` fails matching requests, so you can drive error paths
|
|
169
|
+
without touching the application. `mock list` and `mock clear` manage them;
|
|
170
|
+
clear them when done, since they outlive the command that added them.
|
|
171
|
+
|
|
172
|
+
Only these flat shapes exist. The library's `network.intercept()` takes a
|
|
173
|
+
handler function that no command line can express — if you need dynamic
|
|
174
|
+
behaviour, write it in a test with the library API instead of asking for a
|
|
175
|
+
CLI flag that does not exist.
|
|
176
|
+
|
|
177
|
+
## Saved login state
|
|
178
|
+
|
|
179
|
+
`state save <name>` captures the current browser's cookies and local storage;
|
|
180
|
+
`state load <name>` puts them back. Use it so you log in once instead of
|
|
181
|
+
replaying the form for every exploration.
|
|
182
|
+
|
|
183
|
+
On Chrome/Chromium and Firefox BiDi, load before the first real navigation:
|
|
184
|
+
`state load <name>` → `go <url>`. Cookies plus all captured localStorage origins
|
|
185
|
+
are ready for the page's first script. If a page is already open, reload it
|
|
186
|
+
after the overlay.
|
|
187
|
+
|
|
188
|
+
Classic browsers (including Safari) require `go <url>` → `state load <name>` →
|
|
189
|
+
`reload`, and only a single matching origin is supported. State saved with
|
|
190
|
+
`--session-storage` also requires that active-page order because sessionStorage
|
|
191
|
+
is tab-scoped. Unsupported combinations fail before mutation rather than
|
|
192
|
+
silently restoring only cookies.
|
|
193
|
+
|
|
194
|
+
State files are credentials — cookies included. They live under
|
|
195
|
+
`.craftdriver/state/`, owner-only. Never print one, commit one, or paste its
|
|
196
|
+
contents into a test; pass a bare name (`alice`), never a path. `state save`
|
|
197
|
+
reports counts and origins only, and that is all you should report onward.
|
|
198
|
+
|
|
199
|
+
`state load` clears refs, so take a fresh `snapshot` after it.
|
|
200
|
+
|
|
201
|
+
## Refs vs. durable locators
|
|
202
|
+
|
|
203
|
+
A `ref=eN` from `snapshot` names one element for as long as it lives. It cannot
|
|
204
|
+
drift onto a different element: if the element is removed or duplicated, or the
|
|
205
|
+
page navigates or reloads, the command fails with `STALE_REF` and you take a
|
|
206
|
+
fresh snapshot. Refs are never reused, so an old ref never matches a new page.
|
|
207
|
+
|
|
208
|
+
Refs are still exploration state and never belong in test code. To write a test,
|
|
209
|
+
turn the element into a durable selector and let CraftDriver check it against
|
|
210
|
+
the live page first:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx craftdriver locators ref=e7
|
|
214
|
+
```
|
|
91
215
|
|
|
92
|
-
|
|
216
|
+
Each candidate is reported `unique`, `ambiguous`, or `missing`, ordered by
|
|
217
|
+
durability (role + name, label, test id, unique text, minimal CSS). Use one
|
|
218
|
+
reported `unique`. If none is, add a `data-testid` to the application rather
|
|
219
|
+
than committing a positional selector.
|
|
93
220
|
|
|
94
|
-
|
|
95
|
-
`CRAFTDRIVER_AGENT_TIMEOUT`).
|
|
96
|
-
- Probe with `exists` before `click`/`wait` when you're guessing.
|
|
97
|
-
- Don't `sleep`. `wait <sel>` is auto-waiting; `wait load` waits on
|
|
98
|
-
document state.
|
|
221
|
+
## Output and failures
|
|
99
222
|
|
|
100
|
-
|
|
223
|
+
- TTY output is concise text; piped output is JSON per line.
|
|
224
|
+
- `--json` and `--pretty` force the output format.
|
|
225
|
+
- Exit `0` is success, `1` is action/assertion/no-match failure, and `2` is
|
|
226
|
+
invalid CLI usage.
|
|
227
|
+
- Errors carry the same stable codes as the public library.
|
|
101
228
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
chainable `Locator` ergonomics.
|
|
229
|
+
Do not add sleeps. CLI actions already auto-wait, and `wait` expresses explicit
|
|
230
|
+
page conditions.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CraftDriver — patterns
|
|
2
2
|
|
|
3
|
-
Worked recipes
|
|
4
|
-
[SKILL.md](SKILL.md).
|
|
3
|
+
Worked recipes, each kept compact. Load on demand from [SKILL.md](SKILL.md).
|
|
5
4
|
|
|
6
5
|
## 1. Login, save storage state for reuse
|
|
7
6
|
|
|
7
|
+
Add `.auth/` to the consuming project's `.gitignore`; these files contain live
|
|
8
|
+
session cookies even though CraftDriver writes them with restricted permissions.
|
|
9
|
+
|
|
8
10
|
```ts
|
|
9
11
|
const browser = await Browser.launch();
|
|
10
12
|
await browser.navigateTo('https://app.example.com/login');
|
|
@@ -13,21 +15,28 @@ await browser.locator(By.labelText('Password')).fill(process.env.PW!);
|
|
|
13
15
|
await browser.locator(By.role('button', { name: 'Sign in' })).click();
|
|
14
16
|
await browser.locator(By.testId('dashboard')).expect().toBeVisible();
|
|
15
17
|
|
|
16
|
-
// Persist
|
|
17
|
-
|
|
18
|
-
await fs.writeFile('.auth/state.json', JSON.stringify(state));
|
|
18
|
+
// Persist atomically with owner-only permissions where supported.
|
|
19
|
+
await browser.saveState('.auth/state.json');
|
|
19
20
|
await browser.quit();
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
## 2. Re-use saved login
|
|
23
24
|
|
|
25
|
+
On a BiDi session, launch restores cookies and every captured localStorage
|
|
26
|
+
origin before the first real navigation. Application changes then survive
|
|
27
|
+
reload because the state is seeded once, not replayed on every document.
|
|
28
|
+
|
|
24
29
|
```ts
|
|
25
|
-
const
|
|
26
|
-
const browser = await Browser.launch({ storageState: state });
|
|
30
|
+
const browser = await Browser.launch({ storageState: '.auth/state.json' });
|
|
27
31
|
await browser.navigateTo('https://app.example.com/dashboard');
|
|
28
32
|
await browser.locator(By.testId('dashboard')).expect().toBeVisible();
|
|
29
33
|
```
|
|
30
34
|
|
|
35
|
+
Classic WebDriver cannot seed arbitrary origins before navigation: launch
|
|
36
|
+
rejects non-empty state. Launch first, navigate to the sole captured origin,
|
|
37
|
+
then call `browser.loadState('.auth/state.json')`. Snapshots containing
|
|
38
|
+
sessionStorage also require that active-origin flow, including on BiDi.
|
|
39
|
+
|
|
31
40
|
## 3. Wait for a network response after a click
|
|
32
41
|
|
|
33
42
|
```ts
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Explore an application and write a CraftDriver test
|
|
2
|
+
|
|
3
|
+
This workflow turns live browser evidence into an ordinary, reviewable test.
|
|
4
|
+
|
|
5
|
+
## 1. Learn the project
|
|
6
|
+
|
|
7
|
+
Before editing, inspect `package.json`, existing test files, fixtures, and the
|
|
8
|
+
commands the repository already uses. Reuse its test runner, naming, setup, URL,
|
|
9
|
+
and browser lifecycle. Do not create a second test framework or edit repository
|
|
10
|
+
assistant instructions.
|
|
11
|
+
|
|
12
|
+
## 2. Start the application and browser
|
|
13
|
+
|
|
14
|
+
Run the project's existing app command in its own process. Then:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx craftdriver daemon start
|
|
18
|
+
npx craftdriver go http://127.0.0.1:3000
|
|
19
|
+
npx craftdriver snapshot
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use the actual project URL and wait for a clear page condition when necessary.
|
|
23
|
+
On Windows the daemon is unavailable; use the configured MCP server, or put
|
|
24
|
+
the complete exploration flow into one `--ephemeral` script.
|
|
25
|
+
|
|
26
|
+
## 3. Inspect before acting
|
|
27
|
+
|
|
28
|
+
Read role/name, labels, test IDs, visible text, and nearby DOM structure from the
|
|
29
|
+
snapshot. Prefer a semantic selector and prove it:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx craftdriver exists 'role=button[name=Sign in]'
|
|
33
|
+
npx craftdriver find 'label=Email' --all
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Refs from the snapshot are safe to act on: a ref binds to one element, and if
|
|
37
|
+
that element is gone or the page navigated, the command fails with `STALE_REF`
|
|
38
|
+
instead of hitting something else. Take a fresh snapshot when that happens.
|
|
39
|
+
|
|
40
|
+
Never copy `ref=eN` into test code. When you have the right element, ask for a
|
|
41
|
+
durable selector and let CraftDriver validate it against the live page:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx craftdriver locators ref=e7
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Use a candidate it reports as `unique`. If none is unique, add a `data-testid`
|
|
48
|
+
to the application rather than committing a positional selector.
|
|
49
|
+
|
|
50
|
+
## 4. Exercise the user flow
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx craftdriver fill 'label=Email' alice@example.test
|
|
54
|
+
npx craftdriver fill 'label=Password' secret
|
|
55
|
+
npx craftdriver click 'role=button[name=Sign in]'
|
|
56
|
+
npx craftdriver text 'testid=welcome'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After each meaningful mutation, inspect fresh output or snapshot state. If a
|
|
60
|
+
selector is ambiguous, refine it from observed structure and validate again.
|
|
61
|
+
|
|
62
|
+
**The DOM is not the whole story.** An application can fail while showing a
|
|
63
|
+
generic message, or succeed while logging an error you should not ship. Check
|
|
64
|
+
what the page actually did:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx craftdriver logs --kind error # exceptions + console.error
|
|
68
|
+
npx craftdriver logs --kind request,response --contains /api/
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Capture is already running before your first command, so this works after the
|
|
72
|
+
fact — you never re-run a flow to observe it. Assert on what you find, not on
|
|
73
|
+
what the page happens to render.
|
|
74
|
+
|
|
75
|
+
## 5. Write the smallest durable test
|
|
76
|
+
|
|
77
|
+
Follow the repository's style. A direct test may look like:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { Browser, By, expect } from 'craftdriver';
|
|
81
|
+
|
|
82
|
+
const browser = await Browser.launch({ browserName: 'chrome' });
|
|
83
|
+
try {
|
|
84
|
+
await browser.navigateTo('http://127.0.0.1:3000');
|
|
85
|
+
await browser.locator(By.labelText('Email')).fill('alice@example.test');
|
|
86
|
+
await browser.locator(By.labelText('Password')).fill('secret');
|
|
87
|
+
await browser.locator(By.role('button', { name: 'Sign in' })).click();
|
|
88
|
+
await expect(browser.locator(By.testId('welcome'))).toHaveText(/welcome/i);
|
|
89
|
+
} finally {
|
|
90
|
+
await browser.quit();
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Use the exact public API patterns already present in the repository when they
|
|
95
|
+
differ from this standalone example.
|
|
96
|
+
|
|
97
|
+
## 6. Run and debug
|
|
98
|
+
|
|
99
|
+
Run the narrowest existing test command. On failure:
|
|
100
|
+
|
|
101
|
+
1. read the stable CraftDriver error code;
|
|
102
|
+
2. reproduce against the current app;
|
|
103
|
+
3. take a fresh snapshot and re-check the locator with `locators`;
|
|
104
|
+
4. inspect only the relevant page value/text/attribute;
|
|
105
|
+
5. **read the evidence before theorising** — see below;
|
|
106
|
+
6. change application or test source through an explicit diff;
|
|
107
|
+
7. rerun the focused test, then the repository's required checks.
|
|
108
|
+
|
|
109
|
+
### Read the evidence
|
|
110
|
+
|
|
111
|
+
Most failures that look mysterious in the DOM are explicit in the console or
|
|
112
|
+
the network journal:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx craftdriver logs --kind error # exceptions and console.error
|
|
116
|
+
npx craftdriver logs --kind response --contains /api/ # status codes
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
A page that renders "Something went wrong" tells you nothing; a 4xx on
|
|
120
|
+
`/api/checkout` and a matching `console.error` tell you exactly where to look.
|
|
121
|
+
`--kind error` covers both uncaught exceptions and `console.error`, since a
|
|
122
|
+
caller asking for errors means both.
|
|
123
|
+
Quote that evidence when you explain the failure, rather than guessing from the
|
|
124
|
+
rendered text.
|
|
125
|
+
|
|
126
|
+
For a failure that a snapshot cannot explain — a race, or something that only
|
|
127
|
+
happens mid-flow — record the whole run and inspect it afterwards:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx craftdriver trace start bug
|
|
131
|
+
# …reproduce…
|
|
132
|
+
npx craftdriver trace stop --zip
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
To confirm a diagnosis, drive the branch directly instead of waiting for the
|
|
136
|
+
real backend to misbehave:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npx craftdriver mock add '**/api/checkout*' --status 500
|
|
140
|
+
npx craftdriver mock clear # always clean up; mocks outlive the command
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Never heal a test at runtime
|
|
144
|
+
|
|
145
|
+
Do not make a failing test green through sleeps, broad catch blocks, weaker
|
|
146
|
+
assertions, or silent runtime selector substitution. If the application
|
|
147
|
+
changed, change the test in an explicit source diff and say what changed and
|
|
148
|
+
why. A test that hides a real application change is worse than a failing one,
|
|
149
|
+
because it removes the signal without fixing the cause.
|