testdriverai 7.2.3 → 7.2.9

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/sdk.js CHANGED
@@ -263,6 +263,48 @@ class ElementNotFoundError extends Error {
263
263
  }
264
264
  }
265
265
 
266
+ /**
267
+ * Custom error class for act() failures
268
+ * Includes task execution details and retry information
269
+ */
270
+ class ActError extends Error {
271
+ /**
272
+ * @param {string} message - Error message
273
+ * @param {Object} details - Additional details about the failure
274
+ * @param {string} details.task - The task that was attempted
275
+ * @param {number} details.tries - Number of check attempts made
276
+ * @param {number} details.maxTries - Maximum tries that were allowed
277
+ * @param {number} details.duration - Total execution time in milliseconds
278
+ * @param {Error} [details.cause] - The underlying error that caused the failure
279
+ */
280
+ constructor(message, details = {}) {
281
+ super(message);
282
+ this.name = "ActError";
283
+ this.task = details.task;
284
+ this.tries = details.tries;
285
+ this.maxTries = details.maxTries;
286
+ this.duration = details.duration;
287
+ this.cause = details.cause;
288
+ this.timestamp = new Date().toISOString();
289
+
290
+ // Capture stack trace
291
+ if (Error.captureStackTrace) {
292
+ Error.captureStackTrace(this, ActError);
293
+ }
294
+
295
+ // Enhance error message with execution details
296
+ this.message += `\n\n=== Act Execution Details ===`;
297
+ this.message += `\nTask: "${this.task}"`;
298
+ this.message += `\nTries: ${this.tries}/${this.maxTries}`;
299
+ this.message += `\nDuration: ${this.duration}ms`;
300
+ this.message += `\nTimestamp: ${this.timestamp}`;
301
+
302
+ if (this.cause) {
303
+ this.message += `\nUnderlying error: ${this.cause.message}`;
304
+ }
305
+ }
306
+ }
307
+
266
308
  /**
267
309
  * Element class representing a located or to-be-located element
268
310
  */
@@ -2188,10 +2230,7 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2188
2230
  const absoluteTimestamp = Date.now();
2189
2231
  const startTime = absoluteTimestamp;
2190
2232
 
2191
- // Log finding all action
2192
2233
  const { events } = require("./agent/events.js");
2193
- const findingMessage = formatter.formatElementsFinding(description);
2194
- this.emitter.emit(events.log.log, findingMessage);
2195
2234
 
