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
|
@@ -4,6 +4,38 @@ import os from "os";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { setTestRunInfo } from "./shared-test-state.mjs";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Simple logger for the vitest plugin
|
|
9
|
+
* Supports log levels: debug, info, warn, error
|
|
10
|
+
* Control via TD_LOG_LEVEL environment variable (default: "info")
|
|
11
|
+
* Set TD_LOG_LEVEL=debug for verbose output
|
|
12
|
+
*/
|
|
13
|
+
const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
14
|
+
const currentLogLevel = LOG_LEVELS[process.env.TD_LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
|
|
15
|
+
|
|
16
|
+
const logger = {
|
|
17
|
+
debug: (...args) => {
|
|
18
|
+
if (currentLogLevel <= LOG_LEVELS.debug) {
|
|
19
|
+
console.log("[TestDriver]", ...args);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
info: (...args) => {
|
|
23
|
+
if (currentLogLevel <= LOG_LEVELS.info) {
|
|
24
|
+
console.log("[TestDriver]", ...args);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
warn: (...args) => {
|
|
28
|
+
if (currentLogLevel <= LOG_LEVELS.warn) {
|
|
29
|
+
console.warn("[TestDriver]", ...args);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
error: (...args) => {
|
|
33
|
+
if (currentLogLevel <= LOG_LEVELS.error) {
|
|
34
|
+
console.error("[TestDriver]", ...args);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
7
39
|
/**
|
|
8
40
|
* Timeout wrapper for promises
|
|
9
41
|
* @param {Promise} promise - Promise to wrap
|
|
@@ -58,6 +90,7 @@ function withTimeout(promise, timeoutMs, operationName) {
|
|
|
58
90
|
export const pluginState = {
|
|
59
91
|
testRun: null,
|
|
60
92
|
testRunId: null,
|
|
93
|
+
testRunCompleted: false,
|
|
61
94
|
client: null,
|
|
62
95
|
startTime: null,
|
|
63
96
|
testCases: new Map(),
|
|
@@ -68,6 +101,8 @@ export const pluginState = {
|
|
|
68
101
|
gitInfo: {},
|
|
69
102
|
apiKey: null,
|
|
70
103
|
apiRoot: null,
|
|
104
|
+
// TestDriver options to pass to all instances
|
|
105
|
+
testDriverOptions: {},
|
|
71
106
|
// Dashcam URL tracking (in-memory, no files needed!)
|
|
72
107
|
dashcamUrls: new Map(), // testId -> dashcamUrl
|
|
73
108
|
lastDashcamUrl: null, // Fallback for when test ID isn't available
|
|
@@ -77,7 +112,7 @@ export const pluginState = {
|
|
|
77
112
|
|
|
78
113
|
// Export functions that can be used by the reporter or tests
|
|
79
114
|
export function registerDashcamUrl(testId, url, platform) {
|
|
80
|
-
|
|
115
|
+
logger.debug(`Registering dashcam URL for test ${testId}:`, url);
|
|
81
116
|
pluginState.dashcamUrls.set(testId, { url, platform });
|
|
82
117
|
pluginState.lastDashcamUrl = url;
|
|
83
118
|
}
|
|
@@ -96,7 +131,7 @@ export function getSuiteTestRun(suiteId) {
|
|
|
96
131
|
}
|
|
97
132
|
|
|
98
133
|
export function setSuiteTestRun(suiteId, runData) {
|
|
99
|
-
|
|
134
|
+
logger.debug(`Setting test run for suite ${suiteId}:`, runData);
|
|
100
135
|
pluginState.suiteTestRuns.set(suiteId, runData);
|
|
101
136
|
}
|
|
102
137
|
|
|
@@ -183,6 +218,75 @@ export async function recordTestCaseDirect(token, apiRoot, testCaseData) {
|
|
|
183
218
|
return await response.json();
|
|
184
219
|
}
|
|
185
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Handle process termination and mark test run as cancelled
|
|
223
|
+
*/
|
|
224
|
+
async function handleProcessExit() {
|
|
225
|
+
if (!pluginState.testRun || !pluginState.testRunId) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
logger.info("Process interrupted, marking test run as cancelled...");
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const stats = {
|
|
233
|
+
totalTests: pluginState.testCases.size,
|
|
234
|
+
passedTests: 0,
|
|
235
|
+
failedTests: 0,
|
|
236
|
+
skippedTests: 0,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const completeData = {
|
|
240
|
+
runId: pluginState.testRunId,
|
|
241
|
+
status: "cancelled",
|
|
242
|
+
totalTests: stats.totalTests,
|
|
243
|
+
passedTests: stats.passedTests,
|
|
244
|
+
failedTests: stats.failedTests,
|
|
245
|
+
skippedTests: stats.skippedTests,
|
|
246
|
+
duration: Date.now() - pluginState.startTime,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Update platform if detected
|
|
250
|
+
const platform = getPlatform();
|
|
251
|
+
if (platform) {
|
|
252
|
+
completeData.platform = platform;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
await completeTestRun(completeData);
|
|
256
|
+
logger.info("✅ Test run marked as cancelled");
|
|
257
|
+
} catch (error) {
|
|
258
|
+
logger.error("Failed to mark test run as cancelled:", error.message);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Set up process exit handlers
|
|
263
|
+
let exitHandlersRegistered = false;
|
|
264
|
+
|
|
265
|
+
function registerExitHandlers() {
|
|
266
|
+
if (exitHandlersRegistered) return;
|
|
267
|
+
exitHandlersRegistered = true;
|
|
268
|
+
|
|
269
|
+
// Handle Ctrl+C
|
|
270
|
+
process.on("SIGINT", async () => {
|
|
271
|
+
await handleProcessExit();
|
|
272
|
+
process.exit(130); // Standard exit code for SIGINT
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Handle kill command
|
|
276
|
+
process.on("SIGTERM", async () => {
|
|
277
|
+
await handleProcessExit();
|
|
278
|
+
process.exit(143); // Standard exit code for SIGTERM
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Handle unexpected exits
|
|
282
|
+
process.on("beforeExit", async () => {
|
|
283
|
+
// Only handle if test run is still running (hasn't been completed normally)
|
|
284
|
+
if (pluginState.testRun && !pluginState.testRunCompleted) {
|
|
285
|
+
await handleProcessExit();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
186
290
|
/**
|
|
187
291
|
* Create the TestDriver Vitest plugin
|
|
188
292
|
* This sets up global state and provides the registration API
|
|
@@ -194,12 +298,19 @@ export default function testDriverPlugin(options = {}) {
|
|
|
194
298
|
options.apiRoot || process.env.TD_API_ROOT || "http://localhost:1337";
|
|
195
299
|
pluginState.ciProvider = detectCI();
|
|
196
300
|
pluginState.gitInfo = getGitInfo();
|
|
301
|
+
|
|
302
|
+
// Store TestDriver-specific options (excluding plugin-specific ones)
|
|
303
|
+
const { apiKey, apiRoot, ...testDriverOptions } = options;
|
|
304
|
+
pluginState.testDriverOptions = testDriverOptions;
|
|
305
|
+
|
|
306
|
+
// Register process exit handlers to handle cancellation
|
|
307
|
+
registerExitHandlers();
|
|
197
308
|
|
|
198
309
|
// Note: globalThis setup happens in vitestSetup.mjs for worker processes
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
310
|
+
logger.debug("Initialized with API root:", pluginState.apiRoot);
|
|
311
|
+
if (Object.keys(testDriverOptions).length > 0) {
|
|
312
|
+
logger.debug("Global TestDriver options:", testDriverOptions);
|
|
313
|
+
}
|
|
203
314
|
|
|
204
315
|
return new TestDriverReporter(options);
|
|
205
316
|
}
|
|
@@ -211,25 +322,23 @@ export default function testDriverPlugin(options = {}) {
|
|
|
211
322
|
class TestDriverReporter {
|
|
212
323
|
constructor(options = {}) {
|
|
213
324
|
this.options = options;
|
|
214
|
-
|
|
325
|
+
logger.debug("Reporter created");
|
|
215
326
|
}
|
|
216
327
|
|
|
217
328
|
async onInit(ctx) {
|
|
218
329
|
this.ctx = ctx;
|
|
219
|
-
|
|
330
|
+
logger.debug("onInit called");
|
|
220
331
|
|
|
221
332
|
// Initialize test run
|
|
222
333
|
await this.initializeTestRun();
|
|
223
334
|
}
|
|
224
335
|
|
|
225
336
|
async initializeTestRun() {
|
|
226
|
-
|
|
337
|
+
logger.debug("Initializing test run...");
|
|
227
338
|
|
|
228
339
|
// Check if we should enable the reporter
|
|
229
340
|
if (!pluginState.apiKey) {
|
|
230
|
-
|
|
231
|
-
"[TestDriver Reporter] No API key provided, skipping test recording",
|
|
232
|
-
);
|
|
341
|
+
logger.debug("No API key provided, skipping test recording");
|
|
233
342
|
return;
|
|
234
343
|
}
|
|
235
344
|
|
|
@@ -240,6 +349,7 @@ class TestDriverReporter {
|
|
|
240
349
|
// Generate unique run ID
|
|
241
350
|
pluginState.testRunId = generateRunId();
|
|
242
351
|
pluginState.startTime = Date.now();
|
|
352
|
+
pluginState.testRunCompleted = false; // Reset completion flag
|
|
243
353
|
|
|
244
354
|
// Create test run via direct API call
|
|
245
355
|
const testRunData = {
|
|
@@ -248,6 +358,8 @@ class TestDriverReporter {
|
|
|
248
358
|
...pluginState.gitInfo,
|
|
249
359
|
};
|
|
250
360
|
|
|
361
|
+
// Session ID will be added from the first test result file that includes it
|
|
362
|
+
|
|
251
363
|
// Only add ciProvider if it's not null
|
|
252
364
|
if (pluginState.ciProvider) {
|
|
253
365
|
testRunData.ciProvider = pluginState.ciProvider;
|
|
@@ -274,31 +386,24 @@ class TestDriverReporter {
|
|
|
274
386
|
startTime: pluginState.startTime,
|
|
275
387
|
});
|
|
276
388
|
|
|
277
|
-
|
|
278
|
-
`[TestDriver Reporter] Test run created: ${pluginState.testRunId}`,
|
|
279
|
-
);
|
|
389
|
+
logger.info(`Test run created: ${pluginState.testRunId}`);
|
|
280
390
|
} catch (error) {
|
|
281
|
-
|
|
282
|
-
"[TestDriver Reporter] Failed to initialize:",
|
|
283
|
-
error.message,
|
|
284
|
-
);
|
|
391
|
+
logger.error("Failed to initialize:", error.message);
|
|
285
392
|
pluginState.apiKey = null;
|
|
286
393
|
pluginState.token = null;
|
|
287
394
|
}
|
|
288
395
|
}
|
|
289
396
|
|
|
290
397
|
async onTestRunEnd(testModules, unhandledErrors, reason) {
|
|
291
|
-
|
|
398
|
+
logger.debug("Test run ending with reason:", reason);
|
|
292
399
|
|
|
293
400
|
if (!pluginState.apiKey) {
|
|
294
|
-
|
|
401
|
+
logger.debug("Skipping completion - no API key");
|
|
295
402
|
return;
|
|
296
403
|
}
|
|
297
404
|
|
|
298
405
|
if (!pluginState.testRun) {
|
|
299
|
-
|
|
300
|
-
"[TestDriver Reporter] Skipping completion - no test run created",
|
|
301
|
-
);
|
|
406
|
+
logger.debug("Skipping completion - no test run created");
|
|
302
407
|
return;
|
|
303
408
|
}
|
|
304
409
|
|
|
@@ -306,7 +411,7 @@ class TestDriverReporter {
|
|
|
306
411
|
// Calculate statistics from testModules
|
|
307
412
|
const stats = calculateStatsFromModules(testModules);
|
|
308
413
|
|
|
309
|
-
|
|
414
|
+
logger.debug("Stats:", stats);
|
|
310
415
|
|
|
311
416
|
// Determine overall status based on reason and stats
|
|
312
417
|
let status = "passed";
|
|
@@ -319,9 +424,7 @@ class TestDriverReporter {
|
|
|
319
424
|
}
|
|
320
425
|
|
|
321
426
|
// Complete test run via API
|
|
322
|
-
|
|
323
|
-
`[TestDriver Reporter] Completing test run ${pluginState.testRunId} with status: ${status}`,
|
|
324
|
-
);
|
|
427
|
+
logger.debug(`Completing test run ${pluginState.testRunId} with status: ${status}`);
|
|
325
428
|
|
|
326
429
|
const completeData = {
|
|
327
430
|
runId: pluginState.testRunId,
|
|
@@ -337,39 +440,28 @@ class TestDriverReporter {
|
|
|
337
440
|
const platform = getPlatform();
|
|
338
441
|
if (platform) {
|
|
339
442
|
completeData.platform = platform;
|
|
340
|
-
|
|
341
|
-
`[TestDriver Reporter] Updating test run with platform: ${platform}`,
|
|
342
|
-
);
|
|
443
|
+
logger.debug(`Updating test run with platform: ${platform}`);
|
|
343
444
|
}
|
|
344
445
|
|
|
345
446
|
// Wait for any pending operations (shouldn't be any, but just in case)
|
|
346
447
|
if (pluginState.pendingTestCaseRecords.size > 0) {
|
|
347
|
-
|
|
348
|
-
`[TestDriver Reporter] Waiting for ${pluginState.pendingTestCaseRecords.size} pending operations...`,
|
|
349
|
-
);
|
|
448
|
+
logger.debug(`Waiting for ${pluginState.pendingTestCaseRecords.size} pending operations...`);
|
|
350
449
|
await Promise.all(Array.from(pluginState.pendingTestCaseRecords));
|
|
351
450
|
}
|
|
352
451
|
|
|
353
452
|
// Test cases are reported directly from teardownTest
|
|
354
|
-
|
|
355
|
-
`[TestDriver Reporter] All test cases reported from teardown`,
|
|
356
|
-
);
|
|
453
|
+
logger.debug("All test cases reported from teardown");
|
|
357
454
|
|
|
358
455
|
const completeResponse = await completeTestRun(completeData);
|
|
359
|
-
|
|
360
|
-
`[TestDriver Reporter] ✅ Test run completion API response:`,
|
|
361
|
-
completeResponse,
|
|
362
|
-
);
|
|
456
|
+
logger.debug("Test run completion API response:", completeResponse);
|
|
363
457
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
458
|
+
// Mark test run as completed to prevent duplicate completion
|
|
459
|
+
pluginState.testRunCompleted = true;
|
|
460
|
+
|
|
461
|
+
logger.info(`✅ Test run completed: ${stats.passedTests}/${stats.totalTests} passed`);
|
|
367
462
|
} catch (error) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
error.message,
|
|
371
|
-
);
|
|
372
|
-
console.error("[TestDriver Reporter] Error stack:", error.stack);
|
|
463
|
+
logger.error("Failed to complete test run:", error.message);
|
|
464
|
+
logger.debug("Error stack:", error.stack);
|
|
373
465
|
}
|
|
374
466
|
}
|
|
375
467
|
|
|
@@ -396,15 +488,19 @@ class TestDriverReporter {
|
|
|
396
488
|
? "skipped"
|
|
397
489
|
: "failed";
|
|
398
490
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
491
|
+
logger.info(`Test case completed: ${test.name} (${status})`);
|
|
492
|
+
|
|
493
|
+
// Calculate duration from tracked start time
|
|
494
|
+
const testCase = pluginState.testCases.get(test.id);
|
|
495
|
+
const duration = testCase ? Date.now() - testCase.startTime : 0;
|
|
496
|
+
|
|
497
|
+
logger.debug(`Calculated duration: ${duration}ms (startTime: ${testCase?.startTime}, now: ${Date.now()})`);
|
|
402
498
|
|
|
403
499
|
// Read test metadata from file (cross-process communication)
|
|
404
500
|
let dashcamUrl = null;
|
|
501
|
+
let sessionId = null;
|
|
405
502
|
let testFile = "unknown";
|
|
406
503
|
let testOrder = 0;
|
|
407
|
-
let duration = result.duration || 0;
|
|
408
504
|
|
|
409
505
|
const testResultFile = path.join(
|
|
410
506
|
os.tmpdir(),
|
|
@@ -412,11 +508,15 @@ class TestDriverReporter {
|
|
|
412
508
|
`${test.id}.json`,
|
|
413
509
|
);
|
|
414
510
|
|
|
511
|
+
logger.debug(`Looking for test result file with test.id: ${test.id}`);
|
|
512
|
+
logger.debug(`Test result file path: ${testResultFile}`);
|
|
513
|
+
|
|
415
514
|
try {
|
|
416
515
|
if (fs.existsSync(testResultFile)) {
|
|
417
516
|
const testResult = JSON.parse(fs.readFileSync(testResultFile, "utf-8"));
|
|
418
517
|
dashcamUrl = testResult.dashcamUrl || null;
|
|
419
518
|
const platform = testResult.platform || null;
|
|
519
|
+
sessionId = testResult.sessionId || null;
|
|
420
520
|
testFile =
|
|
421
521
|
testResult.testFile ||
|
|
422
522
|
test.file?.filepath ||
|
|
@@ -424,18 +524,15 @@ class TestDriverReporter {
|
|
|
424
524
|
"unknown";
|
|
425
525
|
testOrder =
|
|
426
526
|
testResult.testOrder !== undefined ? testResult.testOrder : 0;
|
|
427
|
-
|
|
527
|
+
// Don't override duration from file - use Vitest's result.duration
|
|
528
|
+
// duration is already set above from result.duration
|
|
428
529
|
|
|
429
|
-
|
|
430
|
-
`[TestDriver Reporter] ✅ Read from file - dashcam: ${dashcamUrl}, platform: ${platform}, testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms`,
|
|
431
|
-
);
|
|
530
|
+
logger.debug(`Read from file - dashcam: ${dashcamUrl}, platform: ${platform}, sessionId: ${sessionId}, testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms`);
|
|
432
531
|
|
|
433
532
|
// Update test run platform from first test that reports it
|
|
434
533
|
if (platform && !pluginState.detectedPlatform) {
|
|
435
534
|
pluginState.detectedPlatform = platform;
|
|
436
|
-
|
|
437
|
-
`[TestDriver Reporter] 🖥️ Detected platform from test: ${platform}`,
|
|
438
|
-
);
|
|
535
|
+
logger.debug(`Detected platform from test: ${platform}`);
|
|
439
536
|
}
|
|
440
537
|
|
|
441
538
|
// Clean up the file after reading
|
|
@@ -445,37 +542,36 @@ class TestDriverReporter {
|
|
|
445
542
|
// Ignore cleanup errors
|
|
446
543
|
}
|
|
447
544
|
} else {
|
|
448
|
-
|
|
449
|
-
`[TestDriver Reporter] ⚠️ No result file found for test: ${test.id}`,
|
|
450
|
-
);
|
|
545
|
+
logger.debug(`No result file found for test: ${test.id}`);
|
|
451
546
|
// Fallback to test object properties - try multiple sources
|
|
452
|
-
// In Vitest, the file path is
|
|
453
|
-
const module = test.module || test.suite;
|
|
547
|
+
// In Vitest, the file path is on test.module.task.filepath
|
|
454
548
|
testFile =
|
|
549
|
+
test.module?.task?.filepath ||
|
|
550
|
+
test.module?.file?.filepath ||
|
|
551
|
+
test.module?.file?.name ||
|
|
455
552
|
test.file?.filepath ||
|
|
456
553
|
test.file?.name ||
|
|
457
|
-
|
|
458
|
-
|
|
554
|
+
test.suite?.file?.filepath ||
|
|
555
|
+
test.suite?.file?.name ||
|
|
459
556
|
test.location?.file ||
|
|
460
557
|
"unknown";
|
|
461
|
-
|
|
462
|
-
`[TestDriver Reporter] 📂 Resolved testFile for skipped test: ${testFile}`,
|
|
463
|
-
);
|
|
558
|
+
logger.debug(`Resolved testFile: ${testFile}`);
|
|
464
559
|
}
|
|
465
560
|
} catch (error) {
|
|
466
|
-
|
|
467
|
-
`[TestDriver Reporter] ❌ Failed to read test result file:`,
|
|
468
|
-
error.message,
|
|
469
|
-
);
|
|
561
|
+
logger.error("Failed to read test result file:", error.message);
|
|
470
562
|
// Fallback to test object properties - try multiple sources
|
|
471
|
-
|
|
563
|
+
// In Vitest, the file path is on test.module.task.filepath
|
|
472
564
|
testFile =
|
|
565
|
+
test.module?.task?.filepath ||
|
|
566
|
+
test.module?.file?.filepath ||
|
|
567
|
+
test.module?.file?.name ||
|
|
473
568
|
test.file?.filepath ||
|
|
474
569
|
test.file?.name ||
|
|
475
|
-
|
|
476
|
-
|
|
570
|
+
test.suite?.file?.filepath ||
|
|
571
|
+
test.suite?.file?.name ||
|
|
477
572
|
test.location?.file ||
|
|
478
573
|
"unknown";
|
|
574
|
+
logger.debug(`Resolved testFile from fallback: ${testFile}`);
|
|
479
575
|
}
|
|
480
576
|
|
|
481
577
|
// Get test run info from environment variables
|
|
@@ -483,9 +579,7 @@ class TestDriverReporter {
|
|
|
483
579
|
const token = process.env.TD_TEST_RUN_TOKEN;
|
|
484
580
|
|
|
485
581
|
if (!testRunId || !token) {
|
|
486
|
-
|
|
487
|
-
`[TestDriver Reporter] ⚠️ Test run not initialized, skipping test case recording for: ${test.name}`,
|
|
488
|
-
);
|
|
582
|
+
logger.warn(`Test run not initialized, skipping test case recording for: ${test.name}`);
|
|
489
583
|
return;
|
|
490
584
|
}
|
|
491
585
|
|
|
@@ -519,6 +613,11 @@ class TestDriverReporter {
|
|
|
519
613
|
retries: result.retryCount || 0,
|
|
520
614
|
};
|
|
521
615
|
|
|
616
|
+
// Add sessionId if available
|
|
617
|
+
if (sessionId) {
|
|
618
|
+
testCaseData.sessionId = sessionId;
|
|
619
|
+
}
|
|
620
|
+
|
|
522
621
|
// Only include replayUrl if we have a valid dashcam URL
|
|
523
622
|
if (dashcamUrl) {
|
|
524
623
|
testCaseData.replayUrl = dashcamUrl;
|
|
@@ -528,9 +627,7 @@ class TestDriverReporter {
|
|
|
528
627
|
if (errorMessage) testCaseData.errorMessage = errorMessage;
|
|
529
628
|
if (errorStack) testCaseData.errorStack = errorStack;
|
|
530
629
|
|
|
531
|
-
|
|
532
|
-
`[TestDriver Reporter] Recording test case: ${test.name} (${status}) with testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms, replay: ${dashcamUrl ? "yes" : "no"}`,
|
|
533
|
-
);
|
|
630
|
+
logger.debug(`Recording test case: ${test.name} (${status}) with testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms, replay: ${dashcamUrl ? "yes" : "no"}`);
|
|
534
631
|
|
|
535
632
|
const testCaseResponse = await recordTestCaseDirect(
|
|
536
633
|
token,
|
|
@@ -541,17 +638,10 @@ class TestDriverReporter {
|
|
|
541
638
|
const testCaseDbId = testCaseResponse.data?.id;
|
|
542
639
|
const testRunDbId = process.env.TD_TEST_RUN_DB_ID;
|
|
543
640
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
);
|
|
547
|
-
console.log(
|
|
548
|
-
`[TestDriver Reporter] 🔗 View test: ${pluginState.apiRoot.replace("testdriver-api.onrender.com", "app.testdriver.ai")}/test-runs/${testRunDbId}/${testCaseDbId}`,
|
|
549
|
-
);
|
|
641
|
+
logger.debug(`Reported test case to API${dashcamUrl ? " with dashcam URL" : ""}`);
|
|
642
|
+
logger.info(`🔗 View test: ${pluginState.apiRoot.replace("testdriver-api.onrender.com", "app.testdriver.ai")}/runs/${testRunDbId}/${testCaseDbId}`);
|
|
550
643
|
} catch (error) {
|
|
551
|
-
|
|
552
|
-
`[TestDriver Reporter] ❌ Failed to report test case:`,
|
|
553
|
-
error.message,
|
|
554
|
-
);
|
|
644
|
+
logger.error("Failed to report test case:", error.message);
|
|
555
645
|
}
|
|
556
646
|
}
|
|
557
647
|
}
|
|
@@ -571,13 +661,11 @@ function getSuiteName() {
|
|
|
571
661
|
function getPlatform() {
|
|
572
662
|
// First try to get platform from SDK client detected during test execution
|
|
573
663
|
if (pluginState.detectedPlatform) {
|
|
574
|
-
|
|
575
|
-
`[TestDriver Plugin] Using platform from SDK client: ${pluginState.detectedPlatform}`,
|
|
576
|
-
);
|
|
664
|
+
logger.debug(`Using platform from SDK client: ${pluginState.detectedPlatform}`);
|
|
577
665
|
return pluginState.detectedPlatform;
|
|
578
666
|
}
|
|
579
667
|
|
|
580
|
-
|
|
668
|
+
logger.debug("Platform not yet detected from client");
|
|
581
669
|
return null;
|
|
582
670
|
}
|
|
583
671
|
|
|
@@ -594,9 +682,7 @@ function detectPlatformFromTest(test) {
|
|
|
594
682
|
else if (platform === "linux") platform = "linux";
|
|
595
683
|
|
|
596
684
|
pluginState.detectedPlatform = platform;
|
|
597
|
-
|
|
598
|
-
`[TestDriver Plugin] Detected platform from test context: ${platform}`,
|
|
599
|
-
);
|
|
685
|
+
logger.debug(`Detected platform from test context: ${platform}`);
|
|
600
686
|
}
|
|
601
687
|
}
|
|
602
688
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testdriverai",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
|
|
5
5
|
"main": "sdk.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./sdk.js",
|
|
8
|
+
"./core": "./src/core/index.js",
|
|
9
|
+
"./vitest": "./src/vitest/index.mjs",
|
|
10
|
+
"./vitest/hooks": "./src/vitest/hooks.mjs",
|
|
11
|
+
"./vitest/extended": "./src/vitest/extended.mjs",
|
|
12
|
+
"./vitest/lifecycle": "./src/vitest/lifecycle.mjs",
|
|
13
|
+
"./vitest/utils": "./src/vitest/utils.mjs",
|
|
14
|
+
"./vitest/plugin": "./interfaces/vitest-plugin.mjs",
|
|
15
|
+
"./presets": "./src/presets/index.mjs"
|
|
16
|
+
},
|
|
6
17
|
"bin": {
|
|
7
18
|
"testdriverai": "bin/testdriverai.js"
|
|
8
19
|
},
|