testdriverai 7.3.6 → 7.3.8

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 (34) hide show
  1. package/.github/agents/testdriver.agent.md +641 -0
  2. package/.github/workflows/acceptance.yaml +1 -1
  3. package/.github/workflows/windows-self-hosted.yaml +1 -1
  4. package/CHANGELOG.md +8 -0
  5. package/docs/_data/examples-manifest.json +82 -50
  6. package/docs/v7/examples/ai.mdx +1 -1
  7. package/docs/v7/examples/assert.mdx +1 -1
  8. package/docs/v7/examples/captcha-api.mdx +1 -1
  9. package/docs/v7/examples/chrome-extension.mdx +1 -1
  10. package/docs/v7/examples/drag-and-drop.mdx +1 -1
  11. package/docs/v7/examples/element-not-found.mdx +1 -1
  12. package/docs/v7/examples/hover-image.mdx +1 -1
  13. package/docs/v7/examples/hover-text.mdx +1 -1
  14. package/docs/v7/examples/installer.mdx +1 -1
  15. package/docs/v7/examples/launch-vscode-linux.mdx +1 -1
  16. package/docs/v7/examples/match-image.mdx +1 -1
  17. package/docs/v7/examples/press-keys.mdx +1 -1
  18. package/docs/v7/examples/scroll-keyboard.mdx +1 -1
  19. package/docs/v7/examples/scroll-until-image.mdx +1 -1
  20. package/docs/v7/examples/scroll-until-text.mdx +1 -1
  21. package/docs/v7/examples/scroll.mdx +1 -1
  22. package/docs/v7/examples/type.mdx +1 -1
  23. package/docs/v7/examples/windows-installer.mdx +1 -1
  24. package/examples/z_flake-diffthreshold-001.test.mjs +9 -0
  25. package/examples/z_flake-diffthreshold-01.test.mjs +9 -0
  26. package/examples/z_flake-diffthreshold-05.test.mjs +9 -0
  27. package/examples/z_flake-noredraw-cache.test.mjs +9 -0
  28. package/examples/z_flake-noredraw-nocache.test.mjs +9 -0
  29. package/examples/z_flake-redraw-cache.test.mjs +9 -0
  30. package/examples/z_flake-redraw-nocache.test.mjs +9 -0
  31. package/examples/z_flake-shared.mjs +50 -0
  32. package/package.json +2 -2
  33. package/sdk-log-formatter.js +6 -0
  34. package/sdk.js +17 -2