2196
2235
  try {
2197
2236
  const screenshot = await this.system.captureScreenBase64();
@@ -2257,16 +2296,16 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2257
2296
  const duration = Date.now() - startTime;
2258
2297
 
2259
2298
  if (response && response.elements && response.elements.length > 0) {
2260
- // Log found elements
2261
- const foundMessage = formatter.formatElementsFound(
2299
+ // Single log at the end - found elements
2300
+ const formattedMessage = formatter.formatFindAllSingleLine(
2262
2301
  description,
2263
2302
  response.elements.length,
2264
2303
  {
2265
- duration: `${duration}ms`,
2304
+ duration: duration,
2266
2305
  cacheHit: response.cached || false,
2267
2306
  },
2268
2307
  );
2269
- this.emitter.emit(events.log.log, foundMessage);
2308
+ this.emitter.emit(events.log.narration, formattedMessage, true);
2270
2309
 
2271
2310
  // Create Element instances for each found element
2272
2311
  const elements = response.elements.map((elementData) => {
@@ -2316,7 +2355,6 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2316
2355
 
2317
2356
  // Log debug information when elements are found
2318
2357
  if (process.env.VERBOSE || process.env.DEBUG || process.env.TD_DEBUG) {
2319
- const { events } = require("./agent/events.js");
2320
2358
  this.emitter.emit(
2321
2359
  events.log.debug,
2322
2360
  `✓ Found ${elements.length} element(s): "${description}"`,
@@ -2330,6 +2368,19 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2330
2368
 
2331
2369
  return elements;
2332
2370
  } else {
2371
+ const duration = Date.now() - startTime;
2372
+
2373
+ // Single log at the end - no elements found
2374
+ const formattedMessage = formatter.formatFindAllSingleLine(
2375
+ description,
2376
+ 0,
2377
+ {
2378
+ duration: duration,
2379
+ cacheHit: response?.cached || false,
2380
+ },
2381
+ );
2382
+ this.emitter.emit(events.log.narration, formattedMessage, true);
2383
+
2333
2384
  // No elements found - track interaction (fire-and-forget, don't block)
2334
2385
  const sessionId = this.getSessionId();
2335
2386
  if (sessionId && this.sandbox?.send) {
@@ -2354,6 +2405,18 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2354
2405
  return [];
2355
2406
  }
2356
2407
  } catch (error) {
2408
+ const duration = Date.now() - startTime;
2409
+
2410
+ // Single log at the end - error
2411
+ const formattedMessage = formatter.formatFindAllSingleLine(
2412
+ description,
2413
+ 0,
2414
+ {
2415
+ duration: duration,
2416
+ },
2417
+ );
2418
+ this.emitter.emit(events.log.narration, formattedMessage, true);
2419
+
2357
2420
  // Track findAll error interaction (fire-and-forget, don't block)
2358
2421
  const sessionId = this.getSessionId();
2359
2422
  if (sessionId && this.sandbox?.send) {
@@ -2371,8 +2434,6 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2371
2434
  });
2372
2435
  }
2373
2436
 
2374
- const { events } = require("./agent/events.js");
2375
- this.emitter.emit(events.log.log, `Error in findAll: ${error.message}`);
2376
2437
  return [];
2377
2438
  }
2378
2439
  }
@@ -2771,6 +2832,11 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2771
2832
  // handles forwarding to sandbox. This prevents duplicate output to server.
2772
2833
  this.emitter.on("log:**", (message) => {
2773
2834
  const event = this.emitter.event;
2835
+
2836
+ if (event.includes("markdown")) {
2837
+ return;
2838
+ }
2839
+
2774
2840
  if (event === events.log.debug && !debugMode) return;
2775
2841
  if (this.loggingEnabled && message) {
2776
2842
  const prefixedMessage = this.testContext
@@ -2920,39 +2986,11 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2920
2986
  const platform = options.platform || this.config.TD_PLATFORM || "linux";
2921
2987
 
2922
2988
  // Auto-detect sandbox ID from the active sandbox if not provided
2923
- const sandboxId = options.sandboxId || this.agent?.sandbox?.id || null;
2989
+ // For E2B (Linux), the instance has sandboxId; for AWS (Windows), it has instanceId
2990
+ const sandboxId = options.sandboxId || this.instance?.sandboxId || this.instance?.instanceId || this.agent?.sandboxId || null;
2924
2991
 
2925
2992
  // Get or create session ID using the agent's newSession method
2926
2993
  let sessionId = this.agent?.sessionInstance?.get() || null;
2927
-
2928
- // If no session exists, create one using the agent's method
2929
- if (!sessionId && this.agent?.newSession) {
2930
- try {
2931
- await this.agent.newSession();
2932
- sessionId = this.agent.sessionInstance.get();
2933
-
2934
- // Save session ID to file for reuse across test runs
2935
- if (sessionId) {
2936
- const sessionFile = path.join(os.homedir(), '.testdriverai-session');
2937
- fs.writeFileSync(sessionFile, sessionId, { encoding: 'utf-8' });
2938
- }
2939
- } catch (error) {
2940
- // Log but don't fail - tests can run without a session
2941
- console.warn('Failed to create session:', error.message);
2942
- }
2943
- }
2944
-
2945
- // If still no session, try reading from file (for reporter/separate processes)
2946
- if (!sessionId) {
2947
- try {
2948
- const sessionFile = path.join(os.homedir(), '.testdriverai-session');
2949
- if (fs.existsSync(sessionFile)) {
2950
- sessionId = fs.readFileSync(sessionFile, 'utf-8').trim();
2951
- }
2952
- } catch (error) {
2953
- // Ignore file read errors
2954
- }
2955
- }
2956
2994
 
2957
2995
  const data = {
2958
2996
  runId: options.runId,
@@ -3076,26 +3114,98 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
3076
3114
  * This is the SDK equivalent of the CLI's exploratory loop
3077
3115
  *
3078
3116
  * @param {string} task - Natural language description of what to do
3079
- * @param {Object} options - Execution options
3080
- * @param {boolean} [options.validateAndLoop=false] - Whether to validate completion and retry if incomplete
3081
- * @returns {Promise<string|void>} Final AI response if validateAndLoop is true
3117
+ * @param {Object} [options] - Execution options
3118
+ * @param {number} [options.tries=7] - Maximum number of check/retry attempts before giving up
3119
+ * @returns {Promise<ActResult>} Result object with success status and details
3120
+ * @throws {ActError} When the task fails after all tries are exhausted
3121
+ *
3122
+ * @typedef {Object} ActResult
3123
+ * @property {boolean} success - Whether the task completed successfully
3124
+ * @property {string} task - The original task that was executed
3125
+ * @property {number} tries - Number of check attempts made
3126
+ * @property {number} maxTries - Maximum tries that were allowed
3127
+ * @property {number} duration - Total execution time in milliseconds
3128
+ * @property {string} [response] - AI's final response if available
3082
3129
  *
3083
3130
  * @example
3084
3131
  * // Simple execution
3085
- * await client.act('Click the submit button');
3132
+ * const result = await client.act('Click the submit button');
3133
+ * console.log(result.success); // true
3134
+ *
3135
+ * @example
3136
+ * // With custom retry limit
3137
+ * const result = await client.act('Fill out the contact form', { tries: 10 });
3138
+ * console.log(`Completed in ${result.tries} tries`);
3086
3139
  *
3087
3140
  * @example
3088
- * // With validation loop
3089
- * const result = await client.act('Fill out the contact form', { validateAndLoop: true });
3090
- * console.log(result); // AI's final assessment
3141
+ * // Handle failures
3142
+ * try {
3143
+ * await client.act('Complete the checkout process', { tries: 3 });
3144
+ * } catch (error) {
3145
+ * console.log(`Failed after ${error.tries} tries: ${error.message}`);
3146
+ * }
3091
3147
  */
3092
- async act(task) {
3148
+ async act(task, options = {}) {
3093
3149
  this._ensureConnected();
3094
3150
 
3095
- this.analytics.track("sdk.act", { task });
3151
+ const { tries = 7 } = options;
3152
+
3153
+ this.analytics.track("sdk.act", { task, tries });
3154
+
3155
+ const { events } = require("./agent/events.js");
3156
+ const startTime = Date.now();
3096
3157
 
3097
- // Use the agent's exploratoryLoop method directly
3098
- return await this.agent.exploratoryLoop(task, false, true, false);
3158
+ // Store original checkLimit and set custom one if provided
3159
+ const originalCheckLimit = this.agent.checkLimit;
3160
+ this.agent.checkLimit = tries;
3161
+
3162
+ // Reset check count for this act() call
3163
+ const originalCheckCount = this.agent.checkCount;
3164
+ this.agent.checkCount = 0;
3165
+
3166
+ // Emit scoped start marker for act()
3167
+ this.emitter.emit(events.log.log, formatter.formatActStart(task));
3168
+
3169
+ try {
3170
+ // Use the agent's exploratoryLoop method directly
3171
+ const response = await this.agent.exploratoryLoop(task, false, true, false);
3172
+
3173
+ const duration = Date.now() - startTime;
3174
+ const triesUsed = this.agent.checkCount;
3175
+
3176
+ this.emitter.emit(events.log.log, formatter.formatActComplete(duration, true));
3177
+
3178
+ // Restore original checkLimit
3179
+ this.agent.checkLimit = originalCheckLimit;
3180
+ this.agent.checkCount = originalCheckCount;
3181
+
3182
+ return {
3183
+ success: true,
3184
+ task,
3185
+ tries: triesUsed,
3186
+ maxTries: tries,
3187
+ duration,
3188
+ response: response || undefined,
3189
+ };
3190
+ } catch (error) {
3191
+ const duration = Date.now() - startTime;
3192
+ const triesUsed = this.agent.checkCount;
3193
+
3194
+ this.emitter.emit(events.log.log, formatter.formatActComplete(duration, false, error.message));
3195
+
3196
+ // Restore original checkLimit
3197
+ this.agent.checkLimit = originalCheckLimit;
3198
+ this.agent.checkCount = originalCheckCount;
3199
+
3200
+ // Create an enhanced error with additional context using ActError class
3201
+ throw new ActError(`Act failed: ${error.message}`, {
3202
+ task,
3203
+ tries: triesUsed,
3204
+ maxTries: tries,
3205
+ duration,
3206
+ cause: error,
3207
+ });
3208
+ }
3099
3209
  }
3100
3210
 
3101
3211
  /**
@@ -3103,15 +3213,16 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
3103
3213
  * Execute a natural language task using AI
3104
3214
  *
3105
3215
  * @param {string} task - Natural language description of what to do
3106
- * @param {Object} options - Execution options
3107
- * @param {boolean} [options.validateAndLoop=false] - Whether to validate completion and retry if incomplete
3108
- * @returns {Promise<string|void>} Final AI response if validateAndLoop is true
3216
+ * @param {Object} [options] - Execution options
3217
+ * @param {number} [options.tries=7] - Maximum number of check/retry attempts
3218
+ * @returns {Promise<ActResult>} Result object with success status and details
3109
3219
  */
3110
- async ai(task) {
3111
- return await this.act(task);
3220
+ async ai(task, options) {
3221
+ return await this.act(task, options);
3112
3222
  }
3113
3223
  }
3114
3224
 
3115
3225
  module.exports = TestDriverSDK;
3116
3226
  module.exports.Element = Element;
3117
3227
  module.exports.ElementNotFoundError = ElementNotFoundError;
3228
+ module.exports.ActError = ActError;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * TestDriver SDK - Act Test (Vitest)
3
+ * Tests the AI exploratory loop (act) functionality
4
+ */
5
+
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
+
9
+ describe("Act Test", () => {
10
+ it("should use act to search for testdriver on Google", async (context) => {
11
+ const testdriver = TestDriver(context, { newSandbox: true });
12
+
13
+ // provision.chrome() automatically calls ready() and starts dashcam
14
+ await testdriver.provision.chrome({
15
+ url: 'https://www.google.com',
16
+ });
17
+
18
+ // Use act to search for testdriver
19
+ let actRes = await testdriver.act("click on the empty search box, type 'testdriver', and hit enter. do not click the plus button in the search bar");
20
+
21
+ console.log("Act response:", actRes);
22
+
23
+ // Assert the search results are displayed
24
+ const result = await testdriver.assert(
25
+ "search results for testdriver are visible",
26
+ );
27
+
28
+ expect(result).toBeTruthy();
29
+ });
30
+ });
@@ -8,7 +8,7 @@ import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
9
  describe("Assert Test", () => {
10
10
  it("should assert the testdriver login page shows", async (context) => {
11
- const testdriver = TestDriver(context, { newSandbox: true });
11
+ const testdriver = TestDriver(context, { newSandbox: true, headless: false });
12
12
 
13
13
  // provision.chrome() automatically calls ready() and starts dashcam
14
14
  await testdriver.provision.chrome({
@@ -8,7 +8,7 @@ import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
9
  describe("Hover Text Test", () => {
10
10
  it("should click Sign In and verify error message", async (context) => {
11
- const testdriver = TestDriver(context, { headless: true, newSandbox: true, cacheKey: 'hover-text-test' });
11
+ const testdriver = TestDriver(context, { headless: false, newSandbox: true, cacheKey: 'hover-text-test' });
12
12
  await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
13
13
 
14
14
  // Click on Sign In button using new find() API
@@ -6,7 +6,6 @@
6
6
  import crypto from "crypto";
7
7
  import { config } from "dotenv";
8
8
  import fs from "fs";
9
- import os from "os";
10
9
  import path, { dirname } from "path";
11
10
  import { fileURLToPath } from "url";
12
11
  import TestDriver from "../../../sdk.js";
@@ -51,85 +50,6 @@ console.log(
51
50
  process.env.TD_OS || "Not set (will default to linux)",
52
51
  );
53
52
 
54
- // Global test results storage
55
- const testResults = {
56
- tests: [],
57
- startTime: Date.now(),
58
- };
59
-
60
- /**
61
- * Store test result with dashcam URL
62
- * @param {string} testName - Name of the test
63
- * @param {string} testFile - Test file path
64
- * @param {string|null} dashcamUrl - Dashcam URL if available
65
- * @param {Object} sessionInfo - Session information
66
- */
67
- export function storeTestResult(
68
- testName,
69
- testFile,
70
- dashcamUrl,
71
- sessionInfo = {},
72
- ) {
73
-
74
- // Extract replay object ID from dashcam URL
75
- let replayObjectId = null;
76
- if (dashcamUrl) {
77
- const replayIdMatch = dashcamUrl.match(/\/replay\/([^?]+)/);
78
- replayObjectId = replayIdMatch ? replayIdMatch[1] : null;
79
- if (replayObjectId) {
80
- console.log(` Replay Object ID: ${replayObjectId}`);
81
- }
82
- }
83
-
84
- testResults.tests.push({
85
- name: testName,
86
- file: testFile,
87
- dashcamUrl,
88
- replayObjectId,
89
- sessionId: sessionInfo.sessionId,
90
- timestamp: new Date().toISOString(),
91
- });
92
- }
93
-
94
- /**
95
- * Get all test results
96
- * @returns {Object} All collected test results
97
- */
98
- export function getTestResults() {
99
- return {
100
- ...testResults,
101
- endTime: Date.now(),
102
- duration: Date.now() - testResults.startTime,
103
- };
104
- }
105
-
106
- /**
107
- * Save test results to a JSON file
108
- * @param {string} outputPath - Path to save the results
109
- */
110
- export function saveTestResults(outputPath = "test-results/sdk-summary.json") {
111
- const results = getTestResults();
112
- const dir = path.dirname(outputPath);
113
-
114
- // Create directory if it doesn't exist
115
- if (!fs.existsSync(dir)) {
116
- fs.mkdirSync(dir, { recursive: true });
117
- }
118
-
119
- fs.writeFileSync(outputPath, JSON.stringify(results, null, 2));
120
- console.log(`\n📊 Test results saved to: ${outputPath}`);
121
-
122
- // Also print dashcam URLs to console
123
- console.log("\n🎥 Dashcam URLs:");
124
- results.tests.forEach((test) => {
125
- if (test.dashcamUrl) {
126
- console.log(` ${test.name}: ${test.dashcamUrl}`);
127
- }
128
- });
129
-
130
- return results;
131
- }
132
-
133
53
  /**
134
54
  * Intercept console logs and forward to TestDriver sandbox
135
55
  * @param {TestDriver} client - TestDriver client instance
@@ -494,21 +414,9 @@ export async function teardownTest(client, options = {}) {
494
414
  console.log("⏭️ Postrun skipped (disabled in options)");
495
415
  }
496
416
 
497
- // Write test result to a file for the reporter to pick up (cross-process communication)
417
+ // Use Vitest's task.meta for cross-process communication with the reporter
498
418
  if (options.task) {
499
- const testResultFile = path.join(
500
- os.tmpdir(),
501
- "testdriver-results",
502
- `${options.task.id}.json`,
503
- );
504
-
505
419
  try {
506
- // Ensure directory exists
507
- const dir = path.dirname(testResultFile);
508
- if (!fs.existsSync(dir)) {
509
- fs.mkdirSync(dir, { recursive: true });
510
- }
511
-
512
420
  // Get test file path - make it relative to project root
513
421
  const absolutePath =
514
422
  options.task.file?.filepath || options.task.file?.name || "unknown";
@@ -523,33 +431,15 @@ export async function teardownTest(client, options = {}) {
523
431
  testOrder = options.task.suite.tasks.indexOf(options.task);
524
432
  }
525
433
 
526
- // Note: Duration is calculated by Vitest and passed via result.duration
527
- // We include it in the test result file so the reporter can use it
528
-
529
- // Get duration from Vitest result
530
- const result = options.task.result?.();
531
- const duration = result?.duration || 0;
532
-
533
- // Write test result with dashcam URL, platform, and metadata
534
- const testResult = {
535
- testId: options.task.id,
536
- testName: options.task.name,
537
- testFile: testFile,
538
- testOrder: testOrder,
539
- dashcamUrl: dashcamUrl,
540
- replayObjectId: dashcamUrl
541
- ? dashcamUrl.match(/\/replay\/([^?]+)/)?.[1]
542
- : null,
543
- platform: client.os, // Include platform from SDK client (source of truth)
544
- timestamp: Date.now(),
545
- duration: duration, // Include duration from Vitest
546
- };
547
-
548
- fs.writeFileSync(testResultFile, JSON.stringify(testResult, null, 2));
549
-
434
+ // Set metadata on task for the reporter to pick up
435
+ options.task.meta.dashcamUrl = dashcamUrl;
436
+ options.task.meta.platform = client.os; // Include platform from SDK client (source of truth)
437
+ options.task.meta.testFile = testFile;
438
+ options.task.meta.testOrder = testOrder;
439
+ options.task.meta.sessionId = client.getSessionId?.() || null;
550
440
  } catch (error) {
551
441
  console.error(
552
- `[TestHelpers] ❌ Failed to write test result file:`,
442
+ `[TestHelpers] ❌ Failed to set test metadata:`,
553
443
  error.message,
554
444
  );
555
445
  }
@@ -0,0 +1,33 @@
1
+ import { test, expect } from 'vitest';
2
+ import { TestDriver } from 'testdriverai/vitest/hooks';
3
+ import { login } from './login.js';
4
+
5
+ test('should login and add item to cart', async (context) => {
6
+
7
+ // Create TestDriver instance - automatically connects to sandbox
8
+ const testdriver = TestDriver(context);
9
+
10
+ // Launch chrome and navigate to demo app
11
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
12
+
13
+ // Use the login snippet to handle authentication
14
+ // This demonstrates how to reuse test logic across multiple tests
15
+ await login(testdriver);
16
+
17
+ // Add item to cart
18
+ const addToCartButton = await testdriver.find(
19
+ 'add to cart button under TestDriver Hat'
20
+ );
21
+ await addToCartButton.click();
22
+
23
+ // Open cart
24
+ const cartButton = await testdriver.find(
25
+ 'cart button in the top right corner'
26
+ );
27
+ await cartButton.click();
28
+
29
+ // Verify item in cart
30
+ const result = await testdriver.assert('TestDriver Hat is in the cart');
31
+ expect(result).toBeTruthy();
32
+
33
+ });
package/tests/login.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Login snippet - reusable login function
3
+ *
4
+ * This demonstrates how to create reusable test snippets that can be
5
+ * imported and used across multiple test files.
6
+ */
7
+ export async function login(testdriver) {
8
+
9
+ // The password is displayed on screen, have TestDriver extract it
10
+ const password = await testdriver.extract('the password');
11
+
12
+ // Find the username field
13
+ const usernameField = await testdriver.find(
14
+ 'Username, label above the username input field on the login form'
15
+ );
16
+ await usernameField.click();
17
+
18
+ // Type username
19
+ await testdriver.type('standard_user');
20
+
21
+ // Enter password form earlier
22
+ // Marked as secret so it's not logged or stored
23
+ await testdriver.pressKeys(['tab']);
24
+ await testdriver.type(password, { secret: true });
25
+
26
+ // Submit the form
27
+ await testdriver.find('submit button on the login form').click();
28
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import TestDriver from 'testdriverai/vitest';
3
+ import { config } from 'dotenv';
4
+
5
+ // Load environment variables from .env file
6
+ config();
7
+
8
+ export default defineConfig({
9
+ test: {
10
+ testTimeout: 300000,
11
+ hookTimeout: 300000,
12
+ reporters: [
13
+ 'default',
14
+ TestDriver(),
15
+ ],
16
+ setupFiles: ['testdriverai/vitest/setup'],
17
+ },
18
+ });
package/vitest.config.mjs CHANGED
@@ -10,8 +10,9 @@ export default defineConfig({
10
10
  test: {
11
11
  testTimeout: 900000,
12
12
  hookTimeout: 900000,
13
+ disableConsoleIntercept: true,
13
14
  reporters: [
14
- 'verbose',
15
+ 'default',
15
16
  TestDriver(),
16
17
  ['junit', { outputFile: 'test-report.junit.xml' }]
17
18
  ],