testdriverai 7.2.78 → 7.2.80
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/agent/index.js +42 -1
- package/agent/lib/debugger-server.js +6 -0
- package/ai/agents/testdriver.md +72 -5
- package/interfaces/cli/commands/init.js +47 -479
- package/lib/captcha/solver.js +64 -2
- package/lib/init-project.js +426 -0
- package/mcp-server/dist/server.mjs +118 -3
- package/package.json +3 -2
- package/sdk.js +12 -0
package/agent/index.js
CHANGED
|
@@ -2026,6 +2026,7 @@ ${regression}
|
|
|
2026
2026
|
url: url,
|
|
2027
2027
|
token: "V3b8wG9",
|
|
2028
2028
|
testFile: this.testFile || null,
|
|
2029
|
+
os: this.sandboxOs || "linux",
|
|
2029
2030
|
};
|
|
2030
2031
|
|
|
2031
2032
|
// Base64 encode the data (the debugger expects base64, not URL encoding)
|
|
@@ -2034,7 +2035,47 @@ ${regression}
|
|
|
2034
2035
|
// Use the debugger URL instead of the VNC URL
|
|
2035
2036
|
const urlToOpen = `${this.debuggerUrl}?data=${encodedData}`;
|
|
2036
2037
|
|
|
2037
|
-
|
|
2038
|
+
// Check preview mode from config
|
|
2039
|
+
const previewMode = this.config.TD_PREVIEW || "browser";
|
|
2040
|
+
|
|
2041
|
+
if (previewMode === "ide") {
|
|
2042
|
+
// Write session file for VSCode extension to pick up
|
|
2043
|
+
this.writeIdeSessionFile(urlToOpen, data);
|
|
2044
|
+
} else if (previewMode !== "none") {
|
|
2045
|
+
// Open in browser (default behavior)
|
|
2046
|
+
this.emitter.emit(events.showWindow, urlToOpen);
|
|
2047
|
+
}
|
|
2048
|
+
// If preview is "none", don't open anything
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
// Write session file for IDE preview mode
|
|
2053
|
+
writeIdeSessionFile(debuggerUrl, data) {
|
|
2054
|
+
const fs = require("fs");
|
|
2055
|
+
const os = require("os");
|
|
2056
|
+
const path = require("path");
|
|
2057
|
+
|
|
2058
|
+
const sessionDir = path.join(os.homedir(), ".testdriver");
|
|
2059
|
+
const sessionFile = path.join(sessionDir, "ide-session.json");
|
|
2060
|
+
|
|
2061
|
+
try {
|
|
2062
|
+
// Ensure directory exists
|
|
2063
|
+
if (!fs.existsSync(sessionDir)) {
|
|
2064
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
const sessionData = {
|
|
2068
|
+
debuggerUrl: debuggerUrl,
|
|
2069
|
+
resolution: data.resolution || this.config.TD_RESOLUTION,
|
|
2070
|
+
testFile: data.testFile || this.thisFile,
|
|
2071
|
+
os: data.os || this.sandboxOs || "linux",
|
|
2072
|
+
timestamp: Date.now(),
|
|
2073
|
+
};
|
|
2074
|
+
|
|
2075
|
+
fs.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2));
|
|
2076
|
+
logger.log(`IDE session file written: ${sessionFile}`);
|
|
2077
|
+
} catch (error) {
|
|
2078
|
+
logger.warn(`Failed to write IDE session file: ${error.message}`);
|
|
2038
2079
|
}
|
|
2039
2080
|
}
|
|
2040
2081
|
|
|
@@ -113,6 +113,10 @@ async function startDebugger(config = {}, emitter) {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// Store the debugger URL and config for later use
|
|
117
|
+
module.exports.debuggerUrl = url;
|
|
118
|
+
module.exports.config = config;
|
|
119
|
+
|
|
116
120
|
return { port, url };
|
|
117
121
|
} catch (error) {
|
|
118
122
|
console.error("Failed to start debugger server:", error);
|
|
@@ -140,4 +144,6 @@ module.exports = {
|
|
|
140
144
|
stopDebugger,
|
|
141
145
|
broadcastEvent,
|
|
142
146
|
createDebuggerServer,
|
|
147
|
+
debuggerUrl: null,
|
|
148
|
+
config: null,
|
|
143
149
|
};
|
package/ai/agents/testdriver.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: testdriver
|
|
3
3
|
description: An expert at creating and refining automated tests using TestDriver.ai
|
|
4
|
-
tools: [
|
|
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
5
|
mcp-servers:
|
|
6
6
|
testdriver:
|
|
7
7
|
command: npx
|
|
@@ -48,6 +48,41 @@ Use this agent when the user asks to:
|
|
|
48
48
|
|
|
49
49
|
## Prerequisites
|
|
50
50
|
|
|
51
|
+
### Quick Start - Creating Your First TestDriver Test
|
|
52
|
+
|
|
53
|
+
**For new projects, use the `init` command to automatically set up everything:**
|
|
54
|
+
|
|
55
|
+
**CLI:**
|
|
56
|
+
```bash
|
|
57
|
+
npx testdriverai@beta init
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**MCP (via this agent):**
|
|
61
|
+
```
|
|
62
|
+
// apiKey is optional - if not provided, user adds it to .env manually after init
|
|
63
|
+
init({ directory: "." })
|
|
64
|
+
|
|
65
|
+
// Or with API key if available (though MCP typically won't have access to it)
|
|
66
|
+
init({ directory: ".", apiKey: "your_api_key" })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**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.
|
|
70
|
+
|
|
71
|
+
The `init` command creates:
|
|
72
|
+
- ✅ `package.json` with proper dependencies
|
|
73
|
+
- ✅ Example test files (`tests/example.test.js`, `tests/login.js`)
|
|
74
|
+
- ✅ `vitest.config.js` with correct timeouts
|
|
75
|
+
- ✅ `.gitignore` with `.env`
|
|
76
|
+
- ✅ GitHub Actions workflow (`.github/workflows/testdriver.yml`)
|
|
77
|
+
- ✅ VSCode MCP config (`.vscode/mcp.json`)
|
|
78
|
+
- ✅ TestDriver skills and agents in `.github/`
|
|
79
|
+
- ✅ `.env` file (user adds API key manually if not provided to init)
|
|
80
|
+
|
|
81
|
+
**After running init:**
|
|
82
|
+
1. User adds their API key to `.env`: `TD_API_KEY=...`
|
|
83
|
+
2. Test the setup: `npx vitest run`
|
|
84
|
+
3. Start building custom tests using the examples as templates
|
|
85
|
+
|
|
51
86
|
### API Key Setup
|
|
52
87
|
|
|
53
88
|
The user **must** have a TestDriver API key set in their environment:
|
|
@@ -59,14 +94,12 @@ TD_API_KEY=your_api_key_here
|
|
|
59
94
|
|
|
60
95
|
Get your API key at: **https://console.testdriver.ai/team**
|
|
61
96
|
|
|
62
|
-
### Installation
|
|
97
|
+
### Manual Installation
|
|
63
98
|
|
|
64
|
-
|
|
99
|
+
If not using `init`, always use the **beta** tag when installing TestDriver:
|
|
65
100
|
|
|
66
101
|
```bash
|
|
67
102
|
npm install --save-dev testdriverai@beta
|
|
68
|
-
# or
|
|
69
|
-
npx testdriverai@beta init
|
|
70
103
|
```
|
|
71
104
|
|
|
72
105
|
### Test Runner
|
|
@@ -119,6 +152,7 @@ describe("My Test Suite", () => {
|
|
|
119
152
|
const button = await testdriver.find("Sign In button");
|
|
120
153
|
await testdriver.screenshot(); // Capture before click
|
|
121
154
|
await button.click();
|
|
155
|
+
await testdriver.wait(2000); // Wait for state change
|
|
122
156
|
await testdriver.screenshot(); // Capture after click
|
|
123
157
|
|
|
124
158
|
// Assert using natural language
|
|
@@ -207,6 +241,23 @@ await testdriver.screenshot(1, false, true);
|
|
|
207
241
|
- After any action that changes the page state
|
|
208
242
|
- When debugging a flaky or failing test
|
|
209
243
|
|
|
244
|
+
**⚠️ Important: Add delays before screenshots after actions**
|
|
245
|
+
|
|
246
|
+
When you click or interact with an element that triggers a state change (page navigation, modal opening, content loading), **add a short delay before taking a screenshot** to allow the application state to update:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
await element.click();
|
|
250
|
+
await testdriver.wait(2000); // Wait 2-3 seconds for state change
|
|
251
|
+
await testdriver.screenshot(); // Now capture the updated state
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This is especially important for:
|
|
255
|
+
- Navigation clicks (page transitions)
|
|
256
|
+
- Button clicks that open modals or dialogs
|
|
257
|
+
- Form submissions
|
|
258
|
+
- Actions that trigger AJAX requests or animations
|
|
259
|
+
- Any interaction where visual feedback takes time to appear
|
|
260
|
+
|
|
210
261
|
**Screenshot file organization:**
|
|
211
262
|
|
|
212
263
|
```
|
|
@@ -258,6 +309,7 @@ find_and_click({ description: "email input field" })
|
|
|
258
309
|
→ Returns: screenshot with element highlighted
|
|
259
310
|
→ ⚠️ IMMEDIATELY append to test file:
|
|
260
311
|
await testdriver.find("email input field").click();
|
|
312
|
+
await testdriver.wait(2000); // Wait for state change
|
|
261
313
|
await testdriver.screenshot(); // Capture after click
|
|
262
314
|
|
|
263
315
|
type({ text: "user@example.com" })
|
|
@@ -399,6 +451,7 @@ it("should incrementally build test", async (context) => {
|
|
|
399
451
|
|
|
400
452
|
// Step 2: Interact
|
|
401
453
|
await element.click();
|
|
454
|
+
await testdriver.wait(2000); // Wait for state change
|
|
402
455
|
await testdriver.screenshot(); // Capture after click
|
|
403
456
|
|
|
404
457
|
// Step 3: Assert and log
|
|
@@ -463,10 +516,23 @@ await element.click();
|
|
|
463
516
|
|
|
464
517
|
### Scrolling
|
|
465
518
|
|
|
519
|
+
**⚠️ Important: Ensure proper focus before scrolling**
|
|
520
|
+
|
|
521
|
+
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.
|
|
522
|
+
|
|
466
523
|
```javascript
|
|
524
|
+
// If you've been typing in an input, click elsewhere first
|
|
525
|
+
await testdriver.find("page background").click();
|
|
526
|
+
// Or press Escape to unfocus
|
|
527
|
+
await testdriver.pressKeys(["escape"]);
|
|
528
|
+
|
|
529
|
+
// Now scroll
|
|
467
530
|
await testdriver.scroll("down");
|
|
468
531
|
await testdriver.scrollUntilText("Footer text");
|
|
469
532
|
await testdriver.scrollUntilImage("Product image at bottom");
|
|
533
|
+
|
|
534
|
+
// If scroll is not working, try using Page Down key directly
|
|
535
|
+
await testdriver.pressKeys(["pagedown"]);
|
|
470
536
|
```
|
|
471
537
|
|
|
472
538
|
### Executing Code in Sandbox
|
|
@@ -498,6 +564,7 @@ await testdriver.provision.chrome({ url: "https://example.com" });
|
|
|
498
564
|
await testdriver.screenshot(); // After page load
|
|
499
565
|
|
|
500
566
|
await testdriver.find("Login button").click();
|
|
567
|
+
await testdriver.wait(2000); // Wait for state change
|
|
501
568
|
await testdriver.screenshot(); // After click
|
|
502
569
|
|
|
503
570
|
await testdriver.type("user@example.com");
|