testdriverai 7.2.56 → 7.2.58

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.
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Vitest Setup File for AWS Self-Hosted TestDriver Instances
3
- *
3
+ *
4
4
  * This setup file spawns a fresh AWS instance before each test
5
5
  * and terminates it after each test completes.
6
- *
6
+ *
7
7
  * Usage in vitest.config.mjs:
8
8
  * ```js
9
9
  * export default defineConfig({
@@ -15,7 +15,7 @@
15
15
  * },
16
16
  * });
17
17
  * ```
18
- *
18
+ *
19
19
  * Required environment variables:
20
20
  * - AWS_ACCESS_KEY_ID
21
21
  * - AWS_SECRET_ACCESS_KEY
@@ -24,10 +24,10 @@
24
24
  * - AMI_ID
25
25
  */
26
26
 
27
- import { execSync, spawn } from 'child_process';
28
- import { dirname, join } from 'path';
29
- import { fileURLToPath } from 'url';
30
- import { beforeEach } from 'vitest';
27
+ import { execSync, spawn } from "child_process";
28
+ import { dirname, join } from "path";
29
+ import { fileURLToPath } from "url";
30
+ import { beforeEach } from "vitest";
31
31
 
32
32
  const __filename = fileURLToPath(import.meta.url);
33
33
  const __dirname = dirname(__filename);
@@ -56,20 +56,23 @@ globalThis.__testdriverAWS = globalThis.__testdriverAWS || {
56
56
  execSync(
57
57
  `aws ec2 terminate-instances --region "${awsRegion}" --instance-ids "${instanceId}"`,
58
58
  {
59
- encoding: 'utf-8',
59
+ encoding: "utf-8",
60
60
  env: process.env,
61
- stdio: 'inherit',
62
- }
61
+ stdio: "inherit",
62
+ },
63
63
  );
64
64
 
65
65
  console.log(`[TestDriver] Instance terminated: ${instanceId}`);
66
66
  } catch (error) {
67
- console.error('[TestDriver] Failed to terminate instance:', error.message);
67
+ console.error(
68
+ "[TestDriver] Failed to terminate instance:",
69
+ error.message,
70
+ );
68
71
  // Don't throw - we don't want to fail the test because of cleanup issues
69
72
  } finally {
70
73
  testInstances.delete(testId);
71
74
  }
72
- }
75
+ },
73
76
  };
74
77
 
