testdriverai 7.2.49 → 7.2.50

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
@@ -1781,6 +1781,8 @@ ${regression}
1781
1781
  ip: this.ip,
1782
1782
  });
1783
1783
 
1784
+ // Mark instance socket as connected so console logs are forwarded
1785
+ this.sandbox.instanceSocketConnected = true;
1784
1786
  this.emitter.emit(events.sandbox.connected);
1785
1787
 
1786
1788
  this.instance = instance.instance;
@@ -2020,6 +2022,7 @@ ${regression}
2020
2022
  resolution: this.config.TD_RESOLUTION,
2021
2023
  url: url,
2022
2024
  token: "V3b8wG9",
2025
+ testFile: this.testFile || null,
2023
2026
  };
2024
2027
 
2025
2028
  // Base64 encode the data (the debugger expects base64, not URL encoding)
@@ -2097,16 +2100,34 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2097
2100
  sandboxConfig.keepAlive = this.keepAlive;
2098
2101
  }
2099
2102
 
2100
- let instance = await this.sandbox.send(sandboxConfig, 60000 * 8);
2103
+ const { formatter } = require("../sdk-log-formatter.js");
2104
+ const retryDelay = 15000; // 15 seconds between retries
2101
2105
 
2102
- // Save the sandbox ID for reconnection with the correct OS type
2103
- if (instance.sandbox && instance.sandbox.sandboxId) {
2104
- this.saveLastSandboxId(instance.sandbox.sandboxId, this.sandboxOs);
2105
- } else if (instance.sandbox && instance.sandbox.instanceId) {
2106
- this.saveLastSandboxId(instance.sandbox.instanceId, this.sandboxOs);
2107
- }
2106
+ while (true) {
2107
+ let response = await this.sandbox.send(sandboxConfig, 60000 * 8);
2108
+
2109
+ // Check if queued (all slots in use)
2110
+ if (response.type === 'create.queued') {
2111
+ this.emitter.emit(
2112
+ events.log.narration,
2113
+ formatter.getPrefix("queue") + " " + theme.yellow.bold("Waiting") + " " +
2114
+ theme.dim(response.message),
2115
+ );
2108
2116
 
2109
- return instance;
2117
+ // Wait then retry
2118
+ await new Promise(resolve => setTimeout(resolve, retryDelay));
2119
+ continue;
2120
+ }
2121
+
2122
+ // Success - got a sandbox
2123
+ if (response.sandbox && response.sandbox.sandboxId) {
2124
+ this.saveLastSandboxId(response.sandbox.sandboxId, this.sandboxOs);
2125
+ } else if (response.sandbox && response.sandbox.instanceId) {
2126
+ this.saveLastSandboxId(response.sandbox.instanceId, this.sandboxOs);
2127
+ }
2128
+
2129
+ return response;
2130
+ }
2110
2131
  }
2111
2132
 
2112
2133
  async newSession() {
@@ -1,6 +1,7 @@
1
1
  <!doctype html>
2
2
  <html>
3
3
  <head>
4
+ <meta charset="UTF-8">
4
5
  <link rel="preconnect" href="https://fonts.googleapis.com" />
5
6
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
6
7
  <link
@@ -452,7 +453,29 @@
452
453
  eventHandlers.get(event).push(callback);
453
454
  };
454
455
 
