testdriverai 7.1.2 → 7.1.4

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.
Files changed (38) hide show
  1. package/.github/workflows/test-init.yml +145 -0
  2. package/agent/lib/commander.js +2 -2
  3. package/agent/lib/commands.js +10 -5
  4. package/docs/docs.json +29 -84
  5. package/docs/v7/_drafts/migration.mdx +3 -3
  6. package/docs/v7/api/act.mdx +1 -1
  7. package/docs/v7/api/assert.mdx +1 -1
  8. package/docs/v7/api/assertions.mdx +14 -14
  9. package/docs/v7/getting-started/quickstart.mdx +11 -9
  10. package/docs/v7/getting-started/running-and-debugging.mdx +3 -2
  11. package/docs/v7/getting-started/writing-tests.mdx +4 -5
  12. package/docs/v7/overview/readme.mdx +1 -1
  13. package/docs/v7/presets/chrome.mdx +38 -41
  14. package/docs/v7/presets/electron.mdx +107 -100
  15. package/docs/v7/presets/webapp.mdx +40 -43
  16. package/interfaces/cli/commands/init.js +79 -21
  17. package/interfaces/vitest-plugin.mjs +36 -9
  18. package/lib/vitest/hooks.mjs +5 -1
  19. package/manual/test-init-command.js +223 -0
  20. package/package.json +2 -2
  21. package/schema.json +5 -5
  22. package/sdk-log-formatter.js +1 -1
  23. package/sdk.d.ts +8 -8
  24. package/sdk.js +64 -14
  25. package/test/testdriver/exec-output.test.mjs +1 -1
  26. package/test/testdriver/exec-pwsh.test.mjs +1 -1
  27. package/test/testdriver/focus-window.test.mjs +1 -1
  28. package/test/testdriver/hover-image.test.mjs +1 -1
  29. package/test/testdriver/hover-text-with-description.test.mjs +0 -3
  30. package/test/testdriver/match-image.test.mjs +1 -1
  31. package/test/testdriver/scroll-keyboard.test.mjs +1 -1
  32. package/test/testdriver/scroll-until-image.test.mjs +1 -1
  33. package/test/testdriver/scroll-until-text.test.mjs +18 -1
  34. package/test/testdriver/scroll.test.mjs +1 -1
  35. package/test/testdriver/setup/lifecycleHelpers.mjs +105 -5
  36. package/test/testdriver/setup/testHelpers.mjs +6 -2
  37. package/vitest.config.mjs +7 -2
  38. package/test/testdriver/exec-js.test.mjs +0 -43
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test Init Command
5
+ *
6
+ * This script tests the `testdriverai init` command by:
7
+ * 1. Creating a temporary test project
8
+ * 2. Running the init command
9
+ * 3. Verifying all files were created correctly
10
+ * 4. Running the generated test
11
+ * 5. Cleaning up
12
+ *
13
+ * Usage:
14
+ * node manual/test-init-command.js
15
+ *
16
+ * Requirements:
17
+ * - TD_API_KEY environment variable must be set
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const { execSync } = require('child_process');
23
+ const os = require('os');
24
+
25
+ // Colors for terminal output
26
+ const colors = {
27
+ reset: '\x1b[0m',
28
+ green: '\x1b[32m',
29
+ red: '\x1b[31m',
30
+ cyan: '\x1b[36m',
31
+ yellow: '\x1b[33m',
32
+ gray: '\x1b[90m',
33
+ };
34
+
35
+ function log(message, color = 'reset') {
36
+ console.log(`${colors[color]}${message}${colors.reset}`);
37
+ }
38
+
39
+ function success(message) {
40
+ log(`✓ ${message}`, 'green');
41
+ }
42
+
43
+ function error(message) {
44
+ log(`✗ ${message}`, 'red');
45
+ }
46
+
47
+ function info(message) {
48
+ log(message, 'cyan');
49
+ }
50
+
51
+ function step(message) {
52
+ log(`\n${message}`, 'cyan');
53
+ }
54
+
55
+ // Main test function
56
+ async function testInitCommand() {
57
+ // Check for API key
58
+ if (!process.env.TD_API_KEY) {
59
+ error('TD_API_KEY environment variable is required');
60
+ process.exit(1);
61
+ }
62
+
63
+ const testDir = path.join(os.tmpdir(), `test-init-${Date.now()}`);
64
+ const cliPath = path.join(__dirname, '..');
65
+
66
+ try {
67
+ step('📦 Creating test directory...');
68
+ fs.mkdirSync(testDir, { recursive: true });
69
+ success(`Created: ${testDir}`);
70
+
71
+ step('🔧 Setting up .env file...');
72
+ const envContent = `TD_API_KEY=${process.env.TD_API_KEY}\n`;
73
+ fs.writeFileSync(path.join(testDir, '.env'), envContent);
74
+ success('Created .env with API key');
75
+
76
+ step('🚀 Running init command...');
77
+ try {
78
+ execSync(`node ${path.join(cliPath, 'bin/testdriverai.js')} init`, {
79
+ cwd: testDir,
80
+ stdio: 'inherit',
81
+ env: { ...process.env, TD_API_KEY: process.env.TD_API_KEY }
82
+ });
83
+ success('Init command completed');
84
+ } catch (err) {
85
+ error('Init command failed');
86
+ throw err;
87
+ }
88
+
89
+ step('🔍 Verifying project structure...');
90
+
91
+ const expectedFiles = [
92
+ 'package.json',
93
+ 'vitest.config.js',
94
+ 'tests/example.test.js',
95
+ 'tests/login.js',
96
+ '.env',
97
+ '.gitignore',
98
+ '.github/workflows/testdriver.yml'
99
+ ];
100
+
101
+ for (const file of expectedFiles) {
102
+ const filePath = path.join(testDir, file);
103
+ if (!fs.existsSync(filePath)) {
104
+ error(`Missing file: ${file}`);
105
+ throw new Error(`Expected file not found: ${file}`);
106
+ }
107
+ success(`Found: ${file}`);
108
+ }
109
+
110
+ step('📝 Verifying vitest.config.js contents...');
111
+ const vitestConfig = fs.readFileSync(path.join(testDir, 'vitest.config.js'), 'utf8');
112
+
113
+ if (!vitestConfig.includes('TestDriver()')) {
114
+ error('TestDriver reporter not found in vitest.config.js');
115
+ console.log(vitestConfig);
116
+ throw new Error('TestDriver reporter not configured');
117
+ }
118
+ success('TestDriver reporter is configured');
119
+
120
+ if (!vitestConfig.includes('setupFiles') || !vitestConfig.includes('testdriverai/vitest/setup')) {
121
+ error('setupFiles not configured correctly');
122
+ console.log(vitestConfig);
123
+ throw new Error('setupFiles not configured');
124
+ }
125
+ success('setupFiles is configured correctly');
126
+
127
+ if (!vitestConfig.includes('reporters')) {
128
+ error('reporters array not found');
129
+ console.log(vitestConfig);
130
+ throw new Error('reporters not configured');
131
+ }
132
+ success('reporters array includes TestDriver');
133
+
134
+ step('📝 Verifying example test contents...');
135
+ const testFile = fs.readFileSync(path.join(testDir, 'tests/example.test.js'), 'utf8');
136
+
137
+ if (!testFile.includes('.provision.chrome')) {
138
+ error('Test does not use .provision.chrome');
139
+ console.log(testFile);
140
+ throw new Error('Test does not use .provision pattern');
141
+ }
142
+ success('Test uses .provision.chrome');
143
+
144
+ if (!testFile.includes("from 'testdriverai/vitest/hooks'")) {
145
+ error('Test does not import from testdriverai/vitest/hooks');
146
+ console.log(testFile);
147
+ throw new Error('Test does not import TestDriver from vitest/hooks');
148
+ }
149
+ success('Test imports TestDriver from vitest/hooks');
150
+
151
+ if (!testFile.includes("from './login.js'")) {
152
+ error('Test does not import login from ./login.js');
153
+ console.log(testFile);
154
+ throw new Error('Test does not import login snippet');
155
+ }
156
+ success('Test imports login snippet');
157
+
158
+ step('📝 Verifying login snippet contents...');
159
+ const loginFile = fs.readFileSync(path.join(testDir, 'tests/login.js'), 'utf8');
160
+
161
+ if (!loginFile.includes('.extract(')) {
162
+ error('Login snippet does not use .extract()');
163
+ console.log(loginFile);
164
+ throw new Error('Login snippet does not demonstrate .extract()');
165
+ }
166
+ success('Login snippet demonstrates .extract()');
167
+
168
+ if (!loginFile.includes('secret: true')) {
169
+ error('Login snippet does not use secret option');
170
+ console.log(loginFile);
171
+ throw new Error('Login snippet does not demonstrate secret typing');
172
+ }
173
+ success('Login snippet demonstrates secret typing');
174
+
175
+ step('🧪 Running generated test...');
176
+ try {
177
+ execSync('npm test', {
178
+ cwd: testDir,
179
+ stdio: 'inherit',
180
+ env: { ...process.env, TD_API_KEY: process.env.TD_API_KEY }
181
+ });
182
+ success('Test execution completed successfully');
183
+ } catch (err) {
184
+ error('Test execution failed');
185
+ throw err;
186
+ }
187
+
188
+ step('✅ All checks passed!');
189
+ log('\nTest summary:', 'green');
190
+ log(' • Init command executed successfully', 'green');
191
+ log(' • All expected files created', 'green');
192
+ log(' • Configuration files are correct', 'green');
193
+ log(' • Example test has proper patterns', 'green');
194
+ log(' • Generated test runs successfully', 'green');
195
+
196
+ } catch (err) {
197
+ step('❌ Test failed!');
198
+ error(err.message);
199
+ if (err.stack) {
200
+ log(err.stack, 'gray');
201
+ }
202
+ process.exit(1);
203
+ } finally {
204
+ // Optional: Clean up test directory
205
+ // Commented out so you can inspect the generated files
206
+ // step('🧹 Cleaning up...');
207
+ // if (fs.existsSync(testDir)) {
208
+ // fs.rmSync(testDir, { recursive: true, force: true });
209
+ // success('Cleaned up test directory');
210
+ // }
211
+
212
+ info(`\nTest project preserved at: ${testDir}`);
213
+ info('To clean up manually, run:');
214
+ log(` rm -rf ${testDir}`, 'gray');
215
+ }
216
+ }
217
+
218
+ // Run the test
219
+ testInitCommand().catch(err => {
220
+ error('Unexpected error:');
221
+ console.error(err);
222
+ process.exit(1);
223
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.1.2",
3
+ "version": "7.1.4",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "exports": {
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@eslint/js": "^9.10.0",
88
- "@vitest/ui": "^4.0.8",
88
+ "@vitest/ui": "^4.0.15",
89
89
  "chai": "^5.1.2",
90
90
  "esbuild": "0.20.2",
91
91
  "esbuild-plugin-fileloc": "^0.0.6",
package/schema.json CHANGED
@@ -618,7 +618,7 @@
618
618
  }
619
619
  }
620
620
  },
