testdriverai 7.2.55 → 7.2.57

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.
@@ -4,13 +4,15 @@ on:
4
4
  workflow_call:
5
5
  inputs:
6
6
  test_pattern:
7
- description: 'Test file pattern to run'
7
+ description: "Test file pattern to run"
8
8
  required: false
9
9
  type: string
10
- default: 'examples/*.test.mjs'
10
+ default: "examples/*.test.mjs"
11
11
  secrets:
12
12
  TD_API_KEY:
13
13
  required: true
14
+ TWOCAPTCHA_API_KEY:
15
+ required: true
14
16
  TD_WEBSITE:
15
17
  required: false
16
18
  AWS_ACCESS_KEY_ID:
@@ -21,48 +23,60 @@ on:
21
23
  jobs:
22
24
  test:
23
25
  runs-on: ubuntu-latest
24
-
26
+
25
27
  steps:
26
- - name: Checkout repository
27
- uses: actions/checkout@v4
28
- with:
29
- fetch-depth: 0
30
-
31
- - name: Set up Node.js
32
- uses: actions/setup-node@v4
33
- with:
34
- node-version: '20'
35
- cache: 'npm'
36
-
37
- - name: Install dependencies
38
- run: npm ci
39
-
40
- - name: Run Windows tests with self-hosted instances
41
- run: npx vitest run ${{ inputs.test_pattern }}
42
- env:
43
- TD_API_KEY: ${{ secrets.TD_API_KEY }}
44
- TD_WEBSITE: ${{ secrets.TD_WEBSITE }}
45
- TD_OS: windows
46
- AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
47
- AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
48
- AWS_REGION: us-east-2
49
- AWS_LAUNCH_TEMPLATE_ID: lt-0ef9bf26a945fb442
50
- AMI_ID: ami-0dd5fa241273a7d50
51
- RESOLUTION: 1920x1080
52
-
53
- - name: Upload test results
54
- if: always()
55
- uses: actions/upload-artifact@v4
56
- with:
57
- name: test-results-windows
58
- path: test-report.junit.xml
59
- retention-days: 30
60
-
61
- - name: Upload TestDriver AI CLI logs
62
- if: always()
63
- uses: actions/upload-artifact@v4
64
- with:
65
- name: testdriverai-cli-logs-windows
66
- path: /tmp/testdriverai-cli-*.log
67
- if-no-files-found: warn
68
- retention-days: 30
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 0
32
+
33
+ - name: Set up Node.js
34
+ uses: actions/setup-node@v4
35
+ with:
36
+ node-version: "20"
37
+ cache: "npm"
38
+
39
+ - name: Install dependencies
40
+ run: npm ci
41
+
42
+ - name: Debug Environment
43
+ run: |
44
+ echo "Checking environment variables..."
45
+ if [ -n "${{ secrets.TWOCAPTCHA_API_KEY }}" ]; then
46
+ echo "TWOCAPTCHA_API_KEY is set (length: ${#TWOCAPTCHA_API_KEY})"
47
+ else
48
+ echo "TWOCAPTCHA_API_KEY is NOT set"
49
+ fi
50
+ env:
51
+ TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
52
+
53
+ - name: Run Windows tests with self-hosted instances
54
+ run: npx vitest run ${{ inputs.test_pattern }}
55
+ env:
56
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
57
+ TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
58
+ TD_WEBSITE: ${{ secrets.TD_WEBSITE }}
59
+ TD_OS: windows
60
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
61
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
62
+ AWS_REGION: us-east-2
63
+ AWS_LAUNCH_TEMPLATE_ID: lt-0ef9bf26a945fb442
64
+ AMI_ID: ami-0dd5fa241273a7d50
65
+ RESOLUTION: 1920x1080
66
+
67
+ - name: Upload test results
68
+ if: always()
69
+ uses: actions/upload-artifact@v4
70
+ with:
71
+ name: test-results-windows
72
+ path: test-report.junit.xml
73
+ retention-days: 30
74
+
75
+ - name: Upload TestDriver AI CLI logs
76
+ if: always()
77
+ uses: actions/upload-artifact@v4
78
+ with:
79
+ name: testdriverai-cli-logs-windows
80
+ path: /tmp/testdriverai-cli-*.log
81
+ if-no-files-found: warn
82
+ retention-days: 30
package/docs/docs.json CHANGED
@@ -72,6 +72,7 @@
72
72
  "pages": [
73
73
  "/v7/ai",
74
74
  "/v7/assert",
75
+ "/v7/captcha",
75
76
  "/v7/click",
76
77
  "/v7/double-click",
77
78
  "/v7/exec",
@@ -0,0 +1,160 @@
1
+ ---
2
+ title: "captcha()"
3
+ sidebarTitle: "captcha"
4
+ description: "Solve captchas using 2captcha service"
5
+ icon: "shield-check"
6
+ ---
7
+
8
+ # Captcha Solving
9
+
10
+ TestDriver can automatically solve captchas during your tests using the 2captcha service. This feature works on both **Linux** and **Windows** sandboxes.
11
+
12
+ ## Quick Start
13
+
14
+
15
+ ```javascript
16
+ const result = await testdriver.captcha({
17
+ apiKey: process.env.TWOCAPTCHA_API_KEY,
18
+ });
19
+
20
+ console.log(result.success); // true
21
+ console.log(result.token); // The solved captcha token
22
+ ```
23
+
24
+ That's it! TestDriver will automatically:
25
+ - Detect the captcha type on the page
26
+ - Extract the sitekey
27
+ - Solve the captcha via 2captcha
28
+ - Inject the token into the page
29
+ - Trigger any callbacks
30
+
31
+ ## Supported Captcha Types
32
+
33
+ | Type | Auto-Detected | Notes |
34
+ |------|---------------|-------|
35
+ | reCAPTCHA v2 | ✅ | Including invisible |
36
+ | reCAPTCHA v3 | ✅ | Action is auto-detected |
37
+ | hCaptcha | ✅ | |
38
+ | Cloudflare Turnstile | ✅ | |
39
+
40
+ ## Getting a 2captcha API Key
41
+
42
+ 1. Sign up at [2captcha.com](https://2captcha.com)
43
+ 2. Add funds to your account
44
+ 3. Copy your API key from the dashboard
45
+
46
+ ## Configuration Options
47
+
48
+ ```javascript
49
+ const result = await testdriver.captcha({
50
+ // Required
51
+ apiKey: '2CAPTCHA_API_KEY',
52
+
53
+ // Optional - usually auto-detected
54
+ sitekey: '6Le...', // Override auto-detected sitekey
55
+ type: 'recaptcha_v3', // Override auto-detected type
56
+ action: 'submit', // reCAPTCHA v3 action
57
+
58
+ // Timing
59
+ timeout: 120000, // Max wait time (default: 120s)
60
+ pollInterval: 5000, // Poll interval (default: 5s)
61
+ });
62
+ ```
63
+
64
+ ## Full Example
65
+
66
+ ```javascript
67
+ import { describe, expect, it } from "vitest";
68
+ import { TestDriver } from "testdriver";
69
+
70
+ describe("Checkout flow", () => {
71
+ it("should complete checkout with captcha", async (context) => {
72
+ const testdriver = TestDriver(context);
73
+
74
+ // Navigate to checkout page
75
+ await testdriver.provision.chrome({
76
+ url: 'https://example.com/checkout',
77
+ });
78
+
79
+ // Fill out form
80
+ await testdriver.type({ text: 'John Doe', selector: '#name' });
81
+ await testdriver.type({ text: 'john@example.com', selector: '#email' });
82
+
83
+ // Solve the captcha
84
+ const result = await testdriver.captcha({
85
+ apiKey: process.env.TWOCAPTCHA_API_KEY,
86
+ });
87
+
88
+ expect(result.success).toBe(true);
89
+
90
+ // Submit the form
91
+ await testdriver.click({ selector: '#submit' });
92
+
93
+ // Verify success
94
+ await testdriver.find({ text: 'Order confirmed' });
95
+ }, 180000);
96
+ });
97
+ ```
98
+
99
+ ## Environment Variables
100
+
101
+ You can set your API key as an environment variable:
102
+
103
+ ```bash
104
+ export TWOCAPTCHA_API_KEY=your_api_key_here
105
+ ```
106
+
107
+ Then use it in your tests:
108
+
109
+ ```javascript
110
+ ```javascript
111
+ const result = await testdriver.captcha({
112
+ apiKey: process.env.TWOCAPTCHA_API_KEY,
113
+ });
114
+ ```
115
+
116
+ ### GitHub Actions
117
+
118
+ Add the key to your repository secrets and expose it in your workflow:
119
+
120
+ ```yaml
121
+ - name: Run Tests
122
+ run: npm test
123
+ env:
124
+ TWOCAPTCHA_API_KEY: ${{ secrets.TWOCAPTCHA_API_KEY }}
125
+ ```
126
+
127
+ ## How It Works
128
+
129
+ 1. **Detection**: Scans the page for captcha elements (`data-sitekey`, script tags, etc.)
130
+ 2. **Submit**: Sends the captcha challenge to 2captcha's solving service
131
+ 3. **Poll**: Waits for human solvers to complete the captcha
132
+ 4. **Inject**: Injects the solved token into the page's hidden fields
133
+ 5. **Callback**: Triggers any JavaScript callbacks the page expects
134
+
135
+ ## Troubleshooting
136
+
137
+ ### "Could not auto-detect captcha"
138
+
139
+ The captcha element wasn't found on the page. Try:
140
+ - Waiting for the page to fully load before calling `captcha()`
141
+ - Providing the `sitekey` and `type` manually
142
+
143
+ ### Timeout errors
144
+
145
+ Captcha solving typically takes 10-30 seconds. If you're getting timeouts:
146
+ - Increase the `timeout` option
147
+ - Check your 2captcha balance
148
+ - Verify the captcha type is correct
149
+
150
+ ### Token not working
151
+
152
+ Some sites validate tokens immediately. Make sure:
153
+ - The token is injected before form submission
154
+ - The captcha type matches what the site expects
155
+ - For reCAPTCHA v3, the `action` parameter matches the site's expected action
156
+
157
+ ## Requirements
158
+
159
+ - Chrome must be launched with remote debugging enabled (automatic on all sandboxes)
160
+ - A valid 2captcha API key with sufficient balance
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Test for testdriver.captcha() API
3
+ * Clean, simple API for solving captchas
4
+ */
5
+ import { describe, expect, it } from "vitest";
6
+ import { TestDriver } from "../lib/vitest/hooks.mjs";
7
+
8
+ console.log("DEBUG: process.env.TD_OS:", process.env.TD_OS);
9
+
10
+ describe("testdriver.captcha() API", () => {
11
+ it("should solve reCAPTCHA v3 with auto-detect", async (context) => {
12
+ const testdriver = TestDriver(context);
13
+
14
+ // Launch Chrome (remote debugging is enabled automatically on Linux)
15
+ await testdriver.provision.chrome({
16
+ url: "https://2captcha.com/demo/recaptcha-v3",
17
+ });
18
+
19
+ await testdriver.screenshot();
20
+
21
+ // Solve the captcha with just the API key - everything else is auto-detected!
22
+ const result = await testdriver.captcha({
23
+ apiKey: process.env.TWOCAPTCHA_API_KEY,
24
+ });
25
+
26
+ console.log("Captcha result:", result);
27
+ await testdriver.screenshot();
28
+
29
+ expect(result.success).toBe(true);
30
+ }, 180000);
31
+
32
+ it("should solve Cloudflare Turnstile", async (context) => {
33
+ const testdriver = TestDriver(context);
34
+
35
+ await testdriver.provision.chrome({
36
+ url: "https://2captcha.com/demo/cloudflare-turnstile",
37
+ });
38
+
39
+ await testdriver.screenshot();
40
+
41
+ const result = await testdriver.captcha({
42
+ apiKey: process.env.TWOCAPTCHA_API_KEY,
43
+ });
44
+
45
+ console.log("Turnstile result:", result);
46
+ await testdriver.screenshot();
47
+
48
+ expect(result.success).toBe(true);
49
+ }, 180000);
50
+ });
@@ -14,9 +14,7 @@ import { TestDriver } from "../lib/vitest/hooks.mjs";
14
14
  async function performLogin(client, username = "standard_user") {
15
15
  await client.focusApplication("Google Chrome");
16
16
  const password = await client.extract("the password");
17
- const usernameField = await client.find(
18
- "username input",
19
- );
17
+ const usernameField = await client.find("username input");
20
18
  await usernameField.click();
21
19
  await client.type(username);
22
20
  await client.pressKeys(["tab"]);
@@ -27,8 +25,13 @@ async function performLogin(client, username = "standard_user") {
27
25
 
28
26
  describe("Hover Text With Description Test", () => {
29
27
  it("should add TestDriver Hat to cart and verify", async (context) => {
30
- const testdriver = TestDriver(context, { ip: context.ip || process.env.TD_IP, headless: true });
31
- await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
28
+ const testdriver = TestDriver(context, {
29
+ ip: context.ip || process.env.TD_IP,
30
+ headless: true,
31
+ });
32
+ await testdriver.provision.chrome({
33
+ url: "http://testdriver-sandbox.vercel.app/login",
34
+ });
32
35
 
33
36
  //
34
37
  // Perform login first
@@ -47,7 +50,7 @@ describe("Hover Text With Description Test", () => {
47
50
  await cartButton.click();
48
51
 
49
52
  // Assert the TestDriver Hat is in the cart
50
- const result = await testdriver.assert("TestDriver Hat is in the cart");
53
+ const result = await testdriver.assert("There is an item in the cart");
51
54
  expect(result).toBeTruthy();
52
55
  });
53
56
  });
@@ -37,16 +37,22 @@ class InitCommand extends BaseCommand {
37
37
  if (fs.existsSync(envPath)) {
38
38
  const envContent = fs.readFileSync(envPath, "utf8");
39
39
  if (envContent.includes("TD_API_KEY=")) {
40
- console.log(chalk.gray("\n API key already configured in .env, skipping...\n"));
40
+ console.log(
41
+ chalk.gray("\n API key already configured in .env, skipping...\n"),
42
+ );
41
43
  return;
42
44
  }
43
45
  }
44
46
 
45
47
  console.log(chalk.cyan(" Setting up your TestDriver API key...\n"));
46
- console.log(chalk.gray(" Get your API key from: https://console.testdriver.ai/team"));
48
+ console.log(
49
+ chalk.gray(" Get your API key from: https://console.testdriver.ai/team"),
50
+ );
47
51
 
48
52
  // Ask if user wants to open the browser
49
- const shouldOpen = await this.askYesNo(" Open API keys page in browser? (Y/n): ");
53
+ const shouldOpen = await this.askYesNo(
54
+ " Open API keys page in browser? (Y/n): ",
55
+ );
50
56
  if (shouldOpen) {
51
57
  try {
52
58
  // Dynamic import for ES module
@@ -54,12 +60,16 @@ class InitCommand extends BaseCommand {
54
60
  await open("https://console.testdriver.ai/team");
55
61
  console.log(chalk.gray(" Opening browser...\n"));
56
62
  } catch (error) {
57
- console.log(chalk.yellow(" ⚠️ Could not open browser automatically\n"));
63
+ console.log(
64
+ chalk.yellow(" ⚠️ Could not open browser automatically\n"),
65
+ );
58
66
  }
59
67
  }
60
68
 
61
69
  // Prompt for API key with hidden input
62
- const apiKey = await this.promptHidden(" Enter your API key (input will be hidden): ");
70
+ const apiKey = await this.promptHidden(
71
+ " Enter your API key (input will be hidden): ",
72
+ );
63
73
 
64
74
  if (apiKey && apiKey.trim()) {
65
75
  // Save to .env
@@ -70,7 +80,11 @@ class InitCommand extends BaseCommand {
70
80
  fs.writeFileSync(envPath, envContent + `TD_API_KEY=${apiKey.trim()}\n`);
71
81
  console.log(chalk.green("\n ✓ API key saved to .env\n"));
72
82
  } else {
73
- console.log(chalk.yellow("\n ⚠️ No API key entered. You can add it later to .env:\n"));
83
+ console.log(
84
+ chalk.yellow(
85
+ "\n ⚠️ No API key entered. You can add it later to .env:\n",
86
+ ),
87
+ );
74
88
  console.log(chalk.gray(" TD_API_KEY=your_api_key\n"));
75
89
  }
76
90
  }
@@ -81,7 +95,7 @@ class InitCommand extends BaseCommand {
81
95
  async promptHidden(question) {
82
96
  return new Promise((resolve) => {
83
97
  process.stdout.write(question);
84
-
98
+
85
99
  const stdin = process.stdin;
86
100
  const wasRaw = stdin.isRaw;
87
101
  stdin.setRawMode(true);
@@ -131,7 +145,9 @@ class InitCommand extends BaseCommand {
131
145
  rl.question(question, (answer) => {
132
146
  rl.close();
133
147
  const normalized = answer.toLowerCase().trim();
134
- resolve(normalized === "" || normalized === "y" || normalized === "yes");
148
+ resolve(
149
+ normalized === "" || normalized === "y" || normalized === "yes",
150
+ );
135
151
  });
136
152
  });
137
153
  }
@@ -153,17 +169,20 @@ class InitCommand extends BaseCommand {
153
169
  scripts: {
154
170
  test: "vitest run",
155
171
  "test:watch": "vitest",
156
- "test:ui": "vitest --ui"
172
+ "test:ui": "vitest --ui",
157
173
  },
158
174
  keywords: ["testdriver", "testing", "e2e"],
159
175
  author: "",
160
176
  license: "ISC",
161
177
  engines: {
162
- node: ">=20.19.0"
163
- }
178
+ node: ">=20.19.0",
179
+ },
164
180
  };
165
181
 
166
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
182
+ fs.writeFileSync(
183
+ packageJsonPath,
184
+ JSON.stringify(packageJson, null, 2) + "\n",
185
+ );
167
186
  console.log(chalk.green(` Created package.json`));
168
187
  } else {
169
188
  console.log(chalk.gray(" package.json already exists, skipping..."));
@@ -249,7 +268,7 @@ test('should login and add item to cart', async (context) => {
249
268
  await cartButton.click();
250
269
 
251
270
  // Verify item in cart
252
- const result = await testdriver.assert('TestDriver Hat is in the cart');
271
+ const result = await testdriver.assert('There is an item in the cart');
253
272
  expect(result).toBeTruthy();
254
273
 
255
274
  });
@@ -283,7 +302,6 @@ export default defineConfig({
283
302
  fs.writeFileSync(configFile, configContent);
284
303
  console.log(chalk.green(` Created config file: ${configFile}`));
285
304
  }
286
-
287
305
  }
288
306
 
289
307
  /**
@@ -387,10 +405,13 @@ jobs:
387
405
  console.log(chalk.cyan("\n Installing dependencies...\n"));
388
406
 
389
407
  try {
390
- execSync("npm install -D vitest testdriverai@beta && npm install dotenv", {
391
- cwd: process.cwd(),
392
- stdio: "inherit"
393
- });
408
+ execSync(
409
+ "npm install -D vitest testdriverai@beta && npm install dotenv",
410
+ {
411
+ cwd: process.cwd(),
412
+ stdio: "inherit",
413
+ },
414
+ );
394
415
  console.log(chalk.green("\n Dependencies installed successfully!"));
395
416
  } catch (error) {
396
417
  console.log(
@@ -410,10 +431,16 @@ jobs:
410
431
  console.log(chalk.cyan("Next steps:\n"));
411
432
  console.log(" 1. Run your tests:");
412
433
  console.log(chalk.gray(" npx vitest run\n"));
413
- console.log(" 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets");
414
- console.log(chalk.gray(" Settings → Secrets → Actions → New repository secret\n"));
415
434
  console.log(
416
- chalk.cyan("Learn more at https://docs.testdriver.ai/v7/getting-started/\n"),
435
+ " 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets",
436
+ );
437
+ console.log(
438
+ chalk.gray(" Settings → Secrets → Actions → New repository secret\n"),
439
+ );
440
+ console.log(
441
+ chalk.cyan(
442
+ "Learn more at https://docs.testdriver.ai/v7/getting-started/\n",
443
+ ),
417
444
  );
418
445
  }
419
446
  }