@testdriverai/agent 7.11.229-test → 7.11.232-test

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 (100) hide show
  1. package/docs/_data/examples-manifest.json +16 -16
  2. package/docs/_scripts/generate-examples.js +38 -45
  3. package/docs/agent.mdx +174 -93
  4. package/docs/assert.mdx +73 -269
  5. package/docs/aws-setup.mdx +196 -314
  6. package/docs/bring-your-own-machine.mdx +139 -206
  7. package/docs/cache.mdx +144 -151
  8. package/docs/caching.mdx +99 -89
  9. package/docs/captcha.mdx +126 -111
  10. package/docs/ci-cd.mdx +154 -543
  11. package/docs/click.mdx +74 -229
  12. package/docs/client.mdx +177 -231
  13. package/docs/copilot/auto-healing.mdx +86 -370
  14. package/docs/copilot/running-tests.mdx +121 -930
  15. package/docs/customizing-devices.mdx +74 -251
  16. package/docs/dashcam.mdx +72 -405
  17. package/docs/debugging-with-screenshots.mdx +105 -371
  18. package/docs/docs.json +79 -22
  19. package/docs/double-click.mdx +48 -68
  20. package/docs/errors.mdx +102 -176
  21. package/docs/events.mdx +170 -327
  22. package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
  23. package/docs/examples/assert.mdx +28 -41
  24. package/docs/examples/click-button.mdx +71 -0
  25. package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
  26. package/docs/examples/element-not-found.mdx +27 -26
  27. package/docs/examples/findall-coffee-icons.mdx +28 -32
  28. package/docs/examples/installer.mdx +61 -25
  29. package/docs/examples/launch-vscode-linux.mdx +28 -51
  30. package/docs/examples/navigate-and-scroll.mdx +82 -0
  31. package/docs/examples/parse.mdx +24 -28
  32. package/docs/examples/press-keys.mdx +28 -25
  33. package/docs/examples/scroll.mdx +25 -27
  34. package/docs/examples/type.mdx +26 -21
  35. package/docs/exec.mdx +92 -256
  36. package/docs/extract.mdx +69 -170
  37. package/docs/find.mdx +140 -736
  38. package/docs/focus-application.mdx +69 -246
  39. package/docs/generating-tests.mdx +103 -137
  40. package/docs/guide/best-practices-polling.mdx +65 -141
  41. package/docs/hosted.mdx +80 -144
  42. package/docs/hover.mdx +62 -235
  43. package/docs/locating-elements.mdx +74 -369
  44. package/docs/making-assertions.mdx +116 -74
  45. package/docs/mcp-setup.mdx +80 -63
  46. package/docs/mouse-down.mdx +48 -125
  47. package/docs/mouse-up.mdx +47 -129
  48. package/docs/options.mdx +75 -48
  49. package/docs/parse.mdx +95 -184
  50. package/docs/performing-actions.mdx +104 -94
  51. package/docs/press-keys.mdx +59 -299
  52. package/docs/provision.mdx +137 -182
  53. package/docs/quickstart-claude.mdx +85 -0
  54. package/docs/quickstart-github.mdx +62 -32
  55. package/docs/quickstart-manual.mdx +60 -33
  56. package/docs/quickstart-vscode.mdx +97 -0
  57. package/docs/quickstart.mdx +26 -9
  58. package/docs/redraw.mdx +101 -165
  59. package/docs/reusable-code.mdx +97 -200
  60. package/docs/right-click.mdx +47 -89
  61. package/docs/screenshot.mdx +47 -201
  62. package/docs/screenshots.mdx +87 -137
  63. package/docs/scroll.mdx +71 -154
  64. package/docs/secrets.mdx +96 -91
  65. package/docs/self-hosted.mdx +83 -142
  66. package/docs/snippets/enterprise.mdx +15 -0
  67. package/docs/styles.css +68 -0
  68. package/docs/test-results-json.mdx +167 -204
  69. package/docs/type.mdx +56 -304
  70. package/docs/variables.mdx +121 -81
  71. package/docs/wait.mdx +33 -31
  72. package/docs/what-is-testdriver.mdx +80 -32
  73. package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
  74. package/examples/assert.test.mjs +3 -20
  75. package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
  76. package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
  77. package/examples/element-not-found.test.mjs +3 -5
  78. package/examples/findall-coffee-icons.test.mjs +3 -3
  79. package/examples/installer.test.mjs +1 -1
  80. package/examples/launch-vscode-linux.test.mjs +6 -33
  81. package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
  82. package/examples/parse.test.mjs +1 -2
  83. package/examples/press-keys.test.mjs +4 -4
  84. package/examples/scroll.test.mjs +3 -6
  85. package/examples/type.test.mjs +5 -2
  86. package/lib/provision.js +6 -2
  87. package/package.json +1 -1
  88. package/sdk.d.ts +4 -2
  89. package/sdk.js +1 -1
  90. package/docs/claude-mcp-plugin.mdx +0 -244
  91. package/docs/elements.mdx +0 -606
  92. package/docs/examples/ai.mdx +0 -78
  93. package/docs/examples/captcha-api.mdx +0 -92
  94. package/docs/examples/chrome-extension.mdx +0 -156
  95. package/docs/examples/drag-and-drop.mdx +0 -100
  96. package/docs/examples/formatted-logging.mdx +0 -74
  97. package/docs/examples/hover-text.mdx +0 -70
  98. package/docs/examples/match-image.mdx +0 -97
  99. package/docs/examples/scroll-keyboard.mdx +0 -80
  100. package/docs/examples/scroll-until-text.mdx +0 -109