75
78
  /**
@@ -81,22 +84,27 @@ function cleanupAllInstances() {
81
84
  return;
82
85
  }
83
86
 
84
- console.log(`[TestDriver] Emergency cleanup: terminating ${testInstances.size} instance(s)`);
87
+ console.log(
88
+ `[TestDriver] Emergency cleanup: terminating ${testInstances.size} instance(s)`,
89
+ );
85
90
 
86
91
  for (const [testId, instanceInfo] of testInstances.entries()) {
87
92
  const { instanceId, awsRegion } = instanceInfo;
88
-
93
+
89
94
  try {
90
95
  console.log(`[TestDriver] Terminating instance: ${instanceId}`);
91
96
  execSync(
92
97
  `aws ec2 terminate-instances --region "${awsRegion}" --instance-ids "${instanceId}"`,
93
98
  {
94
- encoding: 'utf-8',
95
- stdio: 'inherit',
96
- }
99
+ encoding: "utf-8",
100
+ stdio: "inherit",
101
+ },
97
102
  );
98
103
  } catch (error) {
99
- console.error(`[TestDriver] Failed to terminate instance ${instanceId}:`, error.message);
104
+ console.error(
105
+ `[TestDriver] Failed to terminate instance ${instanceId}:`,
106
+ error.message,
107
+ );
100
108
  }
101
109
  }
102
110
 
@@ -104,26 +112,26 @@ function cleanupAllInstances() {
104
112
  }
105
113
 
106
114
  // Register cleanup handlers for various exit scenarios
107
- process.on('exit', cleanupAllInstances);
108
- process.on('SIGINT', () => {
109
- console.log('\n[TestDriver] Received SIGINT, cleaning up instances...');
115
+ process.on("exit", cleanupAllInstances);
116
+ process.on("SIGINT", () => {
117
+ console.log("\n[TestDriver] Received SIGINT, cleaning up instances...");
110
118
  cleanupAllInstances();
111
119
  // Don't call process.exit here - let the signal handler do its job
112
120
  });
113
- process.on('SIGTERM', () => {
114
- console.log('\n[TestDriver] Received SIGTERM, cleaning up instances...');
121
+ process.on("SIGTERM", () => {
122
+ console.log("\n[TestDriver] Received SIGTERM, cleaning up instances...");
115
123
  cleanupAllInstances();
116
124
  // Don't call process.exit here - let the signal handler do its job
117
125
  });
118
- process.on('uncaughtException', (error) => {
119
- console.error('[TestDriver] Uncaught exception:', error);
126
+ process.on("uncaughtException", (error) => {
127
+ console.error("[TestDriver] Uncaught exception:", error);
120
128
  cleanupAllInstances();
121
129
  // Don't call process.exit here - let Node.js handle the exception
122
130
  });
123
131
 
124
132
  beforeEach(async (context) => {
125
133
  // Only spawn if TD_OS=windows (indicates Windows self-hosted mode)
126
- if (process.env.TD_OS !== 'windows') {
134
+ if (process.env.TD_OS !== "windows") {
127
135
  return;
128
136
  }
129
137
 
@@ -135,61 +143,76 @@ beforeEach(async (context) => {
135
143
  }
136
144
 
137
145
  // Verify required parameters are available
146
+ if (process.env.TD_OS === "windows") {
147
+ console.log(
148
+ "[TestDriver] startup check: TWOCAPTCHA_API_KEY is " +
149
+ (process.env.TWOCAPTCHA_API_KEY ? "REQUIRED" : "MISSING"),
150
+ );
151
+ }
152
+
138
153
  if (!process.env.AWS_LAUNCH_TEMPLATE_ID || !process.env.AMI_ID) {
139
- throw new Error('[TestDriver] TD_OS=windows requires AWS_LAUNCH_TEMPLATE_ID and AMI_ID environment variables');
154
+ throw new Error(
155
+ "[TestDriver] TD_OS=windows requires AWS_LAUNCH_TEMPLATE_ID and AMI_ID environment variables",
156
+ );
140
157
  }
141
158
 
142
159
  // Check if AWS CLI is installed
143
160
  try {
144
- execSync('which aws', { stdio: 'ignore' });
161
+ execSync("which aws", { stdio: "ignore" });
145
162
  } catch (error) {
146
- throw new Error('[TestDriver] AWS CLI is not installed. Install it from https://aws.amazon.com/cli/');
163
+ throw new Error(
164
+ "[TestDriver] AWS CLI is not installed. Install it from https://aws.amazon.com/cli/",
165
+ );
147
166
  }
148
167
 
149
168
  const testId = context.task.id;
150
-
151
- console.log(`[TestDriver] Spawning AWS instance for test: ${context.task.name}`);
152
-
169
+
170
+ console.log(
171
+ `[TestDriver] Spawning AWS instance for test: ${context.task.name}`,
172
+ );
173
+
153
174
  try {
154
175
  // Find the spawn-runner.sh script (relative to this file)
155
- const spawnScriptPath = join(__dirname, '../../setup/aws/spawn-runner.sh');
156
-
176
+ const spawnScriptPath = join(__dirname, "../../setup/aws/spawn-runner.sh");
177
+
157
178
  // Execute spawn-runner.sh with live output streaming
158
179
  const output = await new Promise((resolve, reject) => {
159
- const child = spawn('bash', [spawnScriptPath], {
180
+ const child = spawn("bash", [spawnScriptPath], {
160
181
  env: {
161
182
  ...process.env,
162
- AWS_REGION: process.env.AWS_REGION || 'us-east-2',
163
- RESOLUTION: process.env.RESOLUTION || '1920x1080',
183
+ AWS_REGION: process.env.AWS_REGION || "us-east-2",
184
+ RESOLUTION: process.env.RESOLUTION || "1920x1080",
164
185
  },
165
186
  });
166
187
 
167
- let stdout = '';
168
- let stderr = '';
188
+ let stdout = "";
189
+ let stderr = "";
169
190
 
170
191
  // Stream stdout in real-time
171
- child.stdout.on('data', (data) => {
192
+ child.stdout.on("data", (data) => {
172
193
  const str = data.toString();
173
194
  process.stdout.write(str); // Show output immediately
174
195
  stdout += str;
175
196
  });
176
197
 
177
198
  // Stream stderr in real-time
178
- child.stderr.on('data', (data) => {
199
+ child.stderr.on("data", (data) => {
179
200
  const str = data.toString();
180
201
  process.stderr.write(str); // Show errors immediately
181
202
  stderr += str;
182
203
  });
183
204
 
184
- child.on('close', (code) => {
205
+ child.on("close", (code) => {
185
206
  if (code !== 0) {
186
- reject(new Error(`spawn-runner.sh exited with code ${code}\n${stderr}`));
207
+ reject(
208
+ new Error(`spawn-runner.sh exited with code ${code}\n${stderr}`),
209
+ );
187
210
  } else {
188
211
  resolve(stdout);
189
212
  }
190
213
  });
191
214
 
192
- child.on('error', (err) => {
215
+ child.on("error", (err) => {
193
216
  reject(err);
194
217
  });
195
218
  });
@@ -200,7 +223,7 @@ beforeEach(async (context) => {
200
223
  const awsRegionMatch = output.match(/AWS_REGION=(.+)/);
201
224
 
202
225
  if (!publicIpMatch || !instanceIdMatch || !awsRegionMatch) {
203
- throw new Error('Failed to parse spawn-runner.sh output');
226
+ throw new Error("Failed to parse spawn-runner.sh output");
204
227
  }
205
228
 
206
229
  const publicIp = publicIpMatch[1].trim();
@@ -215,7 +238,7 @@ beforeEach(async (context) => {
215
238
 
216
239
  console.log(`[TestDriver] Instance spawned: ${instanceId} at ${publicIp}`);
217
240
  } catch (error) {
218
- console.error('[TestDriver] Failed to spawn AWS instance:', error.message);
241
+ console.error("[TestDriver] Failed to spawn AWS instance:", error.message);
219
242
  throw error;
220
243
  }
221
244
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.2.56",
3
+ "version": "7.2.58",
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.d.ts CHANGED
@@ -216,7 +216,7 @@ export interface TestDriverOptions {
216
216
  /** Sandbox resolution (default: '1366x768') */