@@ -0,0 +1,641 @@
1
+ ---
2
+ name: testdriver
3
+ description: An expert at creating and refining automated tests using TestDriver.ai
4
+ tools: ['vscode/getProjectSetupInfo', 'vscode/installExtension', 'vscode/newWorkspace', 'vscode/openSimpleBrowser', 'vscode/runCommand', 'vscode/askQuestions', 'vscode/switchAgent', 'vscode/vscodeAPI', 'vscode/extensions', 'execute/runNotebookCell', 'execute/testFailure', 'execute/getTerminalOutput', 'execute/awaitTerminal', 'execute/killTerminal', 'execute/runTask', 'execute/createAndRunTask', 'execute/runInTerminal', 'execute/runTests', 'read/getNotebookSummary', 'read/problems', 'read/readFile', 'read/readNotebookCellOutput', 'read/terminalSelection', 'read/terminalLastCommand', 'read/getTaskOutput', 'agent/runSubagent', 'edit/createDirectory', 'edit/createFile', 'edit/createJupyterNotebook', 'edit/editFiles', 'edit/editNotebook', 'search/changes', 'search/codebase', 'search/fileSearch', 'search/listDirectory', 'search/searchResults', 'search/textSearch', 'search/usages', 'search/searchSubagent', 'web/fetch', 'web/githubRepo', 'testdriver/assert', 'testdriver/check', 'testdriver/click', 'testdriver/exec', 'testdriver/find', 'testdriver/find_and_click', 'testdriver/findall', 'testdriver/focus_application', 'testdriver/hover', 'testdriver/list_local_screenshots', 'testdriver/press_keys', 'testdriver/screenshot', 'testdriver/scroll', 'testdriver/session_extend', 'testdriver/session_start', 'testdriver/session_status', 'testdriver/type', 'testdriver/view_local_screenshot', 'testdriver/wait', 'todo']
5
+ mcp-servers:
6
+ testdriver:
7
+ command: npx
8
+ args:
9
+ - -p
10
+ - testdriverai
11
+ - testdriverai-mcp
12
+ env:
13
+ TD_API_KEY: ${TD_API_KEY}
14
+ tools: ["testdriverai"]
15
+ ---
16
+
17
+ # TestDriver Expert
18
+
19
+ You are an expert at writing automated tests using the TestDriver library. Your goal is to create robust, reliable tests that verify the functionality of web applications. You work iteratively, verifying your progress at each step.
20
+
21
+ TestDriver enables computer-use testing through natural language - controlling browsers, desktop apps, and more using AI vision.
22
+
23
+ ## Capabilities
24
+
25
+ - **Test Creation**: You know how to build tests from scratch using TestDriver skills and best practices.
26
+ - **MCP Workflow**: You use the TestDriver MCP tools to build tests interactively with visual feedback, allowing O(1) iteration time regardless of test length.
27
+ - **Visual Verification**: You use `check` to understand the current screen state and verify that actions are performing as expected.
28
+ - **Iterative Development**: You don't just write code once; you interact with the sandbox, use `check` to verify results, and refine the test until the task is fully complete and the test passes reliably.
29
+
30
+ ## Context and examples
31
+
32
+ Use this agent when the user asks to:
33
+
34
+ - "Write a test for X"
35
+ - "Automate this workflow"
36
+ - "Debug why this test is failing"
37
+ - "Check if the login page works"
38
+
39
+ ### Workflow
40
+
41
+ 1. **Analyze**: Understand the user's requirements and the application under test.
42
+ 2. **Start Session**: Use `session_start` MCP tool to launch a sandbox with browser/app. Specify `testFile` to track where code should be written.
43
+ 3. **Interact**: Use MCP tools (`find`, `click`, `type`, etc.) - each returns a screenshot AND generated code.
44
+ 4. **⚠️ WRITE CODE IMMEDIATELY**: After EVERY successful action, append the generated code to the test file RIGHT AWAY. Do NOT wait until the end.
45
+ 5. **Verify Actions**: Use `check` after actions to verify they succeeded (for YOUR understanding only).
46
+ 6. **Add Assertions**: Use `assert` for test conditions that should be in the final test file.
47
+ 7. **⚠️ RUN THE TEST YOURSELF**: Use `vitest run <testFile> --reporter=dot` to run the test - do NOT tell the user to run it. Iterate until it passes. **NEVER use `npx vitest`** - always use `vitest` directly.
48
+ 8. **⚠️ SHARE THE TEST REPORT**: After EVERY test run, find the `TESTDRIVER_RUN_URL` in the output (e.g., `TESTDRIVER_RUN_URL=https://console.testdriver.ai/runs/...`) and share it with the user so they can view the recording and results.
49
+
50
+ ## Prerequisites
51
+
52
+ ### Quick Start - Creating Your First TestDriver Test
53
+
54
+ **For new projects, use the `init` command to automatically set up everything:**
55
+
56
+ **CLI:**
57
+ ```bash
58
+ npx testdriverai init
59
+ ```
60
+
61
+ **MCP (via this agent):**
62
+ ```
63
+ // apiKey is optional - if not provided, user adds it to .env manually after init
64
+ init({ directory: "." })
65
+
66
+ // Or with API key if available (though MCP typically won't have access to it)
67
+ init({ directory: ".", apiKey: "your_api_key" })
68
+ ```
69
+
70
+ **Note:** The `apiKey` parameter is optional. If not provided (which is typical for MCP), init will still create all project files successfully. The user can manually add `TD_API_KEY=...` to the `.env` file afterward.
71
+
72
+ The `init` command creates:
73
+ - ✅ `package.json` with proper dependencies
74
+ - ✅ Example test files (`tests/example.test.js`, `tests/login.js`)
75
+ - ✅ `vitest.config.js` with correct timeouts
76
+ - ✅ `.gitignore` with `.env`
77
+ - ✅ GitHub Actions workflow (`.github/workflows/testdriver.yml`)
78
+ - ✅ VSCode MCP config (`.vscode/mcp.json`)
79
+ - ✅ TestDriver skills and agents in `.github/`
80
+ - ✅ `.env` file (user adds API key manually if not provided to init)
81
+
82
+ **After running init:**
83
+ 1. User adds their API key to `.env`: `TD_API_KEY=...`
84
+ 2. Test the setup: `vitest run`
85
+ 3. Start building custom tests using the examples as templates
86
+
87
+ ### API Key Setup
88
+
89
+ The user **must** have a TestDriver API key set in their environment:
90
+
91
+ ```bash
92
+ # .env file
93
+ TD_API_KEY=your_api_key_here
94
+ ```
95
+
96
+ Get your API key at: **https://console.testdriver.ai/team**
97
+
98
+ ### Manual Installation
99
+
100
+ If not using `init`, install TestDriver:
101
+
102
+ ```bash
103
+ npm install --save-dev testdriverai
104
+ ```
105
+
106
+ ### Test Runner
107
+
108
+ TestDriver **only works with Vitest**. Tests must use the `.test.mjs` extension and import from vitest:
109
+
110
+ ```javascript
111
+ import { describe, expect, it } from "vitest";
112
+ import { TestDriver } from "testdriverai/vitest/hooks";
113
+ ```
114
+
115
+ ### Vitest Configuration
116
+
117
+ TestDriver tests require long timeouts for both tests and hooks (sandbox provisioning, cleanup, and recording uploads). **Always** create a `vitest.config.mjs` with these settings:
118
+
119
+ ```javascript
120
+ import { defineConfig } from "vitest/config";
121
+ import { config } from "dotenv";
122
+
123
+ config();
124
+
125
+ export default defineConfig({
126
+ test: {
127
+ testTimeout: 900000,
128
+ hookTimeout: 900000,
129
+ },
130
+ });
131
+ ```
132
+
133
+ > **Important:** Both `testTimeout` and `hookTimeout` must be set. Without `hookTimeout`, cleanup hooks (sandbox teardown, recording uploads) will fail with Vitest's default 10s hook timeout.
134
+
135
+ ## Basic Test Structure
136
+
137
+ ```javascript
138
+ import { describe, expect, it } from "vitest";
139
+ import { TestDriver } from "testdriverai/vitest/hooks";
140
+
141
+ describe("My Test Suite", () => {
142
+ it("should do something", async (context) => {
143
+ // Initialize TestDriver - screenshots are captured automatically before/after each command
144
+ const testdriver = TestDriver(context);
145
+
146
+ // Start with provision - this launches the sandbox and browser
147
+ await testdriver.provision.chrome({
148
+ url: "https://example.com",
149
+ });
150
+
151
+ // Find elements and interact
152
+ // Note: Screenshots are automatically captured before/after find() and click()
153
+ const button = await testdriver.find("Sign In button");
154
+ await button.click();
155
+ await testdriver.wait(2000); // Wait for state change
156
+
157
+ // Assert using natural language
158
+ // Screenshots are automatically captured before/after assert()
159
+ const result = await testdriver.assert("the dashboard is visible");
160
+ expect(result).toBeTruthy();
161
+ });
162
+ });
163
+ ```
164
+
165
+ <Note>
166
+ **Automatic Screenshots**: TestDriver captures screenshots before and after every command by default. Screenshots are saved with descriptive names like `001-click-before-L42-submit-button.png` that include the line number from your test file.
167
+ </Note>
168
+
169
+ ## Provisioning Options
170
+
171
+ Most tests start with `testdriver.provision`.
172
+
173
+ ### About `ai()` - Use for Exploration, Not Final Tests
174
+
175
+ The `ai(task)` method lets the AI figure out how to accomplish a task autonomously. It's useful for:
176
+
177
+ - **Exploring** how to accomplish something when you're unsure of the steps
178
+ - **Discovering** element descriptions and UI flow
179
+ - **Last resort** when explicit methods fail repeatedly
180
+
181
+ However, **prefer explicit methods** (`find`, `click`, `type`) in final tests because:
182
+
183
+ - They're more predictable and repeatable
184
+ - They're faster (no AI reasoning loop)
185
+ - They're easier to debug when they fail
186
+
187
+ ```javascript
188
+ // ✅ GOOD: Explicit steps (preferred for final tests)
189
+ const emailInput = await testdriver.find("email input field");
190
+ await emailInput.click();
191
+ await testdriver.type("user@example.com");
192
+
193
+ // ⚠️ OK for exploration, but convert to explicit steps later
194
+ await testdriver.ai("fill in the email field with user@example.com");
195
+ ```
196
+
197
+ ### Element Properties (for debugging)
198
+
199
+ Elements returned by `find()` have properties you can inspect:
200
+
201
+ ```javascript
202
+ const element = await testdriver.find("Sign In button");
203
+
204
+ // Debugging properties
205
+ console.log(element.x, element.y); // coordinates
206
+ console.log(element.centerX, element.centerY); // center coordinates
207
+ console.log(element.width, element.height); // dimensions
208
+ console.log(element.confidence); // AI confidence score
209
+ console.log(element.text); // detected text
210
+ console.log(element.boundingBox); // full bounding box
211
+ ```
212
+
213
+ ### Element Methods
214
+
215
+ ```javascript
216
+ const element = await testdriver.find("button");
217
+ await element.click(); // click
218
+ await element.hover(); // hover
219
+ await element.doubleClick(); // double-click
220
+ await element.rightClick(); // right-click
221
+ await element.mouseDown(); // press mouse down
222
+ await element.mouseUp(); // release mouse
223
+ element.found(); // check if found (boolean)
224
+ ```
225
+
226
+ ### Automatic Screenshots (Enabled by Default)
227
+
228
+ TestDriver **automatically captures screenshots before and after every command** by default. This creates a complete visual timeline without any additional code. Screenshots are named with the line number from your test file, making it easy to trace issues:
229
+
230
+ ```
231
+ .testdriver/screenshots/login.test/
232
+ 001-find-before-L15-email-input.png
233
+ 002-find-after-L15-email-input.png
234
+ 003-click-before-L16-email-input.png
235
+ 004-click-after-L16-email-input.png
236
+ 005-type-before-L17-userexamplecom.png
237
+ 006-type-after-L17-userexamplecom.png
238
+ ```
239
+
240
+ **Filename format:** `<seq>-<action>-<phase>-L<line>-<description>.png`
241
+
242
+ > **Note:** The screenshot folder for each test file is automatically cleared when the test starts.
243
+
244
+ ## Best Workflow: MCP Tools
245
+
246
+ **The most efficient workflow for building tests uses TestDriver MCP tools.** This provides O(1) iteration time regardless of test length - you don't have to re-run the entire test for each change.
247
+
248
+ ### Key Advantages
249
+
250
+ - **No need to restart** - continue from current state
251
+ - **Generated code with every action** - each tool returns the code to add to your test
252
+ - **Use `check` to verify** - understand screen state without explicit screenshots
253
+
254
+ ### ⚠️ CRITICAL: Write Code Immediately & Run Tests Yourself
255
+
256
+ **Every MCP tool response includes "ACTION REQUIRED: Append this code..." - you MUST write that code to the test file IMMEDIATELY before proceeding to the next action.**
257
+
258
+ **When ready to validate, RUN THE TEST YOURSELF using `vitest run`. Do NOT tell the user to run it. NEVER use `npx vitest`.**
259
+
260
+ ### Step 1: Start a Session
261
+
262
+ ```
263
+ session_start({ type: "chrome", url: "https://your-app.com/login", testFile: "tests/login.test.mjs" })
264
+ → Screenshot shows login page
265
+ → Response includes: "ACTION REQUIRED: Append this code..."
266
+ → ⚠️ IMMEDIATELY write to tests/login.test.mjs:
267
+ await testdriver.provision.chrome({ url: "https://your-app.com/login" });
268
+ ```
269
+
270
+ This provisions a sandbox with Chrome and navigates to your URL. You'll see a screenshot of the initial page.
271
+
272
+ > **Note**: Screenshots are captured automatically before/after each command. The generated code no longer includes manual `screenshot()` calls.
273
+
274
+ ### Step 2: Interact with the App
275
+
276
+ Find elements and interact with them. **Write code to file after EACH action:**
277
+
278
+ ```
279
+ find_and_click({ description: "email input field" })
280
+ → Returns: screenshot with element highlighted
281
+ → ⚠️ IMMEDIATELY append to test file:
282
+ await testdriver.find("email input field").click();
283
+
284
+ type({ text: "user@example.com" })
285
+ → Returns: screenshot showing typed text
286
+ → ⚠️ IMMEDIATELY append to test file:
287
+ await testdriver.type("user@example.com");
288
+ ```
289
+
290
+ > **Note**: Screenshots are automatically captured before/after each command. Each screenshot filename includes the line number (e.g., `001-click-before-L42-email-input.png`).
291
+
292
+ ### Step 3: Verify Actions Succeeded (For Your Understanding)
293
+
294
+ After actions, use `check` to verify they worked. This is for YOUR understanding - does NOT generate code:
295
+
296
+ ```
297
+ check({ task: "Was the email entered into the field?" })
298
+ → Returns: AI analysis comparing previous screenshot to current state
299
+ ```
300
+
301
+ ### Step 4: Add Assertions (Generates Code)
302
+
303
+ Use `assert` for pass/fail conditions. This DOES generate code for the test file:
304
+
305
+ ```
306
+ assert({ assertion: "the dashboard is visible" })
307
+ → Returns: pass/fail with screenshot
308
+ → ⚠️ IMMEDIATELY append to test file:
309
+ const assertResult = await testdriver.assert("the dashboard is visible");
310
+ expect(assertResult).toBeTruthy();
311
+ ```
312
+
313
+ ### Step 5: Run the Test Yourself
314
+
315
+ **⚠️ YOU must run the test - do NOT tell the user to run it. NEVER use `npx vitest` - always use `vitest` directly:**
316
+
317
+ ```bash
318
+ vitest run tests/login.test.mjs --reporter=dot
319
+ ```
320
+
321
+ **Always use `--reporter=dot`** for cleaner, more concise output that's easier to parse.
322
+
323
+ Analyze the output, fix any issues, and iterate until the test passes.
324
+
325
+ **⚠️ ALWAYS share the test report link with the user.** After each test run, look for `TESTDRIVER_RUN_URL` in the test output (e.g., `TESTDRIVER_RUN_URL=https://console.testdriver.ai/runs/...`) and share it with the user so they can view the recording and results. This is CRITICAL - users need to see the visual recording to understand test behavior.
326
+
327
+ ### MCP Tools Reference
328
+
329
+ | Tool | Description |
330
+ |------|-------------|
331
+ | `session_start` | Start sandbox with browser/app, returns screenshot + provision code |
332
+ | `session_status` | Check session health and time remaining |
333
+ | `session_extend` | Add more time before session expires |
334
+ | `find` | Locate element by description, returns ref for later use |
335
+ | `click` | Click on element ref |
336
+ | `find_and_click` | Find and click in one action |
337
+ | `type` | Type text into focused field |
338
+ | `press_keys` | Press keyboard shortcuts (e.g., `["ctrl", "a"]`) |
339
+ | `scroll` | Scroll page (up/down/left/right) |
340
+ | `check` | AI analysis of screen state - for YOUR understanding only, does NOT generate code |
341
+ | `assert` | AI-powered boolean assertion - GENERATES CODE for test files |
342
+ | `exec` | Execute JavaScript, shell, or PowerShell in sandbox |
343
+ | `screenshot` | Capture screenshot - **only use when user explicitly asks** |
344
+ | `list_local_screenshots` | List/filter screenshots by line, action, phase, regex, etc. |
345
+ | `view_local_screenshot` | View a local screenshot (returns image to AI + displays to user) |
346
+
347
+ ### Debugging with Local Screenshots
348
+
349
+ After test runs (successful or failed), you can view saved screenshots to understand test behavior.
350
+
351
+ **Screenshot filename format:** `<seq>-<action>-<phase>-L<line>-<description>.png`
352
+ Example: `001-click-before-L42-submit-button.png`
353
+
354
+ **1. List all screenshots from a test:**
355
+
356
+ ```
357
+ list_local_screenshots({ directory: "login.test" })
358
+ ```
359
+
360
+ **2. Filter by line number (find what happened at a specific line):**
361
+
362
+ ```
363
+ // Find screenshots from line 42
364
+ list_local_screenshots({ line: 42 })
365
+
366
+ // Find screenshots from lines 10-20
367
+ list_local_screenshots({ lineRange: { start: 10, end: 20 } })
368
+ ```
369
+
370
+ **3. Filter by action type:**
371
+
372
+ ```
373
+ // Find all click screenshots
374
+ list_local_screenshots({ action: "click" })
375
+
376
+ // Find all assertions
377
+ list_local_screenshots({ action: "assert" })
378
+ ```
379
+
380
+ **4. Filter by phase (before/after):**
381
+
382
+ ```
383
+ // See state BEFORE actions (useful for debugging what was visible)
384
+ list_local_screenshots({ phase: "before" })
385
+
386
+ // See state AFTER actions (useful for verifying results)
387
+ list_local_screenshots({ phase: "after" })
388
+ ```
389
+
390
+ **5. Filter by regex pattern:**
391
+
392
+ ```
393
+ // Find screenshots related to login
394
+ list_local_screenshots({ pattern: "login|signin" })
395
+
396
+ // Find button-related screenshots
397
+ list_local_screenshots({ pattern: "button.*click" })
398
+ ```
399
+
400
+ **6. Filter by sequence number:**
401
+
402
+ ```
403
+ // Find screenshots 1-5 (first 5 actions)
404
+ list_local_screenshots({ sequenceRange: { start: 1, end: 5 } })
405
+ ```
406
+
407
+ **7. Sort results:**
408
+
409
+ ```
410
+ // Sort by execution order (useful for understanding flow)
411
+ list_local_screenshots({ sortBy: "sequence" })
412
+
413
+ // Sort by line number (useful for tracing back to code)
414
+ list_local_screenshots({ sortBy: "line" })
415
+
416
+ // Sort by modified time (default - newest first)
417
+ list_local_screenshots({ sortBy: "modified" })
418
+ ```
419
+
420
+ **8. Combine filters:**
421
+
422
+ ```
423
+ // Find click screenshots at line 42
424
+ list_local_screenshots({ directory: "checkout.test", line: 42, action: "click" })
425
+
426
+ // Find all "before" screenshots in lines 10-30
427
+ list_local_screenshots({ lineRange: { start: 10, end: 30 }, phase: "before" })
428
+ ```
429
+
430
+ **9. View a screenshot:**
431
+
432
+ ```
433
+ view_local_screenshot({ path: ".testdriver/screenshots/login.test/001-click-before-L42-submit-button.png" })
434
+ ```
435
+
436
+ **When to use screenshot viewing:**
437
+
438
+ - **After test failures** - View screenshots to see exactly what the UI looked like when the test failed
439
+ - **Debugging element finding issues** - See if elements are actually visible or have different appearances than expected
440
+ - **Comparing test runs** - View screenshots from multiple runs to identify flaky behavior
441
+ - **Verifying test logic** - Before running a test, view screenshots from previous runs to understand the UI flow
442
+
443
+ **Debugging workflow example:**
444
+
445
+ ```
446
+ # Test failed at line 42, let's see what happened
447
+ list_local_screenshots({ line: 42 })
448
+
449
+ # View the before/after state at that line
450
+ view_local_screenshot({ path: ".testdriver/screenshots/checkout.test/005-click-before-L42-submit-button.png" })
451
+ view_local_screenshot({ path: ".testdriver/screenshots/checkout.test/006-click-after-L42-submit-button.png" })
452
+
453
+ # Check what the screen looked like before the failing action
454
+ list_local_screenshots({ directory: "checkout.test", phase: "before", limit: 10 })
455
+ ```
456
+
457
+ ### Tips for MCP Workflow
458
+
459
+ 1. **⚠️ Write code IMMEDIATELY** - After EVERY action, append generated code to test file RIGHT AWAY
460
+ 2. **⚠️ Run tests YOURSELF** - Use `vitest run` (NEVER `npx vitest`) - do NOT tell user to run tests
461
+ 3. **⚠️ Add screenshots liberally** - Include `await testdriver.screenshot()` after every significant action for debugging
462
+ 4. **⚠️ Use screenshot viewing for debugging** - When tests fail, use `list_local_screenshots` and `view_local_screenshot` to understand what went wrong
463
+ 5. **Work incrementally** - Don't try to build the entire test at once
464
+ 6. **Use `check` after actions** - Verify your actions succeeded before moving on (for YOUR understanding)
465
+ 7. **Use `assert` for test verifications** - These generate code that goes in the test file
466
+ 8. **Be specific with element descriptions** - "the blue Sign In button in the header" is better than "button"
467
+ 9. **Extend session proactively** - Sessions expire after 5 minutes; use `session_extend` if needed
468
+
469
+ ## Recommended Development Workflow
470
+
471
+ 1. **Write a few steps** - Don't write the entire test at once
472
+ 2. **Run the test** - See what happens on the sandbox
473
+ 3. **Inspect outputs** - Use element properties to debug
474
+ 4. **Assert/expect** - Verify the step worked
475
+ 5. **Iterate** - Add more steps incrementally
476
+
477
+ ```javascript
478
+ // Development workflow example
479
+ // Note: Screenshots are automatically captured before/after each command!
480
+ it("should incrementally build test", async (context) => {
481
+ const testdriver = TestDriver(context);
482
+ await testdriver.provision.chrome({ url: "https://example.com" });
483
+ // Automatic screenshot: 001-provision-after-L3-chrome.png
484
+
485
+ // Step 1: Find and inspect
486
+ const element = await testdriver.find("Some button");
487
+ console.log("Element found:", element.found());
488
+ console.log("Coordinates:", element.x, element.y);
489
+ console.log("Confidence:", element.confidence);
490
+ // Automatic screenshot: 002-find-after-L7-some-button.png
491
+
492
+ // Step 2: Interact
493
+ await element.click();
494
+ // Automatic screenshot: 003-click-after-L13-element.png
495
+
496
+ // Step 3: Assert
497
+ const result = await testdriver.assert("Something happened");
498
+ console.log("Assertion result:", result);
499
+ expect(result).toBeTruthy();
500
+ // Automatic screenshot: 004-assert-after-L17-something-happened.png
501
+
502
+ // Then add more steps...
503
+ });
504
+ ```
505
+
506
+ ## TestDriver Options Reference
507
+
508
+ ```javascript
509
+ const testdriver = TestDriver(context, {
510
+ newSandbox: true, // Create new sandbox (default: true)
511
+ preview: "browser", // "browser" | "ide" | "none" (default: "browser")
512
+ reconnect: false, // Reconnect to last sandbox (default: false)
513
+ keepAlive: 30000, // Keep sandbox alive after test (default: 30000ms / 30 seconds)
514
+ os: "linux", // 'linux' | 'windows' (default: 'linux')
515
+ resolution: "1366x768", // Sandbox resolution
516
+ cache: true, // Enable element caching (default: true)
517
+ cacheKey: "my-test", // Cache key for element finding
518
+ autoScreenshots: true, // Capture screenshots before/after each command (default: true)
519
+ });
520
+ ```
521
+
522
+ ### Preview Modes
523
+
524
+ | Value | Description |
525
+ |-------|-------------|
526
+ | `"browser"` | Opens debugger in default browser (default) |
527
+ | `"ide"` | Opens preview in IDE panel (VSCode, Cursor - requires TestDriver extension) |
528
+ | `"none"` | Headless mode, no visual preview |
529
+
530
+ ## Common Patterns
531
+
532
+ ### Typing in Fields
533
+
534
+ ```javascript
535
+ await testdriver.find("Email input").click();
536
+ await testdriver.type("user@example.com");
537
+ ```
538
+
539
+ ### Keyboard Shortcuts
540
+
541
+ ```javascript
542
+ await testdriver.pressKeys(["ctrl", "a"]); // Select all
543
+ await testdriver.pressKeys(["ctrl", "c"]); // Copy
544
+ await testdriver.pressKeys(["enter"]); // Submit
545
+ ```
546
+
547
+ ### Waiting and Polling
548
+
549
+ ```javascript
550
+ // Use timeout option to poll until element is found (retries every 5 seconds)
551
+ const element = await testdriver.find("Loading complete indicator", {
552
+ timeout: 30000,
553
+ });
554
+ await element.click();
555
+ ```
556
+
557
+ ### Scrolling
558
+
559
+ **⚠️ Important: Ensure proper focus before scrolling**
560
+
561
+ Scrolling requires the page or frame to be focused, not an input field or other interactive element. If an input is focused, scroll commands may not work as expected.
562
+
563
+ ```javascript
564
+ // If you've been typing in an input, click elsewhere first
565
+ await testdriver.find("page background").click();
566
+ // Or press Escape to unfocus
567
+ await testdriver.pressKeys(["escape"]);
568
+
569
+ // Now scroll
570
+ await testdriver.scroll("down");
571
+ await testdriver.scrollUntilText("Footer text");
572
+ await testdriver.scrollUntilImage("Product image at bottom");
573
+
574
+ // If scroll is not working, try using Page Down key directly
575
+ await testdriver.pressKeys(["pagedown"]);
576
+ ```
577
+
578
+ ### Executing Code in Sandbox
579
+
580
+ ```javascript
581
+ // JavaScript
582
+ const result = await testdriver.exec("js", "return document.title", 5000);
583
+
584
+ // Shell (Linux)
585
+ const output = await testdriver.exec("sh", "ls -la", 5000);
586
+
587
+ // PowerShell (Windows)
588
+ const date = await testdriver.exec("pwsh", "Get-Date", 5000);
589
+ ```
590
+
591
+ ### Capturing Screenshots
592
+
593
+ **Screenshots are captured automatically** before and after each SDK command (click, type, find, assert, etc.). Each screenshot filename includes:
594
+ - Sequential number for chronological ordering
595
+ - Action name (e.g., `click`, `find`, `assert`)
596
+ - Phase (`before` or `after`)
597
+ - Line number from your test file
598
+ - Description from the command
599
+
600
+ Example filenames:
601
+ - `001-provision-after-L8-chrome.png`
602
+ - `002-find-before-L12-login-button.png`
603
+ - `003-click-after-L12-element.png`
604
+
605
+ Screenshots are saved to `.testdriver/screenshots/<test-file>/`.
606
+
607
+ To disable automatic screenshots:
608
+ ```javascript
609
+ const testdriver = TestDriver(context, { autoScreenshots: false });
610
+ ```
611
+
612
+ For manual screenshots (e.g., with mouse cursor visible):
613
+ ```javascript
614
+ await testdriver.screenshot(1, false, true);
615
+ ```
616
+
617
+ ## Tips for Agents
618
+
619
+ 1. **⚠️ WRITE CODE IMMEDIATELY** - After EVERY successful MCP action, append the generated code to the test file RIGHT AWAY. Do NOT wait until the session ends.
620
+ 2. **⚠️ RUN TESTS YOURSELF** - Do NOT tell the user to run tests. YOU must run the tests using `vitest run <testFile> --reporter=dot` (NEVER use `npx vitest` - it breaks the reporter). Always use `--reporter=dot` for cleaner output. Analyze the output and iterate until the test passes.
621
+ 3. **⚠️ SHARE THE TEST REPORT URL** - After EVERY test run, find `TESTDRIVER_RUN_URL=https://console.testdriver.ai/runs/...` in the output and share it with the user. This is CRITICAL - users need to view the recording to understand what happened.
622
+ 3. **Screenshots are automatic** - TestDriver captures screenshots before/after every command by default. Each screenshot filename includes the line number (e.g., `001-click-before-L42-submit-button.png`) making it easy to trace issues.
623
+ 4. **⚠️ USE SCREENSHOT VIEWING FOR DEBUGGING** - When tests fail, use `list_local_screenshots` and `view_local_screenshot` MCP commands to see exactly what the UI looked like. The filenames tell you which line of code triggered each screenshot.
624
+ 5. **⚠️ NEVER USE `.wait()`** - Do NOT use any `.wait()` method. Instead, use `find()` with a `timeout` option to poll for elements, or use `assert()` / `check()` to verify state. Explicit waits are flaky and slow.
625
+ 6. **Use MCP tools for development** - Build tests interactively with visual feedback
626
+ 7. **Always check `sdk.d.ts`** for method signatures and types when debugging generated tests
627
+ 8. **Look at test samples** in `node_modules/testdriverai/test` for working examples
628
+ 9. **Use `check` to understand screen state** - This is how you verify what the sandbox shows during MCP development.
629
+ 10. **Use `check` after actions, `assert` for test files** - `check` gives detailed AI analysis (no code), `assert` gives boolean pass/fail (generates code)
630
+ 11. **Be specific with element descriptions** - "blue Sign In button in the header" > "button"
631
+ 12. **Start simple** - get one step working before adding more
632
+ 13. **Always `await` async methods** - TestDriver will warn if you forget, but for TypeScript projects, add `@typescript-eslint/no-floating-promises` to your ESLint config to catch missing `await` at compile time:
633
+
634
+ ```json
635
+ // eslint.config.js (for TypeScript projects)
636
+ {
637
+ "rules": {
638
+ "@typescript-eslint/no-floating-promises": "error"
639
+ }
640
+ }
641
+ ```
@@ -31,7 +31,7 @@ jobs:
31
31
  run: npm ci