456
+ // Activity tracking for title updates
457
+ let idleTimeout = null;
458
+ const IDLE_THRESHOLD = 5000; // 5 seconds
459
+
460
+ // Get test file name for title (use just filename, not full path)
461
+ const testFileName = parsedData?.testFile
462
+ ? parsedData.testFile.split('/').pop().split('\\\\').pop()
463
+ : 'TestDriver';
464
+
465
+ const setTitle = (status) => {
466
+ document.title = `[${status}] ${testFileName}`;
467
+ };
468
+
469
+ const resetIdleTimer = () => {
470
+ setTitle("Running");
471
+ if (idleTimeout) clearTimeout(idleTimeout);
472
+ idleTimeout = setTimeout(() => {
473
+ setTitle("Idle");
474
+ }, IDLE_THRESHOLD);
475
+ };
476
+
455
477
  ws.addEventListener("message", (message) => {
478
+ resetIdleTimer();
456
479
  try {
457
480
  const data = JSON.parse(message.data);
458
481
  console.log("WebSocket message received:", data);
@@ -488,6 +511,8 @@
488
511
 
489
512
  ws.addEventListener("close", () => {
490
513
  console.log("WebSocket disconnected");
514
+ if (idleTimeout) clearTimeout(idleTimeout);
515
+ setTitle("Done");
491
516
  document.getElementById("status").textContent = "Disconnected";
492
517
  document.getElementById("status").classList.add("visible");
493
518
  });
@@ -512,8 +537,50 @@
512
537
  vm: {
513
538
  show: "vm:show",
514
539
  },
540
+ test: {
541
+ start: "test:start",
542
+ stop: "test:stop",
543
+ success: "test:success",
544
+ error: "test:error",
545
+ },
546
+ error: {
547
+ fatal: "error:fatal",
548
+ general: "error:general",
549
+ sdk: "error:sdk",
550
+ },
515
551
  };
516
552
 
553
+ // Title state handlers for immediate feedback
554
+ addEventHandler(events.test.start, () => {
555
+ if (idleTimeout) clearTimeout(idleTimeout);
556
+ setTitle("Running");
557
+ });
558
+
559
+ addEventHandler(events.test.stop, () => {
560
+ if (idleTimeout) clearTimeout(idleTimeout);
561
+ setTitle("Stopped");
562
+ });
563
+
564
+ addEventHandler(events.test.success, () => {
565
+ if (idleTimeout) clearTimeout(idleTimeout);
566
+ setTitle("Passed");
567
+ });
568
+
569
+ addEventHandler(events.test.error, () => {
570
+ if (idleTimeout) clearTimeout(idleTimeout);
571
+ setTitle("Failed");
572
+ });
573
+
574
+ addEventHandler(events.error.fatal, () => {
575
+ if (idleTimeout) clearTimeout(idleTimeout);
576
+ setTitle("Error");
577
+ });
578
+
579
+ addEventHandler(events.error.sdk, () => {
580
+ if (idleTimeout) clearTimeout(idleTimeout);
581
+ setTitle("Error");
582
+ });
583
+
517
584
  const effects = document.getElementById("effects");
518
585
  const mouse = document.getElementById("mouse");
519
586
  const screenshotElement = document.getElementById("screenshot");
@@ -14,14 +14,17 @@ class CustomTransport extends Transport {
14
14
  }
15
15
 
16
16
  log(info, callback) {
17
+
17
18
  try {
18
19
  const { message } = info;
19
20
 
20
21
  if (!this.sandbox) {
21
22
  this.sandbox = require("../agent/lib/sandbox");
22
23
  }
23
-
24
+
24
25
  if (this.sandbox && this.sandbox.instanceSocketConnected) {
26
+
27
+
25
28
  if (typeof message === "object") {
26
29
  console.log(chalk.cyan("protecting against base64 error"));
27
30
  console.log(message);
@@ -706,7 +706,6 @@ class TestDriverReporter {
706
706
  startTime: pluginState.startTime,
707
707
  });
708
708
 
709
- logger.info(`Test run created: ${pluginState.testRunId}`);
710
709
  } catch (error) {
711
710
  logger.error("Failed to initialize:", error.message);
712
711
  pluginState.apiKey = null;
@@ -251,6 +251,9 @@ export function TestDriver(context, options = {}) {
251
251
  context.task.meta.testFile = testFile;
252
252
  context.task.meta.testOrder = 0;
253
253
 
254
+ // Pass test file name to SDK for debugger display
255
+ testdriver.testFile = testFile;
256
+
254
257
  // Auto-connect if enabled (default: true)
255
258
  const autoConnect = config.autoConnect !== undefined ? config.autoConnect : true;
256
259
  const debugConsoleSpy = process.env.TD_DEBUG_CONSOLE_SPY === 'true';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.2.49",
3
+ "version": "7.2.50",
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",
package/sdk.js CHANGED
@@ -428,28 +428,28 @@ class Element {
428
428
 
429
429
  // Use default cacheKey from SDK constructor if not provided in find() options
430
430
  // BUT only if cache is not explicitly disabled via cache: false option
431
- if (!cacheKey && this.sdk.options?.cacheKey && this.sdk.cacheThresholds?.find !== -1) {
431
+ if (!cacheKey && this.sdk.options?.cacheKey && !this.sdk._cacheExplicitlyDisabled) {
432
432
  cacheKey = this.sdk.options.cacheKey;
433
433
  }
434
434
 
435
435
  // Determine threshold:
436
- // - If cache is explicitly disabled (threshold = -1), don't use cache even with cacheKey
437
- // - If cacheKey is provided, enable cache (threshold = 0.01 or custom)
438
- // - If no cacheKey, disable cache (threshold = -1) unless explicitly overridden
436
+ // - If cache is explicitly disabled, don't use cache even with cacheKey
437
+ // - If cacheKey is provided, enable cache with threshold
438
+ // - If no cacheKey, disable cache
439
439
  let threshold;
440
- if (this.sdk.cacheThresholds?.find === -1) {
441
- // Cache explicitly disabled via cache: false option
440
+ if (this.sdk._cacheExplicitlyDisabled) {
441
+ // Cache explicitly disabled via cache: false option or TD_NO_CACHE env
442
442
  threshold = -1;
443
443
  cacheKey = null; // Clear any cacheKey to ensure cache is truly disabled
444
444
  } else if (cacheKey) {
445
445
  // cacheKey provided - enable cache with threshold
446
- threshold = cacheThreshold ?? 0.01;
446
+ threshold = cacheThreshold ?? this.sdk.cacheThresholds?.find ?? 0.01;
447
447
  } else if (cacheThreshold !== null) {
448
448
  // Explicit threshold provided without cacheKey
449
449
  threshold = cacheThreshold;
450
450
  } else {
451
- // No cacheKey, no explicit threshold - use global default (which is -1 now)
452
- threshold = this.sdk.cacheThresholds?.find ?? -1;
451
+ // No cacheKey, no explicit threshold - disable cache
452
+ threshold = -1;
453
453
  }
454
454
 
455
455
  // Store the threshold for debugging
@@ -1263,21 +1263,23 @@ class TestDriverSDK {
1263
1263
  // By default, cache is DISABLED (threshold = -1) to avoid unnecessary AI costs
1264
1264
  // To enable cache, provide a cacheKey when calling find() or findAll()
1265
1265
  // Also support TD_NO_CACHE environment variable and cache: false option for backwards compatibility
1266
- const cacheDisabled =
1266
+ const cacheExplicitlyDisabled =
1267
1267
  options.cache === false || process.env.TD_NO_CACHE === "true";
1268
1268
 
1269
- if (cacheDisabled) {
1269
+ // Track whether cache was explicitly disabled (not just default)
1270
+ this._cacheExplicitlyDisabled = cacheExplicitlyDisabled;
1271
+
1272
+ if (cacheExplicitlyDisabled) {
1270
1273
  // Explicit cache disabled via option or env var
1271
1274
  this.cacheThresholds = {
1272
1275
  find: -1,
1273
1276
  findAll: -1,
1274
1277
  };
1275
1278
  } else {
1276
- // Cache disabled by default, enabled only when cacheKey is provided
1277
- // Note: The threshold value here is the fallback when cacheKey is NOT provided
1279
+ // Cache enabled by default when cacheKey is provided
1278
1280
  this.cacheThresholds = {
1279
- find: options.cacheThreshold?.find ?? -1, // Default: cache disabled
1280
- findAll: options.cacheThreshold?.findAll ?? -1, // Default: cache disabled
1281
+ find: options.cacheThreshold?.find ?? 0.01, // Default: 1% threshold
1282
+ findAll: options.cacheThreshold?.findAll ?? 0.01,
1281
1283
  };
1282
1284
  }
1283
1285
 
@@ -2274,6 +2276,11 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2274
2276
  // Set redrawThreshold on agent's cliArgs.options
2275
2277
  this.agent.cliArgs.options.redrawThreshold = this.redrawThreshold;
2276
2278
 
2279
+ // Pass test file name to agent for debugger display
2280
+ if (this.testFile) {
2281
+ this.agent.testFile = this.testFile;
2282
+ }
2283
+
2277
2284
  // Use the agent's buildEnv method which handles all the connection logic
2278
2285
  await this.agent.buildEnv(buildEnvOptions);
2279
2286
 
@@ -2460,28 +2467,28 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2460
2467
 
2461
2468
  // Use default cacheKey from SDK constructor if not provided in findAll() options
2462
2469
  // BUT only if cache is not explicitly disabled via cache: false option
2463
- if (!cacheKey && this.options?.cacheKey && this.cacheThresholds?.findAll !== -1) {
2470
+ if (!cacheKey && this.options?.cacheKey && !this._cacheExplicitlyDisabled) {
2464
2471
  cacheKey = this.options.cacheKey;
2465
2472
  }
2466
2473
 
2467
2474
  // Determine threshold:
2468
- // - If cache is explicitly disabled (threshold = -1), don't use cache even with cacheKey
2469
- // - If cacheKey is provided, enable cache (threshold = 0.01 or custom)
2470
- // - If no cacheKey, disable cache (threshold = -1) unless explicitly overridden
2475
+ // - If cache is explicitly disabled, don't use cache even with cacheKey
2476
+ // - If cacheKey is provided, enable cache with threshold
2477
+ // - If no cacheKey, disable cache
2471
2478
  let threshold;
2472
- if (this.cacheThresholds?.findAll === -1) {
2473
- // Cache explicitly disabled via cache: false option
2479
+ if (this._cacheExplicitlyDisabled) {
2480
+ // Cache explicitly disabled via cache: false option or TD_NO_CACHE env
2474
2481
  threshold = -1;
2475
2482
  cacheKey = null; // Clear any cacheKey to ensure cache is truly disabled
2476
2483
  } else if (cacheKey) {
2477
2484
  // cacheKey provided - enable cache with threshold
2478
- threshold = cacheThreshold ?? 0.01;
2485
+ threshold = cacheThreshold ?? this.cacheThresholds?.findAll ?? 0.01;
2479
2486
  } else if (cacheThreshold !== null) {
2480
2487
  // Explicit threshold provided without cacheKey
2481
2488
  threshold = cacheThreshold;
2482
2489
  } else {
2483
- // No cacheKey, no explicit threshold - use global default (which is -1 now)
2484
- threshold = this.cacheThresholds?.findAll ?? -1;
2490
+ // No cacheKey, no explicit threshold - disable cache
2491
+ threshold = -1;
2485
2492
  }
2486
2493
 
2487
2494
  // Debug log threshold