217
217
  resolution?: string;
218
218
  /** Operating system for the sandbox (default: 'linux') */
219
- os?: 'windows' | 'linux';
219
+ os?: "windows" | "linux";
220
220
  /** Enable analytics tracking (default: true) */
221
221
  analytics?: boolean;
222
222
  /** Enable console logging output (default: true) */
@@ -247,16 +247,18 @@ export interface TestDriverOptions {
247
247
  /** Enable/disable Dashcam video recording (default: true) */
248
248
  dashcam?: boolean;
249
249
  /** Redraw configuration for screen change detection */
250
- redraw?: boolean | {
251
- /** Enable redraw detection (default: true) */
252
- enabled?: boolean;
253
- /** Pixel difference threshold for redraw detection */
254
- diffThreshold?: number;
255
- /** Enable screen redraw detection */
256
- screenRedraw?: boolean;
257
- /** Enable network activity monitoring */
258
- networkMonitor?: boolean;
259
- };
250
+ redraw?:
251
+ | boolean
252
+ | {
253
+ /** Enable redraw detection (default: true) */
254
+ enabled?: boolean;
255
+ /** Pixel difference threshold for redraw detection */
256
+ diffThreshold?: number;
257
+ /** Enable screen redraw detection */
258
+ screenRedraw?: boolean;
259
+ /** Enable network activity monitoring */
260
+ networkMonitor?: boolean;
261
+ };
260
262
  /** @deprecated Use redraw.diffThreshold instead */
261
263
  redrawThreshold?: number | object;
262
264
  /** Additional environment variables */
@@ -277,7 +279,7 @@ export interface ConnectOptions {
277
279
  /** EC2 instance type for sandbox (e.g., 'i3.metal') */
278
280
  sandboxInstance?: string;
279
281
  /** Operating system for the sandbox (default: 'linux') */
280
- os?: 'windows' | 'linux';
282
+ os?: "windows" | "linux";
281
283
  /** Run in headless mode (default: false) */
282
284
  headless?: boolean;
283
285
  /** Reuse recent connection if available (default: true) */
@@ -498,6 +500,36 @@ export interface ExecOptions {
498
500
  silent?: boolean;
499
501
  }
500
502
 
503
+ /** Options for captcha command */
504
+ export interface CaptchaOptions {
505
+ /** 2captcha API key (required) */
506
+ apiKey: string;
507
+ /** Override auto-detected sitekey */
508
+ sitekey?: string;
509
+ /** Captcha type: 'recaptcha_v2', 'recaptcha_v3', 'hcaptcha', 'turnstile' */
510
+ type?: string;
511
+ /** reCAPTCHA v3 action (default: 'verify') */
512
+ action?: string;
513
+ /** Whether to auto-submit the form (default: true) */
514
+ autoSubmit?: boolean;
515
+ /** Polling interval in ms for 2captcha (default: 5000) */
516
+ pollInterval?: number;
517
+ /** Max time in ms to wait for solution (default: 120000) */
518
+ timeout?: number;
519
+ }
520
+
521
+ /** Result of captcha solving */
522
+ export interface CaptchaResult {
523
+ /** Whether the captcha was solved successfully */
524
+ success: boolean;
525
+ /** Success/error message */
526
+ message: string;
527
+ /** The solved captcha token */
528
+ token: string | null;
529
+ /** Raw output from the solver script */
530
+ output: string;
531
+ }
532
+
501
533
  /**
502
534
  * A Promise that resolves to an Element but also has chainable element methods.
503
535
  * This enables syntax like: await testdriver.find("button").click()
@@ -800,7 +832,7 @@ export default class TestDriverSDK {
800
832
  /**
801
833
  * The operating system of the sandbox
802
834
  */
803
- readonly os: 'windows' | 'linux';
835
+ readonly os: "windows" | "linux";
804
836
 
805
837
  /**
806
838
  * Provision API for launching applications
@@ -844,7 +876,7 @@ export default class TestDriverSDK {
844
876
  */
845
877
  getLastSandboxId(): {
846
878
  sandboxId: string | null;
847
- os: 'windows' | 'linux';
879
+ os: "windows" | "linux";
848
880
  ami: string | null;
849
881
  instanceType: string | null;
850
882
  timestamp: string | null;
@@ -879,7 +911,10 @@ export default class TestDriverSDK {
879
911
  * await element.click();
880
912
  */
881
913
  find(description: string, cacheThreshold?: number): ChainableElementPromise;
882
- find(description: string, options?: { cacheThreshold?: number; cacheKey?: string; timeout?: number }): ChainableElementPromise;
914
+ find(
915
+ description: string,
916
+ options?: { cacheThreshold?: number; cacheKey?: string; timeout?: number },
917
+ ): ChainableElementPromise;
883
918
 
884
919
  /**
885
920
  * Find all elements matching a description
@@ -924,20 +959,23 @@ export default class TestDriverSDK {
924
959
  * Type text
925
960
  * @param text - Text to type
926
961
  * @param options - Options object with delay and secret
927
- *
962
+ *
928
963
  * @example
929
964
  * // Type regular text
930
965
  * await client.type('hello world');
931
- *
966
+ *
932
967
  * @example
933
968
  * // Type a password securely (not logged or stored)
934
969
  * await client.type(process.env.TD_PASSWORD, { secret: true });
935
- *
970
+ *
936
971
  * @example
937
972
  * // Type with custom delay
938
973
  * await client.type('slow typing', { delay: 100 });
939
974
  */
940
- type(text: string | number, options?: { delay?: number; secret?: boolean }): Promise<void>;
975
+ type(
976
+ text: string | number,
977
+ options?: { delay?: number; secret?: boolean },
978
+ ): Promise<void>;
941
979
 
942
980
  /**
943
981
  * Wait for text to appear on screen
@@ -1081,7 +1119,10 @@ export default class TestDriverSDK {
1081
1119
  * @param direction - Direction to scroll (default: 'down')
1082
1120
  * @param options - Options object with amount
1083
1121
  */
1084
- scroll(direction?: ScrollDirection, options?: { amount?: number }): Promise<void>;
1122
+ scroll(
1123
+ direction?: ScrollDirection,
1124
+ options?: { amount?: number },
1125
+ ): Promise<void>;
1085
1126
 
1086
1127
  // Application Control
1087
1128
 
@@ -1112,6 +1153,12 @@ export default class TestDriverSDK {
1112
1153
  */
1113
1154
  extract(description: string): Promise<string>;
1114
1155
 
1156
+ /**
1157
+ * Solve a captcha on the current page using 2captcha service
1158
+ * @param options - Captcha solving options
1159
+ */
1160
+ captcha(options: CaptchaOptions): Promise<CaptchaResult>;
1161
+
1115
1162
  // Code Execution
1116
1163
 
1117
1164
  /**