@@ -1,258 +1,221 @@
1
1
  ---
2
- title: "Test Result JSON"
3
- sidebarTitle: "Test Result JSON"
4
- description: "Per-test JSON result files with metadata, versions, and infrastructure details"
2
+ title: "Test result JSON"
3
+ sidebarTitle: "Test result JSON"
4
+ description: "Read each test's result, replay link, and run details from a JSON file."
5
5
  icon: "file-code"
6
6
  ---
7
7
 
8
- ## Overview
8
+ TestDriver writes a JSON file for each test after it finishes.
9
9
 
10
- TestDriver writes a JSON result file for each test case after it finishes. These files have full metadata about the test run. This includes the SDK version, the runner version, infrastructure details, interaction statistics, and links to recordings.
10
+ - It has the test's result, error, and links to its report and replay.
11
+ - It also records versions, sandbox details, and how many `find()` calls hit the cache.
12
+ - Use it in CI scripts instead of parsing console output.
11
13
 
12
- TestDriver writes the result files to:
14
+ ## Turn it on
13
15
 
14
- ```
15
- .testdriver/results/<testFile>/<testName>.json
16
- ```
16
+ The TestDriver Vitest reporter writes the files. `npx testdriverai init` adds it to `vitest.config.js` for you:
17
17
 
18
- For example, a test file `tests/login.test.mjs` with a test named `"should log in"` produces:
18
+ ```javascript vitest.config.js
19
+ import { defineConfig } from 'vitest/config';
20
+ import TestDriver from 'testdriverai/vitest';
19
21
 
22
+ export default defineConfig({
23
+ test: {
24
+ testTimeout: 300000,
25
+ hookTimeout: 300000,
26
+ reporters: ['default', TestDriver()],
27
+ setupFiles: ['testdriverai/vitest/setup'],
28
+ },
29
+ });
20
30
  ```