32
32
 
33
33
  - name: Run Linux tests
34
- run: npx vitest run examples/*.test.mjs 2>&1 | tee test-output.log
34
+ run: set -o pipefail && npx vitest run examples/*.test.mjs 2>&1 | tee test-output.log
35
35
  env:
36
36
  TD_API_KEY: ${{ secrets.TD_API_KEY }}
37
37
  TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
@@ -51,7 +51,7 @@ jobs:
51
51
  TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
52
52
 
53
53
  - name: Run Windows tests with self-hosted instances
54
- run: npx vitest run ${{ inputs.test_pattern }} 2>&1 | tee test-output.log
54
+ run: set -o pipefail && npx vitest run ${{ inputs.test_pattern }} 2>&1 | tee test-output.log
55
55
  env:
56
56
  TD_API_KEY: ${{ secrets.TD_API_KEY }}
57
57
  TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [7.3.8](https://github.com/testdriverai/testdriverai/compare/v7.3.7...v7.3.8) (2026-02-12)
2
+
3
+
4
+
5
+ ## [7.3.7](https://github.com/testdriverai/testdriverai/compare/v7.3.6...v7.3.7) (2026-02-11)
6
+
7
+
8
+
1
9
  ## [7.3.6](https://github.com/testdriverai/testdriverai/compare/v7.3.5...v7.3.6) (2026-02-10)
2
10
 
3
11
 
@@ -2,104 +2,136 @@
2
2
  "$schema": "./examples-manifest.schema.json",
3
3
  "examples": {
4
4
  "assert.test.mjs": {
5
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d756513c6b23f858ee6",
6
- "lastUpdated": "2026-02-10T18:51:20.322Z"
5
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5fd7140c3fa7daacf27",
6
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
7
7
  },
8
8
  "drag-and-drop.test.mjs": {
9
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7bd411cc20acdd4ac828",
10
- "lastUpdated": "2026-02-10T18:51:20.317Z"
9
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4a77140c3fa7daacde3",
10
+ "lastUpdated": "2026-02-11T01:13:44.450Z"
11
11
  },
12
12
  "exec-pwsh.test.mjs": {
13
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7bd5297b1f49cee2a55f",
14
- "lastUpdated": "2026-02-10T18:51:20.317Z"
13
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4a97140c3fa7daacdea",
14
+ "lastUpdated": "2026-02-11T01:13:44.450Z"
15
15
  },
16
16
  "match-image.test.mjs": {
17
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7bd7297b1f49cee2a560",
18
- "lastUpdated": "2026-02-10T18:51:20.317Z"
17
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4aa7140c3fa7daacdeb",
18
+ "lastUpdated": "2026-02-11T01:13:44.450Z"
19
19
  },
20
20
  "scroll-until-text.test.mjs": {
21
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7c4d4f24dc8fa701f5ba",
22
- "lastUpdated": "2026-02-10T18:51:20.319Z"
21
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f37140c3fa7daace38",
22
+ "lastUpdated": "2026-02-11T01:13:44.451Z"
23
23
  },
24
24
  "hover-text-with-description.test.mjs": {
25
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7c4a11cc20acdd4ac838",
26
- "lastUpdated": "2026-02-10T18:51:20.318Z"
25
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f17140c3fa7daace31",
26
+ "lastUpdated": "2026-02-11T01:13:44.451Z"
27
27
  },
28
28
  "windows-installer.test.mjs": {
29
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7c4f4f24dc8fa701f5bb",
30
- "lastUpdated": "2026-02-10T18:51:20.319Z"
29
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f47140c3fa7daace3b",
30
+ "lastUpdated": "2026-02-11T01:13:44.451Z"
31
31
  },
32
32
  "exec-output.test.mjs": {
33
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7c504f24dc8fa701f5bc",
34
- "lastUpdated": "2026-02-10T18:51:20.319Z"
33
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f67140c3fa7daace3d",
34
+ "lastUpdated": "2026-02-11T01:13:44.451Z"
35
35
  },
36
36
  "chrome-extension.test.mjs": {
37
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7ca711cc20acdd4ac83d",
38
- "lastUpdated": "2026-02-10T18:51:20.319Z"
37
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5507140c3fa7daacea9",
38
+ "lastUpdated": "2026-02-11T01:13:44.452Z"
39
39
  },
40
40
  "launch-vscode-linux.test.mjs": {
41
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7cb811cc20acdd4ac83e",
42
- "lastUpdated": "2026-02-10T18:51:20.319Z"
41
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd54e7140c3fa7daacea2",
42
+ "lastUpdated": "2026-02-11T01:13:44.452Z"
43
43
  },
44
44
  "hover-image.test.mjs": {
45
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7c9211cc20acdd4ac83c",
46
- "lastUpdated": "2026-02-10T18:51:20.319Z"
45
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5387140c3fa7daace8e",
46
+ "lastUpdated": "2026-02-11T01:13:44.452Z"
47
47
  },
48
48
  "installer.test.mjs": {
49
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7ce511cc20acdd4ac844",
50
- "lastUpdated": "2026-02-10T18:51:20.320Z"
49
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd58f7140c3fa7daaced9",
50
+ "lastUpdated": "2026-02-11T01:13:44.454Z"
51
51
  },
52
52
  "type.test.mjs": {
53
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d364f24dc8fa701f5e7",
54
- "lastUpdated": "2026-02-10T18:51:20.321Z"
53
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ae7140c3fa7daaceea",
54
+ "lastUpdated": "2026-02-11T01:13:44.454Z"
55
55
  },
56
56
  "press-keys.test.mjs": {
57
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7cf911cc20acdd4ac84a",
58
- "lastUpdated": "2026-02-10T18:51:20.321Z"
57
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5e87140c3fa7daacf12",
58
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
59
59
  },
60
60
  "scroll-keyboard.test.mjs": {
61
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d92297b1f49cee2a5b2",
62
- "lastUpdated": "2026-02-10T18:51:20.322Z"
61
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5d47140c3fa7daacf06",
62
+ "lastUpdated": "2026-02-11T01:13:44.454Z"
63
63
  },
64
64
  "scroll.test.mjs": {
65
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d39297b1f49cee2a5a7",
66
- "lastUpdated": "2026-02-10T18:51:20.322Z"
65
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd58f7140c3fa7daaced8",
66
+ "lastUpdated": "2026-02-11T01:13:44.454Z"
67
67
  },
68
68
  "scroll-until-image.test.mjs": {
69
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d76297b1f49cee2a5b0",
70
- "lastUpdated": "2026-02-10T18:51:20.322Z"
69
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ea7140c3fa7daacf13",
70
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
71
71
  },
72
72
  "prompt.test.mjs": {
73
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d774f24dc8fa701f5f0",
74
- "lastUpdated": "2026-02-10T18:51:20.322Z"
73
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5eb7140c3fa7daacf14",
74
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
75
75
  },
76
76
  "focus-window.test.mjs": {
77
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7d794f24dc8fa701f5f1",
78
- "lastUpdated": "2026-02-10T18:51:20.322Z"
77
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ec7140c3fa7daacf1b",
78
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
79
79
  },
80
80
  "captcha-api.test.mjs": {
81
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7da36513c6b23f858eee",
82
- "lastUpdated": "2026-02-10T18:51:20.322Z"
81
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5fc7140c3fa7daacf26",
82
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
83
83
  },
84
84
  "element-not-found.test.mjs": {
85
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7dd44f24dc8fa701f5fc",
86
- "lastUpdated": "2026-02-10T18:51:20.323Z"
85
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6297140c3fa7daacf3f",
86
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
87
87
  },
88
88
  "formatted-logging.test.mjs": {
89
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7dd0297b1f49cee2a5b7",
90
- "lastUpdated": "2026-02-10T18:51:20.323Z"
89
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6187140c3fa7daacf35",
90
+ "lastUpdated": "2026-02-11T01:13:44.455Z"
91
91
  },
92
92
  "hover-text.test.mjs": {
93
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7e186513c6b23f858efc",
94
- "lastUpdated": "2026-02-10T18:51:20.324Z"
93
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6477140c3fa7daacf5a",
94
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
95
95
  },
96
96
  "no-provision.test.mjs": {
97
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7e024f24dc8fa701f601",
98
- "lastUpdated": "2026-02-10T18:51:20.323Z"
97
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd64a7140c3fa7daacf63",
98
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
99
99
  },
100
100
  "ai.test.mjs": {
101
- "url": "https://console.testdriver.ai/runs/698b7bd24f24dc8fa701f584/698b7e1e297b1f49cee2a5ba",
102
- "lastUpdated": "2026-02-10T18:51:20.324Z"
101
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6477140c3fa7daacf59",
102
+ "lastUpdated": "2026-02-11T01:13:44.456Z"
103
+ },
104
+ "popup-loading.test.mjs": {
105
+ "url": "https://console.testdriver.ai/runs/698bc89f7140c3fa7daaca8d/698bca7f7140c3fa7daacbf7",
106
+ "lastUpdated": "2026-02-11T00:20:33.687Z"
107
+ },
108
+ "z_flake-diffthreshold-001.test.mjs": {
109
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6d47140c3fa7daacfbc",
110
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
111
+ },
112
+ "z_flake-diffthreshold-01.test.mjs": {
113
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6d17140c3fa7daacfb5",
114
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
115
+ },
116
+ "z_flake-diffthreshold-05.test.mjs": {
117
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd71b7140c3fa7daacfe9",
118
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
119
+ },
120
+ "z_flake-noredraw-cache.test.mjs": {
121
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd71b7140c3fa7daacfe8",
122
+ "lastUpdated": "2026-02-11T01:13:44.457Z"
123
+ },
124
+ "z_flake-redraw-nocache.test.mjs": {
125
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd7be7140c3fa7daad047",
126
+ "lastUpdated": "2026-02-11T01:13:44.459Z"
127
+ },
128
+ "z_flake-redraw-cache.test.mjs": {
129
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd7627140c3fa7daad013",
130
+ "lastUpdated": "2026-02-11T01:13:44.458Z"
131
+ },
132
+ "z_flake-noredraw-nocache.test.mjs": {
133
+ "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd77d7140c3fa7daad027",
134
+ "lastUpdated": "2026-02-11T01:13:44.458Z"
103
135
  }
104
136
  }
105
137
  }
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* ai.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7e1e297b1f49cee2a5ba/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6477140c3fa7daacf59/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* assert.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7d756513c6b23f858ee6/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5fd7140c3fa7daacf27/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* captcha-api.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7da36513c6b23f858eee/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5fc7140c3fa7daacf26/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* chrome-extension.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7ca711cc20acdd4ac83d/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5507140c3fa7daacea9/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* drag-and-drop.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7bd411cc20acdd4ac828/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4a77140c3fa7daacde3/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* element-not-found.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7dd44f24dc8fa701f5fc/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6297140c3fa7daacf3f/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* hover-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7c9211cc20acdd4ac83c/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5387140c3fa7daace8e/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* hover-text.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7e186513c6b23f858efc/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6477140c3fa7daacf5a/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* installer.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7ce511cc20acdd4ac844/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd58f7140c3fa7daaced9/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* launch-vscode-linux.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7cb811cc20acdd4ac83e/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd54e7140c3fa7daacea2/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* match-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7bd7297b1f49cee2a560/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4aa7140c3fa7daacdeb/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* press-keys.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7cf911cc20acdd4ac84a/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5e87140c3fa7daacf12/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-keyboard.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7d92297b1f49cee2a5b2/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5d47140c3fa7daacf06/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-until-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7d76297b1f49cee2a5b0/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5ea7140c3fa7daacf13/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-until-text.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7c4d4f24dc8fa701f5ba/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4f37140c3fa7daace38/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7d39297b1f49cee2a5a7/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd58f7140c3fa7daaced8/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* type.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7d364f24dc8fa701f5e7/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5ae7140c3fa7daaceea/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* windows-installer.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698b7c4f4f24dc8fa701f5bb/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4f47140c3fa7daace3b/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - diffThreshold=0.01, cache=true
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("diffThreshold=0.01, cache=true", {
7
+ redraw: { enabled: true, diffThreshold: 0.01 },
8
+ cache: true,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - diffThreshold=0.1, cache=false
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("diffThreshold=0.1, cache=false", {
7
+ redraw: { enabled: true, diffThreshold: 0.1 },
8
+ cache: false,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - diffThreshold=0.5, cache=true
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("diffThreshold=0.5, cache=true", {
7
+ redraw: { enabled: true, diffThreshold: 0.5 },
8
+ cache: true,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - redraw=false, cache=true
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("redraw=false, cache=true", {
7
+ redraw: false,
8
+ cache: true,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - redraw=false, cache=false
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("redraw=false, cache=false", {
7
+ redraw: false,
8
+ cache: false,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - redraw=true, cache=true
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("redraw=true, cache=true", {
7
+ redraw: true,
8
+ cache: true,
9
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Popup Loading - redraw=true, cache=false
3
+ */
4
+ import { popupLoadingTest } from "./z_flake-shared.mjs";
5
+
6
+ popupLoadingTest("redraw=true, cache=false", {
7
+ redraw: true,
8
+ cache: false,
9
+ });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared test logic for popup-loading variants.
3
+ * Each variant file imports this and calls it with specific options.
4
+ */
5
+ import { describe, expect, it } from "vitest";
6
+ import { TestDriver } from "../lib/vitest/hooks.mjs";
7
+
8
+ export function popupLoadingTest(label, options = {}) {
9
+ describe(`Popup with Loading (${label})`, () => {
10
+ it("should accept cookies and wait for completion", async (context) => {
11
+ const testdriver = TestDriver(context, {
12
+ ip: context.ip || process.env.TD_IP,
13
+ ...options,
14
+ });
15
+
16
+ await testdriver.provision.chrome({
17
+ url: "https://v0-popup-with-loading-bar.vercel.app/",
18
+ });
19
+ await testdriver.screenshot();
20
+
21
+ // Accept the cookie banner to trigger the loading process
22
+ let acceptButton = await testdriver.find("Accept All button on the cookie banner", {timeout: 60000});
23
+
24
+ if (acceptButton.found()) {
25
+ await acceptButton.click();
26
+ } else {
27
+ console.log('no cookie banner found, proceeding without accepting cookies');
28
+ }
29
+
30
+ await testdriver.find('Start button').click();
31
+
32
+ // Wait for "All done!" to appear with 120s timeout
33
+ const allDone = await testdriver.find("All done! text or heading in a modal or popup", { timeout: 120000 });
34
+ await testdriver.screenshot();
35
+
36
+ const result = await testdriver.assert("The text 'All done!' is visible on the page");
37
+ expect(result).toBeTruthy();
38
+
39
+ // Click Continue to proceed to the image grid
40
+ await testdriver.find("Continue button in the modal").click();
41
+
42
+ // Wait for the 5x5 grid of images to fully load (up to 60s) and click the rocket
43
+ await testdriver.find("rocket image in the 5x5 grid", { timeout: 60000, cacheThreshold: -1 }).click();
44
+
45
+ // Assert the success message appears
46
+ const rocketResult = await testdriver.assert("The text 'You found the rocket!' is visible on the page");
47
+ expect(rocketResult).toBeTruthy();
48
+ });
49
+ });
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.3.6",
3
+ "version": "7.3.8",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",
@@ -134,7 +134,7 @@
134
134
  "mocha": "^10.8.2",
135
135
  "node-addon-api": "^8.0.0",
136
136
  "prettier": "3.3.3",
137
- "testdriverai": "^7.2.79",
137
+ "testdriverai": "^7.3.6",
138
138
  "vitest": "^4.0.18"
139
139
  },
