testdriverai 7.2.2 → 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.
@@ -15,11 +15,9 @@
15
15
  * });
16
16
  */
17
17
 
18
- import fs from 'fs';
19
- import os from 'os';
18
+ import chalk from 'chalk';
20
19
  import path from 'path';
21
20
  import { vi } from 'vitest';
22
- import chalk from 'chalk';
23
21
  import TestDriverSDK from '../../sdk.js';
24
22
 
25
23
  /**
@@ -78,26 +76,30 @@ function setupConsoleSpy(client, taskId) {
78
76
  }
79
77
  };
80
78
 
79
+ // Store original console methods before spying
80
+ const originalLog = console.log.bind(console);
81
+ const originalError = console.error.bind(console);
82
+ const originalWarn = console.warn.bind(console);
83
+ const originalInfo = console.info.bind(console);
84
+
81
85
  // Create spies for each console method
82
86
  const logSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
83
- // Call through to original
84
- logSpy.mock.calls; // Track calls
85
- process.stdout.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
87
+ originalLog(...args); // Call original (Vitest will capture this)
86
88
  forwardToSandbox(args);
87
89
  });
88
90
 
89
91
  const errorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
90
- process.stderr.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
92
+ originalError(...args);
91
93
  forwardToSandbox(args);
92
94
  });
93
95
 
94
96
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation((...args) => {
95
- process.stderr.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
97
+ originalWarn(...args);
96
98
  forwardToSandbox(args);
97
99
  });
98
100
 
99
101
  const infoSpy = vi.spyOn(console, 'info').mockImplementation((...args) => {
100
- process.stdout.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
102
+ originalInfo(...args);
101
103
  forwardToSandbox(args);
102
104
  });
103
105
 
@@ -133,6 +135,7 @@ const lifecycleHandlers = new WeakMap();
133
135
  * @param {string} [options.apiKey] - TestDriver API key (defaults to process.env.TD_API_KEY)
134
136
  * @param {boolean} [options.headless] - Run sandbox in headless mode
135
137
  * @param {boolean} [options.newSandbox] - Create new sandbox
138
+ * @param {number} [options.timeout=0] - Sandbox timeout (TTL) in milliseconds. 0 = use provider default (5 min for E2B Linux)
136
139
  * @param {boolean} [options.autoConnect=true] - Automatically connect to sandbox
137
140
  * @returns {TestDriver} TestDriver client instance
138
141
  *
@@ -238,9 +241,9 @@ export function TestDriver(context, options = {}) {
238
241
  console.log('');
239
242
  console.log('🎥' + chalk.yellow(` Dashcam URL`) + `: ${dashcamUrl}`);
240
243
  console.log('');
241
- // Write test result to file for the reporter (cross-process communication)
242
- // This should happen regardless of whether dashcam succeeded, to ensure platform info is available
243
- const testId = context.task.id;
244
+
245
+ // Set test metadata directly on the Vitest task context
246
+ // This is the proper way to pass data from test to reporter
244
247
  const platform = testdriver.os || 'linux';
245
248
  const absolutePath = context.task.file?.filepath || context.task.file?.name || 'unknown';
246
249
  const projectRoot = process.cwd();
@@ -248,27 +251,16 @@ export function TestDriver(context, options = {}) {
248
251
  ? path.relative(projectRoot, absolutePath)
249
252
  : absolutePath;
250
253
 
251
- // Create results directory if it doesn't exist
252
- const resultsDir = path.join(os.tmpdir(), 'testdriver-results');
253
- if (!fs.existsSync(resultsDir)) {
254
- fs.mkdirSync(resultsDir, { recursive: true });
255
- }
256
-
257
- // Write test result file
258
- const testResultFile = path.join(resultsDir, `${testId}.json`);
259
- const testResult = {
260
- dashcamUrl: dashcamUrl || null,
261
- platform,
262
- testFile,
263
- testOrder: 0,
264
- sessionId: testdriver.getSessionId(),
265
- };
266
-
267
- fs.writeFileSync(testResultFile, JSON.stringify(testResult, null, 2));
254
+ // Set metadata on the task for the reporter to read
255
+ context.task.meta.dashcamUrl = dashcamUrl || null;
256
+ context.task.meta.platform = platform;
257
+ context.task.meta.testFile = testFile;
258
+ context.task.meta.testOrder = 0;
259
+ context.task.meta.sessionId = testdriver.getSessionId();
268
260
 
269
- // Also register in memory if plugin is available
261
+ // Also register in memory if plugin is available (for cross-process scenarios)
270
262
  if (globalThis.__testdriverPlugin?.registerDashcamUrl) {
271
- globalThis.__testdriverPlugin.registerDashcamUrl(testId, dashcamUrl, platform);
263
+ globalThis.__testdriverPlugin.registerDashcamUrl(context.task.id, dashcamUrl, platform);
272
264
  }
273
265
  } catch (error) {
274
266
  // Log more detailed error information for debugging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.2.2",
3
+ "version": "7.2.9",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "exports": {
@@ -52,6 +52,7 @@
52
52
  "@oclif/plugin-help": "^6.2.30",
53
53
  "@oclif/plugin-not-found": "^3.2.59",
54
54
  "@oclif/plugin-warn-if-update-available": "^3.1.43",
55
+ "@sentry/node": "^9.47.1",
55
56
  "@stoplight/yaml-ast-parser": "^0.0.50",
56
57
  "ajv": "^8.17.1",
57
58
  "arktype": "^2.1.19",
@@ -97,8 +98,8 @@
97
98
  "mocha": "^10.8.2",
98
99
  "node-addon-api": "^8.0.0",
99
100
  "prettier": "3.3.3",
100
- "testdriverai": "^6.1.11",
101
- "vitest": "^4.0.15"
101
+ "testdriverai": "^7.2.3",
102
+ "vitest": "^4.0.16"
102
103
  },
103
104
  "optionalDependencies": {
104
105
  "@esbuild/linux-x64": "^0.21.5"
@@ -878,6 +878,47 @@ class SDKLogFormatter {
878
878
 
879
879
  return `\n${parts.join(" ")}\n`;
880
880
  }
881
+
882
+ /**
883
+ * Format act() start message - provides visual scope boundary
884
+ * @param {string} task - The task being executed
885
+ * @returns {string} Formatted act start message
886
+ */
887
+ formatActStart(task) {
888
+ const parts = [];
889
+ this.addTimestamp(parts);
890
+ parts.push(this.getPrefix("action"));
891
+ parts.push(chalk.bold.cyan("Act"));
892
+ parts.push(chalk.cyan(`"${task}"`));
893
+ return parts.join(" ");
894
+ }
895
+
896
+ /**
897
+ * Format act() completion message - provides visual scope boundary
898
+ * @param {number} durationMs - Duration in milliseconds
899
+ * @param {boolean} success - Whether the act completed successfully
900
+ * @param {string} [error] - Error message if failed
901
+ * @returns {string} Formatted act complete message
902
+ */
903
+ formatActComplete(durationMs, success, error = null) {
904
+ const parts = [];
905
+ this.addTimestamp(parts);
906
+ parts.push(this.getResultPrefix());
907
+
908
+ if (success) {
909
+ parts.push(chalk.green("complete"));
910
+ } else {
911
+ parts.push(chalk.red("failed"));
912
+ if (error) {
913
+ parts.push(chalk.dim("·"));
914
+ parts.push(chalk.red(error));
915
+ }
916
+ }
917
+
918
+ parts.push(this.formatDurationColored(durationMs, "default"));
919
+
920
+ return parts.join(" ");
921
+ }
881
922
  }
882
923
 
883
924
  // Export singleton instance