testdriverai 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +550 -0
- package/CODEOWNERS +0 -1
- package/README.md +126 -0
- package/agent/index.js +43 -18
- package/agent/lib/commands.js +794 -135
- package/agent/lib/redraw.js +124 -39
- package/agent/lib/sandbox.js +10 -1
- package/agent/lib/sdk.js +21 -0
- package/docs/MIGRATION.md +425 -0
- package/docs/PRESETS.md +210 -0
- package/docs/docs.json +91 -37
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/quickstart.mdx +273 -140
- package/docs/v7/guides/best-practices.mdx +486 -0
- package/docs/v7/guides/caching-ai.mdx +215 -0
- package/docs/v7/guides/caching-selectors.mdx +292 -0
- package/docs/v7/guides/caching.mdx +366 -0
- package/docs/v7/guides/ci-cd/azure.mdx +587 -0
- package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
- package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
- package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
- package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
- package/docs/v7/guides/ci-cd/travis.mdx +438 -0
- package/docs/v7/guides/debugging.mdx +349 -0
- package/docs/v7/guides/faq.mdx +393 -0
- package/docs/v7/guides/performance.mdx +517 -0
- package/docs/v7/guides/troubleshooting.mdx +526 -0
- package/docs/v7/guides/vitest-plugin.mdx +477 -0
- package/docs/v7/guides/vitest.mdx +535 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +287 -0
- package/docs/v7/presets/electron.mdx +435 -0
- package/docs/v7/presets/vscode.mdx +398 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/docs/v7/progressive-apis/CORE.md +459 -0
- package/docs/v7/progressive-apis/HOOKS.md +360 -0
- package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
- package/docs/v7/progressive-apis/PROVISION.md +266 -0
- package/interfaces/vitest-plugin.mjs +186 -100
- package/package.json +12 -1
- package/sdk.d.ts +335 -42
- package/sdk.js +756 -95
- package/src/core/Dashcam.js +469 -0
- package/src/core/index.d.ts +150 -0
- package/src/core/index.js +12 -0
- package/src/presets/index.mjs +331 -0
- package/src/vitest/extended.mjs +108 -0
- package/src/vitest/hooks.d.ts +119 -0
- package/src/vitest/hooks.mjs +298 -0
- package/src/vitest/index.mjs +64 -0
- package/src/vitest/lifecycle.mjs +277 -0
- package/src/vitest/utils.mjs +150 -0
- package/test/dashcam.test.js +137 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +13 -31
- package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
- package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +7 -19
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +6 -19
- package/testdriver/acceptance-sdk/exec-js.test.mjs +6 -18
- package/testdriver/acceptance-sdk/exec-output.test.mjs +8 -20
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +13 -25
- package/testdriver/acceptance-sdk/focus-window.test.mjs +8 -20
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +5 -20
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +10 -19
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +7 -19
- package/testdriver/acceptance-sdk/hover-text.test.mjs +5 -19
- package/testdriver/acceptance-sdk/match-image.test.mjs +7 -19
- package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +5 -19
- package/testdriver/acceptance-sdk/prompt.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +6 -20
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +9 -23
- package/testdriver/acceptance-sdk/scroll.test.mjs +12 -21
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +124 -352
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
- package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
- package/testdriver/acceptance-sdk/type.test.mjs +19 -58
- package/vitest.config.mjs +1 -0
- package/.vscode/mcp.json +0 -9
- package/MIGRATION.md +0 -389
- package/PLUGIN_MIGRATION.md +0 -222
- package/PROMPT_CACHE.md +0 -200
- package/SDK_LOGGING.md +0 -222
- package/SDK_MIGRATION.md +0 -474
- package/SDK_README.md +0 -1122
- package/debug-screenshot-1763401388589.png +0 -0
- package/examples/run-tests-with-recording.sh +0 -70
- package/examples/screenshot-example.js +0 -63
- package/examples/sdk-awesome-logs-demo.js +0 -177
- package/examples/sdk-cache-thresholds.js +0 -96
- package/examples/sdk-element-properties.js +0 -155
- package/examples/sdk-simple-example.js +0 -65
- package/examples/test-recording-example.test.js +0 -166
- package/mcp-server/AI_GUIDELINES.md +0 -57
- package/test-find-api.js +0 -73
- package/test-prompt-cache.js +0 -96
- package/test-sandbox-render.js +0 -28
- package/test-sdk-methods.js +0 -15
- package/test-sdk-refactor.js +0 -53
- package/test-stack-trace.mjs +0 -57
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
package/agent/lib/redraw.js
CHANGED
|
@@ -3,13 +3,28 @@ const fs = require("fs");
|
|
|
3
3
|
const { events } = require("../events");
|
|
4
4
|
const theme = require("./theme");
|
|
5
5
|
|
|
6
|
+
// Default redraw options
|
|
7
|
+
const DEFAULT_REDRAW_OPTIONS = {
|
|
8
|
+
enabled: true, // Master switch to enable/disable redraw detection
|
|
9
|
+
screenRedraw: true, // Enable screen redraw detection
|
|
10
|
+
networkMonitor: true, // Enable network activity monitoring
|
|
11
|
+
diffThreshold: 0.1, // Percentage threshold for screen diff (0.1 = 0.1%)
|
|
12
|
+
};
|
|
13
|
+
|
|
6
14
|
// Factory function that creates redraw functionality with the provided system instance
|
|
7
15
|
const createRedraw = (
|
|
8
16
|
emitter,
|
|
9
17
|
system,
|
|
10
18
|
sandbox,
|
|
11
|
-
|
|
19
|
+
defaultOptions = {},
|
|
12
20
|
) => {
|
|
21
|
+
// Merge default options with provided defaults
|
|
22
|
+
const baseOptions = { ...DEFAULT_REDRAW_OPTIONS, ...defaultOptions };
|
|
23
|
+
// Support legacy redrawThresholdPercent number argument
|
|
24
|
+
if (typeof defaultOptions === 'number') {
|
|
25
|
+
baseOptions.diffThreshold = defaultOptions;
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
const networkUpdateInterval = 15000;
|
|
14
29
|
|
|
15
30
|
let lastTxBytes = null;
|
|
@@ -116,16 +131,13 @@ const createRedraw = (
|
|
|
116
131
|
{ threshold: 0.1 },
|
|
117
132
|
);
|
|
118
133
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const diffPercentage = (differentPixels / totalPixels) * 100;
|
|
124
|
-
return diffPercentage.toFixed(1);
|
|
125
|
-
}
|
|
134
|
+
// Calculate percentage difference based on pixel differences
|
|
135
|
+
// Always return a number (0 if no difference)
|
|
136
|
+
const diffPercentage = (differentPixels / totalPixels) * 100;
|
|
137
|
+
return parseFloat(diffPercentage.toFixed(2));
|
|
126
138
|
} catch (error) {
|
|
127
139
|
console.error("Error comparing images:", error);
|
|
128
|
-
return false
|
|
140
|
+
return 0; // Return 0 on error instead of false
|
|
129
141
|
}
|
|
130
142
|
}
|
|
131
143
|
|
|
@@ -146,46 +158,104 @@ const createRedraw = (
|
|
|
146
158
|
}
|
|
147
159
|
}
|
|
148
160
|
|
|
149
|
-
|
|
161
|
+
// Current options for the active redraw cycle
|
|
162
|
+
let currentOptions = { ...baseOptions };
|
|
163
|
+
|
|
164
|
+
async function start(options = {}) {
|
|
165
|
+
// Merge base options with per-call options
|
|
166
|
+
currentOptions = { ...baseOptions, ...options };
|
|
167
|
+
|
|
168
|
+
console.log('[redraw] start() called with options:', JSON.stringify(currentOptions));
|
|
169
|
+
|
|
170
|
+
// If redraw is completely disabled, return early
|
|
171
|
+
if (!currentOptions.enabled) {
|
|
172
|
+
console.log('[redraw] start() - redraw disabled, returning null');
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// If both screenRedraw and networkMonitor are disabled, disable redraw
|
|
177
|
+
if (!currentOptions.screenRedraw && !currentOptions.networkMonitor) {
|
|
178
|
+
currentOptions.enabled = false;
|
|
179
|
+
console.log('[redraw] start() - both screenRedraw and networkMonitor disabled, returning null');
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
150
183
|
resetState();
|
|
151
|
-
|
|
152
|
-
|
|
184
|
+
|
|
185
|
+
// Only start network monitoring if enabled
|
|
186
|
+
if (currentOptions.networkMonitor) {
|
|
187
|
+
startNetworkMonitoring();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Only capture start image if screen redraw is enabled
|
|
191
|
+
if (currentOptions.screenRedraw) {
|
|
192
|
+
startImage = await system.captureScreenPNG(0.25, true);
|
|
193
|
+
console.log('[redraw] start() - captured startImage:', startImage);
|
|
194
|
+
}
|
|
195
|
+
|
|
153
196
|
return startImage;
|
|
154
197
|
}
|
|
155
198
|
|
|
156
|
-
async function checkCondition(resolve, startTime, timeoutMs) {
|
|
157
|
-
|
|
199
|
+
async function checkCondition(resolve, startTime, timeoutMs, options) {
|
|
200
|
+
const { enabled, screenRedraw, networkMonitor, diffThreshold } = options;
|
|
201
|
+
|
|
202
|
+
// If redraw is disabled, resolve immediately
|
|
203
|
+
if (!enabled) {
|
|
204
|
+
resolve("true");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let nowImage = screenRedraw ? await system.captureScreenPNG(0.25, true) : null;
|
|
158
209
|
let timeElapsed = Date.now() - startTime;
|
|
159
210
|
let diffPercent = 0;
|
|
160
211
|
let isTimeout = timeElapsed > timeoutMs;
|
|
161
212
|
|
|
162
|
-
if
|
|
213
|
+
// Check screen redraw if enabled and we have a start image to compare against
|
|
214
|
+
if (screenRedraw && !screenHasRedrawn && startImage && nowImage) {
|
|
215
|
+
console.log('[redraw] checkCondition() - comparing images:', { startImage, nowImage });
|
|
163
216
|
diffPercent = await imageDiffPercent(startImage, nowImage);
|
|
164
|
-
|
|
217
|
+
console.log('[redraw] checkCondition() - diffPercent:', diffPercent, 'threshold:', diffThreshold);
|
|
218
|
+
screenHasRedrawn = diffPercent > diffThreshold;
|
|
219
|
+
console.log('[redraw] checkCondition() - screenHasRedrawn:', screenHasRedrawn);
|
|
220
|
+
} else if (screenRedraw && !startImage) {
|
|
221
|
+
// If no start image was captured, capture one now and wait for next check
|
|
222
|
+
console.log('[redraw] checkCondition() - no startImage, capturing now');
|
|
223
|
+
startImage = await system.captureScreenPNG(0.25, true);
|
|
165
224
|
}
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
225
|
+
|
|
226
|
+
// If screen redraw is disabled, consider it as "redrawn"
|
|
227
|
+
const effectiveScreenRedrawn = screenRedraw ? screenHasRedrawn : true;
|
|
228
|
+
// If network monitor is disabled, consider it as "settled"
|
|
229
|
+
const effectiveNetworkSettled = networkMonitor ? networkSettled : true;
|
|
230
|
+
|
|
231
|
+
// Log redraw status
|
|
232
|
+
let redrawText = !screenRedraw
|
|
233
|
+
? theme.dim(`disabled`)
|
|
234
|
+
: effectiveScreenRedrawn
|
|
235
|
+
? theme.green(`y`)
|
|
236
|
+
: theme.dim(`${diffPercent}/${diffThreshold}%`);
|
|
237
|
+
let networkText = !networkMonitor
|
|
238
|
+
? theme.dim(`disabled`)
|
|
239
|
+
: effectiveNetworkSettled
|
|
240
|
+
? theme.green(`y`)
|
|
241
|
+
: theme.dim(
|
|
242
|
+
`${Math.trunc((diffRxBytes + diffTxBytes) / networkUpdateInterval)}b/s`,
|
|
243
|
+
);
|
|
176
244
|
let timeoutText = isTimeout
|
|
177
245
|
? theme.green(`y`)
|
|
178
246
|
: theme.dim(`${Math.floor(timeElapsed / 1000)}/${timeoutMs / 1000}s`);
|
|
179
247
|
|
|
180
248
|
emitter.emit(events.redraw.status, {
|
|
181
249
|
redraw: {
|
|
182
|
-
|
|
250
|
+
enabled: screenRedraw,
|
|
251
|
+
hasRedrawn: effectiveScreenRedrawn,
|
|
183
252
|
diffPercent,
|
|
184
|
-
threshold:
|
|
253
|
+
threshold: diffThreshold,
|
|
185
254
|
text: redrawText,
|
|
186
255
|
},
|
|
187
256
|
network: {
|
|
188
|
-
|
|
257
|
+
enabled: networkMonitor,
|
|
258
|
+
settled: effectiveNetworkSettled,
|
|
189
259
|
rxBytes: diffRxBytes,
|
|
190
260
|
txBytes: diffTxBytes,
|
|
191
261
|
text: networkText,
|
|
@@ -198,27 +268,42 @@ const createRedraw = (
|
|
|
198
268
|
},
|
|
199
269
|
});
|
|
200
270
|
|
|
201
|
-
if ((
|
|
271
|
+
if ((effectiveScreenRedrawn && effectiveNetworkSettled) || isTimeout) {
|
|
202
272
|
emitter.emit(events.redraw.complete, {
|
|
203
|
-
screenHasRedrawn,
|
|
204
|
-
networkSettled,
|
|
273
|
+
screenHasRedrawn: effectiveScreenRedrawn,
|
|
274
|
+
networkSettled: effectiveNetworkSettled,
|
|
205
275
|
isTimeout,
|
|
206
276
|
timeElapsed,
|
|
207
277
|
});
|
|
208
278
|
resolve("true");
|
|
209
279
|
} else {
|
|
210
280
|
setTimeout(() => {
|
|
211
|
-
checkCondition(resolve, startTime, timeoutMs);
|
|
281
|
+
checkCondition(resolve, startTime, timeoutMs, options);
|
|
212
282
|
}, 500);
|
|
213
283
|
}
|
|
214
284
|
}
|
|
215
285
|
|
|
216
|
-
function wait(timeoutMs) {
|
|
286
|
+
function wait(timeoutMs, options = {}) {
|
|
287
|
+
// Merge current options with any per-call overrides
|
|
288
|
+
const waitOptions = { ...currentOptions, ...options };
|
|
289
|
+
|
|
290
|
+
// If redraw is disabled, resolve immediately
|
|
291
|
+
if (!waitOptions.enabled) {
|
|
292
|
+
return Promise.resolve("true");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// If both are disabled, resolve immediately
|
|
296
|
+
if (!waitOptions.screenRedraw && !waitOptions.networkMonitor) {
|
|
297
|
+
return Promise.resolve("true");
|
|
298
|
+
}
|
|
299
|
+
|
|
217
300
|
return new Promise((resolve) => {
|
|
218
301
|
const startTime = Date.now();
|
|
219
|
-
// Start network monitoring if not already started
|
|
220
|
-
|
|
221
|
-
|
|
302
|
+
// Start network monitoring if not already started and enabled
|
|
303
|
+
if (waitOptions.networkMonitor) {
|
|
304
|
+
startNetworkMonitoring();
|
|
305
|
+
}
|
|
306
|
+
checkCondition(resolve, startTime, timeoutMs, waitOptions);
|
|
222
307
|
});
|
|
223
308
|
}
|
|
224
309
|
|
|
@@ -226,7 +311,7 @@ const createRedraw = (
|
|
|
226
311
|
stopNetworkMonitoring(networkInterval);
|
|
227
312
|
}
|
|
228
313
|
|
|
229
|
-
return { start, wait, cleanup };
|
|
314
|
+
return { start, wait, cleanup, DEFAULT_OPTIONS: DEFAULT_REDRAW_OPTIONS };
|
|
230
315
|
};
|
|
231
316
|
|
|
232
|
-
module.exports = { createRedraw };
|
|
317
|
+
module.exports = { createRedraw, DEFAULT_REDRAW_OPTIONS };
|
package/agent/lib/sandbox.js
CHANGED
|
@@ -2,7 +2,7 @@ const WebSocket = require("ws");
|
|
|
2
2
|
const marky = require("marky");
|
|
3
3
|
const { events } = require("../events");
|
|
4
4
|
|
|
5
|
-
const createSandbox = (emitter, analytics) => {
|
|
5
|
+
const createSandbox = (emitter, analytics, sessionInstance) => {
|
|
6
6
|
class Sandbox {
|
|
7
7
|
constructor() {
|
|
8
8
|
this.socket = null;
|
|
@@ -15,6 +15,7 @@ const createSandbox = (emitter, analytics) => {
|
|
|
15
15
|
this.messageId = 0;
|
|
16
16
|
this.uniqueId = Math.random().toString(36).substring(7);
|
|
17
17
|
this.os = null; // Store OS value to send with every message
|
|
18
|
+
this.sessionInstance = sessionInstance; // Store session instance to include in messages
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
send(message) {
|
|
@@ -35,6 +36,14 @@ const createSandbox = (emitter, analytics) => {
|
|
|
35
36
|
message.os = this.os;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
// Add session to every message if available (for interaction tracking)
|
|
40
|
+
if (this.sessionInstance && !message.session) {
|
|
41
|
+
const sessionId = this.sessionInstance.get();
|
|
42
|
+
if (sessionId) {
|
|
43
|
+
message.session = sessionId;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
// Start timing for this message
|
|
39
48
|
const timingKey = `sandbox-${message.type}`;
|
|
40
49
|
marky.mark(timingKey);
|
package/agent/lib/sdk.js
CHANGED
|
@@ -194,6 +194,27 @@ const createSDK = (emitter, config, sessionInstance) => {
|
|
|
194
194
|
|
|
195
195
|
return value;
|
|
196
196
|
} catch (error) {
|
|
197
|
+
// Check if this is an API validation error with detailed problems
|
|
198
|
+
if (error.response?.data?.problems) {
|
|
199
|
+
const problems = error.response.data.problems;
|
|
200
|
+
const errorMessage = error.response.data.message || 'API validation error';
|
|
201
|
+
const detailedError = new Error(
|
|
202
|
+
`${errorMessage}\n\nDetails:\n${problems.map(p => ` - ${p}`).join('\n')}`
|
|
203
|
+
);
|
|
204
|
+
detailedError.originalError = error;
|
|
205
|
+
detailedError.problems = problems;
|
|
206
|
+
|
|
207
|
+
// Emit the formatted error
|
|
208
|
+
emitter.emit(events.error.sdk, {
|
|
209
|
+
message: detailedError.message,
|
|
210
|
+
code: error.response?.data?.code || error.code,
|
|
211
|
+
problems: problems,
|
|
212
|
+
fullError: error,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
throw detailedError;
|
|
216
|
+
}
|
|
217
|
+
|
|
197
218
|
outputError(error);
|
|
198
219
|
throw error; // Re-throw the error so calling code can handle it properly
|
|
199
220
|
}
|