21
- .testdriver/results/tests/login.test.mjs/should_log_in.json
22
- ```
23
-
24
- <Note>
25
- TestDriver makes the test names safe for the file system. It changes special characters to underscores. It cuts the names to 200 characters.
26
- </Note>
27
-
28
- ## Enabling
29
-
30
- You do not need configuration. The TestDriver Vitest reporter plugin writes the JSON files automatically each time the tests run.
31
-
32
- ## JSON Schema
33
-
34
- Each result file is organized into logical groups:
35
-
36
- ### `versions`
37
-
38
- | Field | Type | Description |
39
- |---|---|---|
40
- | `versions.sdk` | `string \| null` | TestDriver SDK version (e.g. `"7.8.0"`) |
41
- | `versions.vitest` | `string \| null` | Vitest version used to run the test |
42
- | `versions.api` | `string \| null` | TestDriver API server version |
43
- | `versions.runnerBefore` | `string \| null` | Runner version at sandbox start |
44
- | `versions.runnerAfter` | `string \| null` | Runner version after auto-update |
45
- | `versions.runnerWasUpdated` | `boolean` | Whether the runner was auto-updated during provisioning |
46
-
47
- ### `test`
48
-
49
- | Field | Type | Description |
50
- |---|---|---|
51
- | `test.file` | `string \| null` | Relative path to the test file |
52
- | `test.name` | `string \| null` | Name of the test case |
53
- | `test.suite` | `string \| null` | Name of the parent `describe` block |
54
- | `test.passed` | `boolean` | Whether the test passed |
55
- | `test.caseId` | `string \| null` | Database ID for this test case |
56
- | `test.runId` | `string \| null` | Database ID for the overall test run |
57
- | `test.error` | `string \| null` | Error message if the test failed |
58
- | `test.errorStack` | `string \| null` | Error stack trace if the test failed |
59
-
60
- ### `urls`
61
31
 
62
- | Field | Type | Description |
63
- |---|---|---|
64
- | `urls.api` | `string \| null` | API root URL used for this test |
65
- | `urls.console` | `string \| null` | TestDriver console base URL |
66
- | `urls.vnc` | `string \| null` | VNC URL for the sandbox |
67
- | `urls.testRun` | `string \| null` | Direct link to this test case in the console |
68
-
69
- ### `replay`
70
-
71
- The `replay` object contains the recording replay URL and derived embed links. The `gifUrl` and `embedUrl` are generated automatically from the replay URL.
72
-
73
- | Field | Type | Description |
74
- |---|---|---|
75
- | `replay.url` | `string \| null` | Recording replay URL |
76
- | `replay.gifUrl` | `string \| null` | Animated GIF thumbnail of the recording |
77
- | `replay.embedUrl` | `string \| null` | Embeddable replay URL (appends `&embed=true`) |
78
- | `replay.markdown` | `string \| null` | Ready-to-use Markdown embed with GIF linking to the replay |
32
+ The reporter records each run in TestDriver, so it needs your API key in `TD_API_KEY`. If it can't record the run, it doesn't write the files.
79
33
 
80
- The `replay.markdown` field produces a clickable GIF badge you can paste directly into PR comments, README files, or issue descriptions:
34
+ ## Where the files go
81
35
 
82
- ```markdown
83
- [![Test Recording](https://api.testdriver.ai/replay/abc123/gif?shareKey=xyz)](https://console.testdriver.ai/replay/abc123?share=xyz)
36
+ ```text
37
+ .testdriver/results/<test file>/<test id>.json
84
38
  ```
85
39
 
86
- ### `date`
40
+ - `<test file>` is the test file's path relative to the project root, such as `tests/login.test.mjs`.
41
+ - `<test id>` is Vitest's ID for the test, with characters other than letters, digits, `_`, `.`, and `-` replaced by `_`. Read the test's name from `test.name` inside the file.
87
42
 
88
- | Field | Type | Description |
89
- |---|---|---|
90
- | `date` | `string` | ISO 8601 timestamp when the test finished |
91
-
92
- ### `team`
93
-
94
- | Field | Type | Description |
95
- |---|---|---|
96
- | `team.id` | `string \| null` | Team ID from the sandbox |
97
- | `team.sessionId` | `string \| null` | SDK session ID |
43
+ The folder isn't cleared between runs. Delete `.testdriver/results` before a run if you only want the latest results.
98
44
 
99
- ### `infrastructure`
100
-
101
- | Field | Type | Description |
102
- |---|---|---|
103
- | `infrastructure.sandboxId` | `string \| null` | Sandbox instance ID |
104
- | `infrastructure.instanceId` | `string \| null` | Instance ID |
105
- | `infrastructure.os` | `string \| null` | Operating system of the sandbox (`"linux"` or `"windows"`) |
106
- | `infrastructure.amiId` | `string \| null` | AWS AMI ID used for provisioning |
107
- | `infrastructure.e2bTemplateId` | `string \| null` | E2B template ID used for provisioning |
108
- | `infrastructure.imageVersion` | `string \| null` | Sandbox image version |
109
-
110
- ### `realtime`
111
-
112
- | Field | Type | Description |
113
- |---|---|---|
114
- | `realtime.channel` | `string \| null` | Ably channel name used for communication |
115
- | `realtime.messageCount` | `number` | Number of messages published to the realtime channel |
116
-
117
- ### `interactions`
118
-
119
- | Field | Type | Description |
120
- |---|---|---|
121
- | `interactions.total` | `number` | Total number of interactions recorded |
122
- | `interactions.cached` | `number` | Number of interactions served from cache |
123
- | `interactions.byType` | `object` | Breakdown of interactions by type (e.g. `find`, `click`, `assert`) |
45
+ ## Example
124
46
 
125
- ## Example Output
126
-
127
- ```json
47
+ ```json .testdriver/results/tests/login.test.mjs/1223128da3_0_0.json
128
48
  {
129
- "sdkVersion": "7.8.0",
130
- "vitestVersion": "4.0.0",
131
- "apiVersion": "1.45.0",
132
- "runnerVersionBefore": "2.1.0",
133
- "runnerVersionAfter": "2.1.1",
134
- "wasUpdated": true,
135
- "apiUrl": "https://api.testdriver.ai",
136
- "consoleUrl": "https://console.testdriver.ai",
137
- "testRunLink": "https://console.testdriver.ai/runs/abc123/def456",
138
- "dashcamUrl": "https://app.dashcam.io/replay/abc123",
139
- "vncUrl": "wss://sandbox-123.testdriver.ai/vnc",
140
- "date": "2025-01-15T14:30:00.000Z",
49
+ "versions": {
50
+ "sdk": "7.11.228",
51
+ "vitest": "4.0.8",
52
+ "api": "7.11.228",
53
+ "runnerBefore": "2.4.1",
54
+ "runnerAfter": "2.4.1",
55
+ "runnerWasUpdated": false
56
+ },
57
+ "test": {
58
+ "file": "tests/login.test.mjs",
59
+ "name": "shows an error for an empty form",
60
+ "suite": "Login",
61
+ "passed": false,
62
+ "caseId": "6712f0c4e1a2b3c4d5e6f701",
63
+ "runId": "6712f0b9e1a2b3c4d5e6f700",
64
+ "error": "AI Assertion failed: an error says the fields are required",
65
+ "errorStack": "Error: AI Assertion failed: ..."
66
+ },
67
+ "urls": {
68
+ "api": "https://api.testdriver.ai",
69
+ "console": "https://console.testdriver.ai",
70
+ "vnc": null,
71
+ "testRun": "https://console.testdriver.ai/runs/6712f0b9e1a2b3c4d5e6f700/6712f0c4e1a2b3c4d5e6f701"
72
+ },
73
+ "replay": {
74
+ "url": "https://console.testdriver.ai/replay/6789abcdef0123456789abcd?share=k3y",
75
+ "gifUrl": "https://api.testdriver.ai/replay/6789abcdef0123456789abcd/gif?shareKey=k3y",
76
+ "embedUrl": "https://console.testdriver.ai/replay/6789abcdef0123456789abcd?share=k3y&embed=true",
77
+ "markdown": "[![Test Recording](https://api.testdriver.ai/replay/6789abcdef0123456789abcd/gif?shareKey=k3y)](https://console.testdriver.ai/replay/6789abcdef0123456789abcd?share=k3y)"
78
+ },
79
+ "date": "2026-10-21T14:30:00.000Z",
141
80
  "team": {
142
81
  "id": "team_abc123",
143
82
  "sessionId": "sess_xyz789"
144
83
  },
145
84
  "infrastructure": {
146
- "sandboxId": "sandbox-123",
147
- "instanceId": "i-abc123",
85
+ "sandboxId": "sbx-123",
86
+ "instanceId": null,
148
87
  "os": "linux",
149
- "amiId": "ami-0abc123",
88
+ "amiId": null,
150
89
  "e2bTemplateId": null,
151
- "imageVersion": "v2.1.0"
90
+ "imageVersion": null
152
91
  },
153
92
  "realtime": {
154
- "channel": "sandbox:sandbox-123",
93
+ "channel": "sandbox:sbx-123",
155
94
  "messageCount": 42
156
95
  },
157
96
  "interactions": {
158
- "total": 15,
159
- "cached": 3,
160
- "byType": {
161
- "find": 8,
162
- "click": 5,
163
- "assert": 2
164
- }
97
+ "total": 3,
98
+ "cached": 1,
99
+ "byType": { "find": 3 }
165
100
  }
166
101
  }
167
102
  ```
168
103
 
169
- ## Using Result Files in CI
104
+ Values that TestDriver doesn't know are `null`.
170
105
 
171
- Result files are useful for extracting test metadata in CI pipelines without parsing log output.
106
+ ## Fields
172
107
 
173
- ### GitHub Actions Example
108
+ ### versions
174
109
 
175
- Use `fromJSON` to parse a result file into a GitHub Actions expression you can reference in subsequent steps:
110
+ | Field | Type | Description |
111
+ | --- | --- | --- |
112
+ | `versions.sdk` | `string \| null` | TestDriver SDK version |
113
+ | `versions.vitest` | `string \| null` | Vitest version |
114
+ | `versions.api` | `string \| null` | TestDriver API version |
115
+ | `versions.runnerBefore` | `string \| null` | Sandbox runner version when the sandbox started |
116
+ | `versions.runnerAfter` | `string \| null` | Runner version after any update during setup |
117
+ | `versions.runnerWasUpdated` | `boolean` | Whether the runner was updated during setup |
176
118
 
177
- ```yaml
178
- - name: Run tests
179
- run: vitest run tests/login.test.mjs
119
+ ### test
180
120
 
181
- - name: Parse result
182
- id: result
183
- run: |
184
- # Read the first JSON result file
185
- FILE=$(find .testdriver/results -name '*.json' | head -n 1)
186
- echo "json=$(cat "$FILE")" >> "$GITHUB_OUTPUT"
187
-
188
- - name: Comment on PR
189
- if: fromJSON(steps.result.outputs.json).test.passed == false
190
- uses: actions/github-script@v7
191
- with:
192
- script: |
193
- const result = ${{ steps.result.outputs.json }};
194
- await github.rest.issues.createComment({
195
- owner: context.repo.owner,
196
- repo: context.repo.repo,
197
- issue_number: context.issue.number,
198
- body: [
199
- `❌ **${result.test.name}** failed`,
200
- ``,
201
- `Error: ${result.test.error}`,
202
- ``,
203
- result.replay.markdown,
204
- ``,
205
- `[View full recording](${result.urls.testRun})`
206
- ].join('\n')
207
- });
208
- ```
121
+ | Field | Type | Description |
122
+ | --- | --- | --- |
123
+ | `test.file` | `string \| null` | Test file path, relative to the directory you ran the tests from |
124
+ | `test.name` | `string \| null` | The test's name |
125
+ | `test.suite` | `string \| null` | The enclosing `describe` names, outermost first, joined with ` > ` |
126
+ | `test.passed` | `boolean` | Whether the test passed |
127
+ | `test.caseId` | `string \| null` | ID of this test's result in TestDriver |
128
+ | `test.runId` | `string \| null` | ID of the whole run in TestDriver |
129
+ | `test.error` | `string \| null` | The error message, if the test failed |
130
+ | `test.errorStack` | `string \| null` | The error's stack trace, if the test failed |
131
+
132
+ ### urls
209
133
 
210
- You can also load all results into a matrix or iterate over them:
134
+ | Field | Type | Description |
135
+ | --- | --- | --- |
136
+ | `urls.api` | `string \| null` | The TestDriver API the test used |
137
+ | `urls.console` | `string \| null` | The TestDriver console |
138
+ | `urls.vnc` | `string \| null` | The sandbox's VNC address |
139
+ | `urls.testRun` | `string \| null` | This test's report in the console |
140
+
141
+ ### replay
142
+
143
+ The test's [Dashcam](/dashcam) recording.
144
+
145
+ | Field | Type | Description |
146
+ | --- | --- | --- |
147
+ | `replay.url` | `string \| null` | The replay link |
148
+ | `replay.gifUrl` | `string \| null` | An animated GIF of the recording |
149
+ | `replay.embedUrl` | `string \| null` | The replay link with `&embed=true`, for embedding |
150
+ | `replay.markdown` | `string \| null` | Markdown for the GIF, linked to the replay, ready to paste into a pull request comment or README |
151
+
152
+ `gifUrl`, `embedUrl`, and `markdown` are filled in only when the replay link includes a `?share=` key.
153
+
154
+ ### date, team, and infrastructure
155
+
156
+ | Field | Type | Description |
157
+ | --- | --- | --- |
158
+ | `date` | `string` | When the test finished, as an ISO 8601 string |
159
+ | `team.id` | `string \| null` | Your team's ID |
160
+ | `team.sessionId` | `string \| null` | The SDK session ID |
161
+ | `infrastructure.sandboxId` | `string \| null` | The sandbox's ID |
162
+ | `infrastructure.instanceId` | `string \| null` | The sandbox's instance ID |
163
+ | `infrastructure.os` | `string \| null` | `"linux"` or `"windows"` |
164
+ | `infrastructure.amiId` | `string \| null` | The machine image the sandbox started from |
165
+ | `infrastructure.e2bTemplateId` | `string \| null` | The sandbox template the sandbox started from |
166
+ | `infrastructure.imageVersion` | `string \| null` | The sandbox image version |
167
+
168
+ ### realtime and interactions
169
+
170
+ | Field | Type | Description |
171
+ | --- | --- | --- |
172
+ | `realtime.channel` | `string \| null` | The realtime channel used to talk to the sandbox |
173
+ | `realtime.messageCount` | `number` | Messages sent on that channel |
174
+ | `interactions.total` | `number` | Number of `find()` and `findAll()` calls |
175
+ | `interactions.cached` | `number` | How many of those were answered from the [cache](/caching) |
176
+ | `interactions.byType` | `object` | Counts per method, such as `{ "find": 3, "findAll": 1 }` |
177
+
178
+ ## Use the results in CI
179
+
180
+ This GitHub Actions step adds a table of results to the job summary:
211
181
 
212
182
  ```yaml
213
183
  - name: Run tests
214
- run: vitest run tests/*.test.mjs
215
-
216
- - name: Collect results
217
- id: results
218
- run: |
219
- # Merge all result files into a JSON array
220
- echo "json=$(find .testdriver/results -name '*.json' -exec cat {} + | jq -s '.')" >> "$GITHUB_OUTPUT"
184
+ run: npx vitest run
221
185
 
222
- - name: Summary
186
+ - name: Summarize results
187
+ if: always()
223
188
  run: |
224
- echo '## Test Results' >> $GITHUB_STEP_SUMMARY
225
- RESULTS='${{ steps.results.outputs.json }}'
226
- echo "$RESULTS" | jq -r '.[] | "| \(.test.name) | \(if .test.passed then "✅" else "❌" end) | \(.urls.testRun) |"' >> $GITHUB_STEP_SUMMARY
189
+ echo '| Test | Result | Report |' >> "$GITHUB_STEP_SUMMARY"
190
+ echo '| --- | --- | --- |' >> "$GITHUB_STEP_SUMMARY"
191
+ find .testdriver/results -name '*.json' -exec cat {} + \
192
+ | jq -rs '.[] | "| \(.test.name) | \(if .test.passed then "Passed" else "Failed" end) | \(.urls.testRun) |"' \
193
+ >> "$GITHUB_STEP_SUMMARY"
227
194
  ```
228
195
 
229
- ### Reading Results Programmatically
230
-
231
- ```javascript
232
- import fs from "fs";
233
- import path from "path";
196
+ To read the results in Node.js:
234
197
 
235
- const resultsDir = ".testdriver/results";
198
+ ```javascript scripts/report.mjs
199
+ import fs from 'node:fs';
200
+ import path from 'node:path';
236
201
 
237
- function readResults(dir) {
238
- const results = [];
239
- for (const testDir of fs.readdirSync(dir, { recursive: true })) {
240
- const fullPath = path.join(dir, testDir);
241
- if (fullPath.endsWith(".json") && fs.statSync(fullPath).isFile()) {
242
- results.push(JSON.parse(fs.readFileSync(fullPath, "utf-8")));
243
- }
244
- }
245
- return results;
246
- }
202
+ const dir = '.testdriver/results';
203
+ const results = fs
204
+ .readdirSync(dir, { recursive: true })
205
+ .filter((file) => file.endsWith('.json'))
206
+ .map((file) => JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8')));
247
207
 
248
- const results = readResults(resultsDir);
249
- const passed = results.filter(r => r.test.passed);
250
- const failed = results.filter(r => !r.test.passed);
208
+ const failed = results.filter((result) => !result.test.passed);
209
+ console.log(`${results.length - failed.length} passed, ${failed.length} failed`);
251
210
 
252
- console.log(`${passed.length} passed, ${failed.length} failed`);
253
- for (const r of failed) {
254
- console.log(` FAIL: ${r.test.name} — ${r.test.error}`);
255
- console.log(` Recording: ${r.urls.testRun}`);
256
- console.log(` Embed: ${r.replay.markdown}`);
211
+ for (const result of failed) {
212
+ console.log(`FAIL ${result.test.name}: ${result.test.error}`);
213
+ console.log(` Report: ${result.urls.testRun}`);
257
214
  }
258
215
  ```
216
+
217
+ ## Related
218
+
219
+ - [Dashcam](/dashcam): how the replay is recorded
220
+ - [Run in CI/CD](/ci-cd): run your tests on every pull request
221
+ - [Run tests](/copilot/running-tests): run tests locally and in parallel