621
- "remember": {
621
+ "extract": {
622
622
  "type": "object",
623
623
  "required": [
624
624
  "description",
@@ -628,7 +628,7 @@
628
628
  "properties": {
629
629
  "command": {
630
630
  "type": "string",
631
- "const": "remember"
631
+ "const": "extract"
632
632
  },
633
633
  "description": {
634
634
  "type": "string"
@@ -783,7 +783,7 @@
783
783
  "scroll-until-text",
784
784
  "scroll-until-image",
785
785
  "focus-application",
786
- "remember",
786
+ "extract",
787
787
  "assert",
788
788
  "exec",
789
789
  "if",
@@ -971,12 +971,12 @@
971
971
  "if": {
972
972
  "properties": {
973
973
  "command": {
974
- "const": "remember"
974
+ "const": "extract"
975
975
  }
976
976
  }
977
977
  },
978
978
  "then": {
979
- "$ref": "#/$defs/commandsAvailable/remember"
979
+ "$ref": "#/$defs/commandsAvailable/extract"
980
980
  }
981
981
  },
982
982
  {
@@ -130,7 +130,7 @@ class SDKLogFormatter {
130
130
  // Validation
131
131
  assert: chalk.green("✅"),
132
132
  verify: chalk.green("🔍"),
133
- remember: chalk.blue("🧠"),
133
+ extract: chalk.blue("🧠"),
134
134
 
135
135
  // System
136
136
  connect: chalk.green("🔌"),
package/sdk.d.ts CHANGED
@@ -433,9 +433,9 @@ export interface FocusApplicationOptions {
433
433
  name: string;
434
434
  }
435
435
 
436
- /** Options for remember command */
437
- export interface RememberOptions {
438
- /** What to remember */
436
+ /** Options for extract command */
437
+ export interface ExtractOptions {
438
+ /** What to extract */
439
439
  description: string;
440
440
  }
441
441
 
@@ -919,15 +919,15 @@ export default class TestDriverSDK {
919
919
  assert(assertion: string, options?: object): Promise<boolean>;
920
920
 
921
921
  /**
922
- * Extract and remember information from the screen using AI
922
+ * Extract information from the screen using AI
923
923
  * @param options - Options object with description
924
924
  */
925
- remember(options: RememberOptions): Promise<string>;
925
+ extract(options: { description: string }): Promise<string>;
926
926
  /**
927
- * Extract and remember information from the screen using AI (positional arguments - legacy)
928
- * @param description - What to remember
927
+ * Extract information from the screen using AI (positional arguments - legacy)
928
+ * @param description - What to extract
929
929
  */
930
- remember(description: string): Promise<string>;
930
+ extract(description: string): Promise<string>;
931
931
 
932
932
  // Code Execution
933
933
 
package/sdk.js CHANGED
@@ -1,6 +1,3 @@
1
- // Auto-load environment variables from .env file if it exists
2
- require("dotenv").config();
3
-
4
1
  const fs = require("fs");
5
2
  const path = require("path");
6
3
  const os = require("os");
@@ -342,12 +339,12 @@ class Element {
342
339
  }
343
340
 
344
341
  // Determine threshold:
345
- // - If cacheKey is provided, enable cache (threshold = 0.05 or custom)
342
+ // - If cacheKey is provided, enable cache (threshold = 0.01 or custom)
346
343
  // - If no cacheKey, disable cache (threshold = -1) unless explicitly overridden
347
344
  let threshold;
348
345
  if (cacheKey) {
349
346
  // cacheKey provided - enable cache with threshold
350
- threshold = cacheThreshold ?? 0.05;
347
+ threshold = cacheThreshold ?? 0.01;
351
348
  } else if (cacheThreshold !== null) {
352
349
  // Explicit threshold provided without cacheKey
353
350
  threshold = cacheThreshold;
@@ -1258,11 +1255,65 @@ class TestDriverSDK {
1258
1255
  console.log('[provision.chrome] ✅ Dashcam started');
1259
1256
  }
1260
1257
 
1258
+ // Set up Chrome profile with preferences
1259
+ const shell = this.os === 'windows' ? 'pwsh' : 'sh';
1260
+ const userDataDir = this.os === 'windows'
1261
+ ? 'C:\\Users\\testdriver\\AppData\\Local\\TestDriver\\Chrome'
1262
+ : '/tmp/testdriver-chrome-profile';
1263
+
1264
+ // Create user data directory and Default profile directory
1265
+ const defaultProfileDir = this.os === 'windows'
1266
+ ? `${userDataDir}\\Default`
1267
+ : `${userDataDir}/Default`;
1268
+
1269
+ const createDirCmd = this.os === 'windows'
1270
+ ? `New-Item -ItemType Directory -Path "${defaultProfileDir}" -Force | Out-Null`
1271
+ : `mkdir -p "${defaultProfileDir}"`;
1272
+
1273
+ await this.exec(shell, createDirCmd, 10000, true);
1274
+
1275
+ // Write Chrome preferences
1276
+ const chromePrefs = {
1277
+ credentials_enable_service: false,
1278
+ profile: {
1279
+ password_manager_enabled: false,
1280
+ default_content_setting_values: {}
1281
+ },
1282
+ signin: {
1283
+ allowed: false
1284
+ },
1285
+ sync: {
1286
+ requested: false,
1287
+ first_setup_complete: true,
1288
+ sync_all_os_types: false
1289
+ },
1290
+ autofill: {
1291
+ enabled: false
1292
+ },
1293
+ local_state: {
1294
+ browser: {
1295
+ has_seen_welcome_page: true
1296
+ }
1297
+ }
1298
+ };
1299
+
1300
+ const prefsPath = this.os === 'windows'
1301
+ ? `${defaultProfileDir}\\Preferences`
1302
+ : `${defaultProfileDir}/Preferences`;
1303
+
1304
+ const prefsJson = JSON.stringify(chromePrefs, null, 2);
1305
+ const writePrefCmd = this.os === 'windows'
1306
+ ? `Set-Content -Path "${prefsPath}" -Value '${prefsJson.replace(/'/g, "''")}'`
1307
+ : `cat > "${prefsPath}" << 'EOF'\n${prefsJson}\nEOF`;
1308
+
1309
+ await this.exec(shell, writePrefCmd, 10000, true);
1310
+ console.log('[provision.chrome] ✅ Chrome preferences configured');
1311
+
1261
1312
  // Build Chrome launch command
1262
1313
  const chromeArgs = [];
1263
1314
  if (maximized) chromeArgs.push('--start-maximized');
1264
1315
  if (guest) chromeArgs.push('--guest');
1265
- chromeArgs.push('--disable-fre', '--no-default-browser-check', '--no-first-run');
1316
+ chromeArgs.push('--disable-fre', '--no-default-browser-check', '--no-first-run', '--disable-infobars', `--user-data-dir=${userDataDir}`);
1266
1317
 
1267
1318
  // Add dashcam-chrome extension on Linux
1268
1319
  if (this.os === 'linux') {
@@ -1270,7 +1321,6 @@ class TestDriverSDK {
1270
1321
  }
1271
1322
 
1272
1323
  // Launch Chrome
1273
- const shell = this.os === 'windows' ? 'pwsh' : 'sh';
1274
1324
 
1275
1325
  if (this.os === 'windows') {
1276
1326
  const argsString = chromeArgs.map(arg => `"${arg}"`).join(', ');
@@ -1668,12 +1718,12 @@ class TestDriverSDK {
1668
1718
  }
1669
1719
 
1670
1720
  // Determine threshold:
1671
- // - If cacheKey is provided, enable cache (threshold = 0.05 or custom)
1721
+ // - If cacheKey is provided, enable cache (threshold = 0.01 or custom)
1672
1722
  // - If no cacheKey, disable cache (threshold = -1) unless explicitly overridden
1673
1723
  let threshold;
1674
1724
  if (cacheKey) {
1675
1725
  // cacheKey provided - enable cache with threshold
1676
- threshold = cacheThreshold ?? 0.05;
1726
+ threshold = cacheThreshold ?? 0.01;
1677
1727
  } else if (cacheThreshold !== null) {
1678
1728
  // Explicit threshold provided without cacheKey
1679
1729
  threshold = cacheThreshold;
@@ -2051,15 +2101,15 @@ class TestDriverSDK {
2051
2101
  */
2052
2102
  doc: "Focus an application by name",
2053
2103
  },
2054
- remember: {
2055
- name: "remember",
2104
+ extract: {
2105
+ name: "extract",
2056
2106
  /**
2057
- * Extract and remember information from the screen using AI
2107
+ * Extract information from the screen using AI
2058
2108
  * @param {Object|string} options - Options object or description (legacy positional)
2059
- * @param {string} options.description - What to remember
2109
+ * @param {string} options.description - What to extract
2060
2110
  * @returns {Promise<string>}
2061
2111
  */
2062
- doc: "Extract and remember information from the screen",
2112
+ doc: "Extract information from the screen",
2063
2113
  },
2064
2114
  assert: {
2065
2115
  name: "assert",
@@ -6,7 +6,7 @@
6
6
  import { describe, expect, it } from "vitest";
7
7
  import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
- describe.skipIf(process.env.TD_OS === "linux")("Exec Output Test", () => {
9
+ describe.skip("Exec Output Test", () => {
10
10
  it(
11
11
  "should set date using PowerShell and navigate to calendar",
12
12
  async (context) => {
@@ -6,7 +6,7 @@
6
6
  import { describe, expect, it } from "vitest";
7
7
  import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
- describe.skipIf(process.env.TD_OS === "linux")("Exec PowerShell Test", () => {
9
+ describe.skip("Exec PowerShell Test", () => {
10
10
  it(
11
11
  "should generate random email using PowerShell and enter it",
12
12
  async (context) => {
@@ -7,7 +7,7 @@ import { describe, expect, it } from "vitest";
7
7
  import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
9
  describe("Focus Window Test", () => {
10
- it.skipIf(process.env.TD_OS === "linux")(
10
+ it.skip(
11
11
  "should click Microsoft Edge icon and focus Google Chrome",
12
12
  async (context) => {
13
13
  const testdriver = TestDriver(context, { headless: true });
@@ -9,7 +9,7 @@ import { performLogin } from "./setup/testHelpers.mjs";
9
9
 
10
10
  describe("Hover Image Test", () => {
11
11
  it("should click on shopping cart icon and verify empty cart", async (context) => {
12
- const testdriver = TestDriver(context, { headless: true });
12
+ const testdriver = TestDriver(context, { headless: false });
13
13
 
14
14
  // provision.chrome() automatically calls ready() and starts dashcam
15
15
  await testdriver.provision.chrome({
@@ -17,21 +17,18 @@ describe("Hover Text With Description Test", () => {
17
17
  await performLogin(testdriver);
18
18
 
19
19
  // Click on "Add to Cart" under TestDriver Hat
20
- await testdriver.focusApplication("Google Chrome");
21
20
  const addToCartButton = await testdriver.find(
22
21
  "Add to Cart, add to cart button under TestDriver Hat",
23
22
  );
24
23
  await addToCartButton.click();
25
24
 
26
25
  // Click on the cart
27
- await testdriver.focusApplication("Google Chrome");
28
26
  const cartButton = await testdriver.find(
29
27
  "Cart, cart button in the top right corner",
30
28
  );
31
29
  await cartButton.click();
32
30
 
33
31
  // Assert the TestDriver Hat is in the cart
34
- await testdriver.focusApplication("Google Chrome");
35
32
  const result = await testdriver.assert("TestDriver Hat is in the cart");
36
33
  expect(result).toBeTruthy();
37
34
  });
@@ -14,7 +14,7 @@ const __filename = fileURLToPath(import.meta.url);
14
14
  const __dirname = dirname(__filename);
15
15
 
16
16
  describe("Match Image Test", () => {
17
- it("should match shopping cart image and verify empty cart", async (context) => {
17
+ it.skip("should match shopping cart image and verify empty cart", async (context) => {
18
18
  const testdriver = TestDriver(context, { headless: true });
19
19
  await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
20
20
 
@@ -27,7 +27,7 @@ describe("Scroll Keyboard Test", () => {
27
27
  "The Hamster Dance, large heading at top of page",
28
28
  );
29
29
  await heading.click();
30
- await testdriver.scroll("down", 1000);
30
+ await testdriver.scroll("down", { amount: 1000 });
31
31
 
32
32
  // Assert the page is scrolled down
33
33
  const result = await testdriver.assert(
@@ -7,7 +7,7 @@ import { describe, expect, it } from "vitest";
7
7
  import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
8
 
9
9
  describe("Scroll Until Image Test", () => {
10
- it("should scroll until brown colored house image appears", async (context) => {
10
+ it.skip("should scroll until brown colored house image appears", async (context) => {
11
11
  const testdriver = TestDriver(context, { headless: true });
12
12
  await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
13
13
 
@@ -18,7 +18,24 @@ describe("Scroll Until Text Test", () => {
18
18
 
19
19
  // Scroll until text appears
20
20
  await testdriver.focusApplication("Google Chrome");
21
- await testdriver.scrollUntilText({ text: "testdriver socks", direction: "down" });
21
+ // Scroll until text appears
22
+ let found = false;
23
+ let scrollCount = 0;
24
+ const maxScrolls = 10;
25
+
26
+ while (!found && scrollCount < maxScrolls) {
27
+ const findResult = await testdriver.find("testdriver socks");
28
+ if (findResult) {
29
+ found = true;
30
+ } else {
31
+ await testdriver.scroll({ direction: "down" });
32
+ scrollCount++;
33
+ }
34
+ }
35
+
36
+ if (!found) {
37
+ throw new Error(`Failed to find "testdriver socks" after ${maxScrolls} scrolls`);
38
+ }
22
39
 
23
40
  // Assert testdriver socks appears on screen
24
41
  await testdriver.focusApplication("Google Chrome");
@@ -32,7 +32,7 @@ describe("Scroll Test", () => {
32
32
  await heading.click();
33
33
 
34
34
  // Scroll down
35
- await testdriver.scroll("down", 1000);
35
+ await testdriver.scroll("down", { amount: 1000 });
36
36
 
37
37
  // Assert page is scrolled
38
38
  const result = await testdriver.assert("the page is scrolled down, the hamster dance heading is not visible on the page");