testdriverai 7.9.21-test → 7.9.23-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.
- package/agent/lib/sandbox.js +9 -11
- package/ai/skills/testdriver-dashcam/SKILL.md +34 -1
- package/docs/v7/dashcam.mdx +34 -1
- package/examples/web-logs.test.mjs +39 -0
- package/lib/core/index.d.ts +13 -1
- package/lib/init-project.js +0 -10
- package/package.json +1 -1
package/agent/lib/sandbox.js
CHANGED
|
@@ -372,7 +372,9 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
372
372
|
step: 'discontinuity',
|
|
373
373
|
message: 'Recovering missed messages after connection interruption...',
|
|
374
374
|
});
|
|
375
|
-
self._recoverFromDiscontinuity()
|
|
375
|
+
self._recoverFromDiscontinuity().catch(function (err) {
|
|
376
|
+
logger.debug('[realtime] Discontinuity recovery error (suppressed): ' + (err.message || err));
|
|
377
|
+
});
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
// When the channel is detached or failed (e.g. sandbox terminated — 404/90001),
|
|
@@ -881,11 +883,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
881
883
|
// Also check channel history in case runner.ready was published
|
|
882
884
|
// before we subscribed (race condition on fast-booting agents).
|
|
883
885
|
try {
|
|
884
|
-
self._sessionChannel.history({ limit: 50 }
|
|
885
|
-
if (err) {
|
|
886
|
-
logger.warn('History lookup failed (non-fatal): ' + (err.message || err));
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
886
|
+
self._sessionChannel.history({ limit: 50 }).then(function (page) {
|
|
889
887
|
if (page && page.items) {
|
|
890
888
|
for (var i = 0; i < page.items.length; i++) {
|
|
891
889
|
var item = page.items[i];
|
|
@@ -896,6 +894,8 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
896
894
|
}
|
|
897
895
|
}
|
|
898
896
|
}
|
|
897
|
+
}).catch(function (err) {
|
|
898
|
+
logger.warn('History lookup failed (non-fatal): ' + (err.message || err));
|
|
899
899
|
});
|
|
900
900
|
} catch (histErr) {
|
|
901
901
|
logger.warn('History call threw (non-fatal): ' + (histErr.message || histErr));
|
|
@@ -1033,11 +1033,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
1033
1033
|
// Also check channel history in case runner.ready was published
|
|
1034
1034
|
// before we subscribed (race condition on fast-booting agents).
|
|
1035
1035
|
try {
|
|
1036
|
-
self._sessionChannel.history({ limit: 50 }
|
|
1037
|
-
if (err) {
|
|
1038
|
-
logger.warn('History lookup failed (non-fatal): ' + (err.message || err));
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1036
|
+
self._sessionChannel.history({ limit: 50 }).then(function (page) {
|
|
1041
1037
|
if (page && page.items) {
|
|
1042
1038
|
for (var i = 0; i < page.items.length; i++) {
|
|
1043
1039
|
var item = page.items[i];
|
|
@@ -1048,6 +1044,8 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
1048
1044
|
}
|
|
1049
1045
|
}
|
|
1050
1046
|
}
|
|
1047
|
+
}).catch(function (err) {
|
|
1048
|
+
logger.warn('History lookup failed (non-fatal): ' + (err.message || err));
|
|
1051
1049
|
});
|
|
1052
1050
|
} catch (histErr) {
|
|
1053
1051
|
logger.warn('History call threw (non-fatal): ' + (histErr.message || histErr));
|
|
@@ -192,6 +192,29 @@ await dashcam.addApplicationLog(application, name)
|
|
|
192
192
|
await dashcam.addApplicationLog('Google Chrome', 'Browser Logs');
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
+
### addWebLog()
|
|
196
|
+
|
|
197
|
+
Track web request logs by URL pattern:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
await dashcam.addWebLog(pattern, name)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
<ParamField path="pattern" type="string" required>
|
|
204
|
+
URL pattern to match (e.g., `"*example.com*"`)
|
|
205
|
+
</ParamField>
|
|
206
|
+
|
|
207
|
+
<ParamField path="name" type="string" required>
|
|
208
|
+
Display name for the log
|
|
209
|
+
</ParamField>
|
|
210
|
+
|
|
211
|
+
**Returns:** `Promise<void>`
|
|
212
|
+
|
|
213
|
+
**Example:**
|
|
214
|
+
```javascript
|
|
215
|
+
await dashcam.addWebLog('*example.com*', 'Web Logs');
|
|
216
|
+
```
|
|
217
|
+
|
|
195
218
|
### addLog()
|
|
196
219
|
|
|
197
220
|
Generic method to add any type of log:
|
|
@@ -209,7 +232,7 @@ await dashcam.addLog(config)
|
|
|
209
232
|
</ParamField>
|
|
210
233
|
|
|
211
234
|
<ParamField path="type" type="string" required>
|
|
212
|
-
Log type: `'file'`, `'
|
|
235
|
+
Log type: `'file'`, `'application'`, or `'web'`
|
|
213
236
|
</ParamField>
|
|
214
237
|
|
|
215
238
|
<ParamField path="path" type="string">
|
|
@@ -219,6 +242,10 @@ await dashcam.addLog(config)
|
|
|
219
242
|
<ParamField path="application" type="string">
|
|
220
243
|
Application name (required for type='application')
|
|
221
244
|
</ParamField>
|
|
245
|
+
|
|
246
|
+
<ParamField path="pattern" type="string">
|
|
247
|
+
URL pattern to match (required for type='web', e.g., `"*example.com*"`)
|
|
248
|
+
</ParamField>
|
|
222
249
|
</Expandable>
|
|
223
250
|
</ParamField>
|
|
224
251
|
|
|
@@ -237,6 +264,12 @@ await dashcam.addLog({
|
|
|
237
264
|
type: 'application',
|
|
238
265
|
application: 'Google Chrome'
|
|
239
266
|
});
|
|
267
|
+
|
|
268
|
+
await dashcam.addLog({
|
|
269
|
+
name: 'Web Logs',
|
|
270
|
+
type: 'web',
|
|
271
|
+
pattern: '*example.com*'
|
|
272
|
+
});
|
|
240
273
|
```
|
|
241
274
|
|
|
242
275
|
### isRecording()
|
package/docs/v7/dashcam.mdx
CHANGED
|
@@ -193,6 +193,29 @@ await dashcam.addApplicationLog(application, name)
|
|
|
193
193
|
await dashcam.addApplicationLog('Google Chrome', 'Browser Logs');
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
+
### addWebLog()
|
|
197
|
+
|
|
198
|
+
Track web request logs by URL pattern:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
await dashcam.addWebLog(pattern, name)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
<ParamField path="pattern" type="string" required>
|
|
205
|
+
URL pattern to match (e.g., `"*example.com*"`)
|
|
206
|
+
</ParamField>
|
|
207
|
+
|
|
208
|
+
<ParamField path="name" type="string" required>
|
|
209
|
+
Display name for the log
|
|
210
|
+
</ParamField>
|
|
211
|
+
|
|
212
|
+
**Returns:** `Promise<void>`
|
|
213
|
+
|
|
214
|
+
**Example:**
|
|
215
|
+
```javascript
|
|
216
|
+
await dashcam.addWebLog('*example.com*', 'Web Logs');
|
|
217
|
+
```
|
|
218
|
+
|
|
196
219
|
### addLog()
|
|
197
220
|
|
|
198
221
|
Generic method to add any type of log:
|
|
@@ -210,7 +233,7 @@ await dashcam.addLog(config)
|
|
|
210
233
|
</ParamField>
|
|
211
234
|
|
|
212
235
|
<ParamField path="type" type="string" required>
|
|
213
|
-
Log type: `'file'`, `'
|
|
236
|
+
Log type: `'file'`, `'application'`, or `'web'`
|
|
214
237
|
</ParamField>
|
|
215
238
|
|
|
216
239
|
<ParamField path="path" type="string">
|
|
@@ -220,6 +243,10 @@ await dashcam.addLog(config)
|
|
|
220
243
|
<ParamField path="application" type="string">
|
|
221
244
|
Application name (required for type='application')
|
|
222
245
|
</ParamField>
|
|
246
|
+
|
|
247
|
+
<ParamField path="pattern" type="string">
|
|
248
|
+
URL pattern to match (required for type='web', e.g., `"*example.com*"`)
|
|
249
|
+
</ParamField>
|
|
223
250
|
</Expandable>
|
|
224
251
|
</ParamField>
|
|
225
252
|
|
|
@@ -238,6 +265,12 @@ await dashcam.addLog({
|
|
|
238
265
|
type: 'application',
|
|
239
266
|
application: 'Google Chrome'
|
|
240
267
|
});
|
|
268
|
+
|
|
269
|
+
await dashcam.addLog({
|
|
270
|
+
name: 'Web Logs',
|
|
271
|
+
type: 'web',
|
|
272
|
+
pattern: '*example.com*'
|
|
273
|
+
});
|
|
241
274
|
```
|
|
242
275
|
|
|
243
276
|
### isRecording()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver SDK - Web Logs Test (Vitest)
|
|
3
|
+
* Opens Notion, adds web logs via dashcam, then navigates to several URLs.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { TestDriver } from "../lib/vitest/hooks.mjs";
|
|
8
|
+
import { getDefaults } from "./config.mjs";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Navigate Chrome to a new URL using Ctrl+L
|
|
12
|
+
* @param {import('../../sdk.js').default} client - TestDriver client instance
|
|
13
|
+
* @param {string} url - URL to navigate to
|
|
14
|
+
*/
|
|
15
|
+
async function navigateTo(client, url) {
|
|
16
|
+
await client.pressKeys(["ctrl", "l"]);
|
|
17
|
+
await client.type(url);
|
|
18
|
+
await client.pressKeys(["enter"]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("Web Logs Test", () => {
|
|
22
|
+
it("should open Notion, add web logs, and navigate to multiple URLs", async (context) => {
|
|
23
|
+
const testdriver = TestDriver(context, { ...getDefaults(context) });
|
|
24
|
+
|
|
25
|
+
// Add web logs for all domains
|
|
26
|
+
await testdriver.dashcam.addWebLog("http*://*github.com*");
|
|
27
|
+
|
|
28
|
+
// Launch Chrome to notion.so
|
|
29
|
+
await testdriver.provision.chrome({
|
|
30
|
+
url: "https://www.notion.so",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Navigate to GitHub
|
|
34
|
+
await navigateTo(testdriver, "https://github.com");
|
|
35
|
+
const githubLoaded = await testdriver.assert("the GitHub homepage is visible");
|
|
36
|
+
expect(githubLoaded).toBeTruthy();
|
|
37
|
+
|
|
38
|
+
});
|
|
39
|
+
});
|
package/lib/core/index.d.ts
CHANGED
|
@@ -38,6 +38,13 @@ export class Dashcam {
|
|
|
38
38
|
*/
|
|
39
39
|
addApplicationLog(application: string, name: string): Promise<void>;
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Add a web log to Dashcam
|
|
43
|
+
* @param pattern - URL pattern to match (e.g., "*example.com*")
|
|
44
|
+
* @param name - Name/description for the log entry
|
|
45
|
+
*/
|
|
46
|
+
addWebLog(pattern: string, name: string): Promise<void>;
|
|
47
|
+
|
|
41
48
|
/**
|
|
42
49
|
* Start recording
|
|
43
50
|
* @returns Promise that resolves when recording starts
|
|
@@ -68,7 +75,7 @@ export interface LogConfig {
|
|
|
68
75
|
/**
|
|
69
76
|
* Type of log entry
|
|
70
77
|
*/
|
|
71
|
-
type: 'file' | 'application';
|
|
78
|
+
type: 'file' | 'application' | 'web';
|
|
72
79
|
|
|
73
80
|
/**
|
|
74
81
|
* Path to file (for file logs)
|
|
@@ -80,6 +87,11 @@ export interface LogConfig {
|
|
|
80
87
|
*/
|
|
81
88
|
application?: string;
|
|
82
89
|
|
|
90
|
+
/**
|
|
91
|
+
* URL pattern (for web logs, e.g., "*example.com*")
|
|
92
|
+
*/
|
|
93
|
+
pattern?: string;
|
|
94
|
+
|
|
83
95
|
/**
|
|
84
96
|
* Name/description for the log entry
|
|
85
97
|
*/
|
package/lib/init-project.js
CHANGED
|
@@ -447,16 +447,6 @@ jobs:
|
|
|
447
447
|
if (copiedCount > 0) {
|
|
448
448
|
progress(`✓ Copied ${copiedCount} agent(s) to .github/agents/`);
|
|
449
449
|
}
|
|
450
|
-
|
|
451
|
-
// Also set testdriver.md as copilot-instructions.md if it doesn't already exist
|
|
452
|
-
const copilotInstructionsPath = path.join(targetDir, ".github", "copilot-instructions.md");
|
|
453
|
-
const testdriverAgentSource = path.join(agentsSourceDir, "testdriver.md");
|
|
454
|
-
if (!fs.existsSync(copilotInstructionsPath) && fs.existsSync(testdriverAgentSource)) {
|
|
455
|
-
fs.copyFileSync(testdriverAgentSource, copilotInstructionsPath);
|
|
456
|
-
progress("✓ Created .github/copilot-instructions.md");
|
|
457
|
-
} else if (fs.existsSync(copilotInstructionsPath)) {
|
|
458
|
-
progress("⊘ copilot-instructions.md already exists, skipping");
|
|
459
|
-
}
|
|
460
450
|
} else {
|
|
461
451
|
progress("⚠ Agents directory not found (will be available after npm install)");
|
|
462
452
|
}
|