testdriverai 7.2.91 → 7.2.92

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/index.js CHANGED
@@ -214,9 +214,6 @@ class TestDriverAgent extends EventEmitter2 {
214
214
  // Ignore sandbox close errors during exit
215
215
  }
216
216
  }
217
-
218
- // Clean up IDE session file
219
- this.cleanupIdeSessionFile();
220
217
 
221
218
  shouldRunPostrun =
222
219
  !this.hasRunPostrun &&
@@ -2042,8 +2039,8 @@ ${regression}
2042
2039
  const previewMode = this.config.TD_PREVIEW || "browser";
2043
2040
 
2044
2041
  if (previewMode === "ide") {
2045
- // Write session file for VSCode extension to pick up
2046
- this.writeIdeSessionFile(urlToOpen, data);
2042
+ // Send session to VS Code extension via HTTP
2043
+ this.sendIdeSessionNotification(urlToOpen, data);
2047
2044
  } else if (previewMode !== "none") {
2048
2045
  // Open in browser (default behavior)
2049
2046
  this.emitter.emit(events.showWindow, urlToOpen);
@@ -2052,64 +2049,135 @@ ${regression}
2052
2049
  }
2053
2050
  }
2054
2051
 
2055
- // Write session file for IDE preview mode
2056
- writeIdeSessionFile(debuggerUrl, data) {
2052
+ // Find the VS Code instance that contains the test file
2053
+ findTargetIdeInstance(testFilePath) {
2057
2054
  const fs = require("fs");
2058
2055
  const os = require("os");
2059
2056
  const path = require("path");
2060
2057
 
2061
- const sessionDir = path.join(os.homedir(), ".testdriver");
2062
- const sessionsDir = path.join(sessionDir, "ide-sessions");
2058
+ const instancesDir = path.join(os.homedir(), ".testdriver", "ide-instances");
2063
2059
 
2064
- // Generate a unique session ID based on test file and timestamp
2065
- const testFileName = (data.testFile || this.thisFile || "test")
2066
- .split(path.sep).pop()
2067
- .replace(/\.[^/.]+$/, ""); // Remove file extension
2068
- const sessionId = `${testFileName}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
2069
- const sessionFile = path.join(sessionsDir, `${sessionId}.json`);
2070
-
2071
- try {
2072
- // Ensure directories exist
2073
- if (!fs.existsSync(sessionDir)) {
2074
- fs.mkdirSync(sessionDir, { recursive: true });
2075
- }
2076
- if (!fs.existsSync(sessionsDir)) {
2077
- fs.mkdirSync(sessionsDir, { recursive: true });
2078
- }
2060
+ if (!fs.existsSync(instancesDir)) {
2061
+ return null;
2062
+ }
2079
2063
 
2080
- const sessionData = {
2081
- sessionId: sessionId,
2082
- debuggerUrl: debuggerUrl,
2083
- resolution: data.resolution || this.config.TD_RESOLUTION,
2084
- testFile: data.testFile || this.thisFile,
2085
- os: data.os || this.sandboxOs || "linux",
2086
- timestamp: Date.now(),
2087
- };
2064
+ const files = fs.readdirSync(instancesDir);
2065
+ const normalizedTestPath = testFilePath ? path.normalize(testFilePath) : null;
2066
+
2067
+ let matchingInstance = null;
2068
+ let longestMatchLength = 0;
2088
2069
 
2089
- fs.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2));
2090
- logger.log(`IDE session file written: ${sessionFile}`);
2070
+ for (const file of files) {
2071
+ if (!file.endsWith('.json')) continue;
2091
2072
 
2092
- // Store session file path for cleanup on exit
2093
- this._ideSessionFile = sessionFile;
2094
- } catch (error) {
2095
- logger.warn(`Failed to write IDE session file: ${error.message}`);
2096
- }
2097
- }
2098
-
2099
- // Clean up IDE session file when test completes
2100
- cleanupIdeSessionFile() {
2101
- if (this._ideSessionFile) {
2102
- const fs = require("fs");
2103
2073
  try {
2104
- if (fs.existsSync(this._ideSessionFile)) {
2105
- fs.unlinkSync(this._ideSessionFile);
2106
- logger.log(`IDE session file cleaned up: ${this._ideSessionFile}`);
2074
+ const registrationPath = path.join(instancesDir, file);
2075
+ const registration = JSON.parse(fs.readFileSync(registrationPath, 'utf-8'));
2076
+
2077
+ // Check if this instance is still alive (registration within last 60 seconds or process exists)
2078
+ const isRecent = Date.now() - registration.timestamp < 60000;
2079
+
2080
+ // Skip stale registrations
2081
+ if (!isRecent) {
2082
+ // Try to clean up stale file
2083
+ try { fs.unlinkSync(registrationPath); } catch {}
2084
+ continue;
2085
+ }
2086
+
2087
+ // If we have a test file path, find the best matching workspace
2088
+ if (normalizedTestPath && registration.workspacePaths) {
2089
+ for (const workspacePath of registration.workspacePaths) {
2090
+ const normalizedWorkspace = path.normalize(workspacePath);
2091
+ if (normalizedTestPath.startsWith(normalizedWorkspace + path.sep) ||
2092
+ normalizedTestPath === normalizedWorkspace) {
2093
+ // Prefer longest match (most specific workspace)
2094
+ if (normalizedWorkspace.length > longestMatchLength) {
2095
+ longestMatchLength = normalizedWorkspace.length;
2096
+ matchingInstance = registration;
2097
+ }
2098
+ }
2099
+ }
2100
+ } else if (!matchingInstance) {
2101
+ // If no test file path, just use the first available instance
2102
+ matchingInstance = registration;
2107
2103
  }
2108
2104
  } catch (error) {
2109
- // Ignore cleanup errors
2105
+ // Ignore malformed registration files
2110
2106
  }
2111
- this._ideSessionFile = null;
2112
2107
  }
2108
+
2109
+ return matchingInstance;
2110
+ }
2111
+
2112
+ // Send session notification to VS Code extension via HTTP
2113
+ sendIdeSessionNotification(debuggerUrl, data) {
2114
+ const http = require("http");
2115
+ const path = require("path");
2116
+
2117
+ const testFilePath = data.testFile || this.thisFile;
2118
+ const targetInstance = this.findTargetIdeInstance(testFilePath);
2119
+
2120
+ if (!targetInstance) {
2121
+ logger.warn("No VS Code instance found for IDE preview. Make sure VS Code with TestDriver extension is open.");
2122
+ // Fall back to browser
2123
+ this.emitter.emit(events.showWindow, debuggerUrl);
2124
+ return;
2125
+ }
2126
+
2127
+ // Generate a unique session ID
2128
+ const testFileName = (testFilePath || "test")
2129
+ .split(path.sep).pop()
2130
+ .replace(/\.[^/.]+$/, "");
2131
+ const sessionId = `${testFileName}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
2132
+
2133
+ const sessionData = {
2134
+ sessionId: sessionId,
2135
+ debuggerUrl: debuggerUrl,
2136
+ resolution: data.resolution || this.config.TD_RESOLUTION,
2137
+ testFile: testFilePath,
2138
+ os: data.os || this.sandboxOs || "linux",
2139
+ timestamp: Date.now(),
2140
+ };
2141
+
2142
+ const postData = JSON.stringify(sessionData);
2143
+
2144
+ const options = {
2145
+ hostname: '127.0.0.1',
2146
+ port: targetInstance.port,
2147
+ path: '/session',
2148
+ method: 'POST',
2149
+ headers: {
2150
+ 'Content-Type': 'application/json',
2151
+ 'Content-Length': Buffer.byteLength(postData)
2152
+ },
2153
+ timeout: 5000
2154
+ };
2155
+
2156
+ const req = http.request(options, (res) => {
2157
+ if (res.statusCode === 200) {
2158
+ logger.log(`IDE session notification sent to port ${targetInstance.port}`);
2159
+ } else {
2160
+ logger.warn(`IDE session notification failed with status ${res.statusCode}`);
2161
+ // Fall back to browser on failure
2162
+ this.emitter.emit(events.showWindow, debuggerUrl);
2163
+ }
2164
+ });
2165
+
2166
+ req.on('error', (error) => {
2167
+ logger.warn(`Failed to send IDE session notification: ${error.message}`);
2168
+ // Fall back to browser on error
2169
+ this.emitter.emit(events.showWindow, debuggerUrl);
2170
+ });
2171
+
2172
+ req.on('timeout', () => {
2173
+ req.destroy();
2174
+ logger.warn('IDE session notification timed out');
2175
+ // Fall back to browser on timeout
2176
+ this.emitter.emit(events.showWindow, debuggerUrl);
2177
+ });
2178
+
2179
+ req.write(postData);
2180
+ req.end();
2113
2181
  }
2114
2182
 
2115
2183
  async connectToSandboxService() {
@@ -8,7 +8,6 @@ const DEFAULT_REDRAW_OPTIONS = {
8
8
  enabled: true, // Master switch to enable/disable redraw detection
9
9
  screenRedraw: true, // Enable screen redraw detection
10
10
  networkMonitor: false, // Enable network activity monitoring
11
- noChangeTimeoutMs: 1500, // Exit early if no screen change detected after this time
12
11
  };
13
12
 
14
13
  // Factory function that creates redraw functionality with the provided system instance
@@ -242,7 +241,7 @@ const createRedraw = (
242
241
  }
243
242
 
244
243
  async function checkCondition(resolve, startTime, timeoutMs, options) {
245
- const { enabled, screenRedraw, networkMonitor, noChangeTimeoutMs = 1500 } = options;
244
+ const { enabled, screenRedraw, networkMonitor } = options;
246
245
 
247
246
  // If redraw is disabled, resolve immediately
248
247
  if (!enabled) {
@@ -260,9 +259,6 @@ const createRedraw = (
260
259
  let diffFromInitial = 0;
261
260
  let diffFromLast = 0;
262
261
  let isTimeout = timeElapsed > timeoutMs;
263
-
264
- // Early exit: if no screen change detected after noChangeTimeoutMs, assume action had no visual effect
265
- const noChangeTimeout = screenRedraw && !hasChangedFromInitial && timeElapsed > noChangeTimeoutMs;
266
262
 
267
263
  // Screen stability detection:
268
264
  // 1. Check if screen has changed from initial (detect transition)
@@ -291,14 +287,8 @@ const createRedraw = (
291
287
  lastScreenImage = nowImage;
292
288
  }
293
289
 
294
- // Screen is settled when:
295
- // 1. It has changed from initial AND consecutive frames are now stable, OR
296
- // 2. No change was detected after noChangeTimeoutMs (action had no visual effect)
297
- const screenSettled = (hasChangedFromInitial && consecutiveFramesStable) || noChangeTimeout;
298
-
299
- if (noChangeTimeout && !hasChangedFromInitial) {
300
- emitter.emit(events.log.debug, `[redraw] No screen change detected after ${noChangeTimeoutMs}ms, settling early`);
301
- }
290
+ // Screen is settled when it has changed from initial AND consecutive frames are now stable
291
+ const screenSettled = hasChangedFromInitial && consecutiveFramesStable;
302
292
 
303
293
  // If screen redraw is disabled, consider it as "settled"
304
294
  const effectiveScreenSettled = screenRedraw ? screenSettled : true;
@@ -355,7 +345,6 @@ const createRedraw = (
355
345
  networkSettled: effectiveNetworkSettled,
356
346
  isTimeout,
357
347
  timeElapsed,
358
- noChangeTimeout,
359
348
  });
360
349
  resolve("true");
361
350
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.2.91",
3
+ "version": "7.2.92",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",