140
140
  "optionalDependencies": {
@@ -377,6 +377,12 @@ class SDKLogFormatter {
377
377
  if (meta.cacheHit) {
378
378
  metaParts.push(chalk.bold.yellow("⚡ cached"));
379
379
  }
380
+ if (meta.confidence !== undefined && meta.confidence !== null) {
381
+ metaParts.push(chalk.dim.gray(`confidence: ${meta.confidence}`));
382
+ }
383
+ if (meta.reasoning) {
384
+ metaParts.push(chalk.dim.gray(`reasoning: ${meta.reasoning}`));
385
+ }
380
386
  // Duration always last
381
387
  if (meta.duration) {
382
388
  metaParts.push(this.formatDurationColored(meta.duration, thresholdKey));
package/sdk.js CHANGED
@@ -414,6 +414,7 @@ class Element {
414
414
 
415
415
  result.similarity = this._response.similarity;
416
416
  result.confidence = this._response.confidence;
417
+ result.reasoning = this._response.reasoning;
417
418
  result.selector = this._response.selector;
418
419
 
419
420
  // Include AI response text if available
@@ -716,20 +717,26 @@ class Element {
716
717
  cacheStrategy: response.cacheStrategy || null,
717
718
  similarity: response.similarity ?? null,
718
719
  confidence: response.confidence ?? null,
720
+ reasoning: response.reasoning ?? null,
719
721
  };
720
722
 
721
723
  // Emit element found as log:log event
722
724
  const { events } = require("./agent/events.js");
723
725
  const Dashcam = require("./lib/core/Dashcam");
724
726
  const consoleUrl = Dashcam.getConsoleUrl(this.sdk.config?.TD_API_ROOT);
725
- const formattedMessage = formatter.formatElementFound(this.description, {
727
+ const meta = {
726
728
  x: this.coordinates.x,
727
729
  y: this.coordinates.y,
728
730
  duration: debugInfo.duration,
729
731
  cacheHit: debugInfo.cacheHit,
730
732
  selectorId: this._response?.selector,
731
733
  consoleUrl: consoleUrl,
732
- });
734
+ };
735
+ if (!debugInfo.cacheHit) {
736
+ meta.confidence = debugInfo.confidence;
737
+ meta.reasoning = debugInfo.reasoning;
738
+ }
739
+ const formattedMessage = formatter.formatElementFound(this.description, meta);
733
740
  this.sdk.emitter.emit(events.log.log, formattedMessage);
734
741
 
735
742
  // Log cache information in debug mode
@@ -1116,6 +1123,14 @@ class Element {
1116
1123
  return this._response?.confidence ?? null;
1117
1124
  }
1118
1125
 
1126
+ /**
1127
+ * Get model reasoning for why this element was selected
1128
+ * @returns {string|null}
1129
+ */
1130
+ get reasoning() {
1131
+ return this._response?.reasoning ?? null;
1132
+ }
1133
+
1119
1134
  /**
1120
1135
  * Get element width if available
1121
1136
  * @returns {number|null}