testdriverai 7.0.0 → 7.1.0

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 (112) hide show
  1. package/AGENTS.md +550 -0
  2. package/CODEOWNERS +0 -1
  3. package/README.md +126 -0
  4. package/agent/index.js +43 -18
  5. package/agent/lib/commands.js +794 -135
  6. package/agent/lib/redraw.js +124 -39
  7. package/agent/lib/sandbox.js +10 -1
  8. package/agent/lib/sdk.js +21 -0
  9. package/docs/MIGRATION.md +425 -0
  10. package/docs/PRESETS.md +210 -0
  11. package/docs/docs.json +91 -37
  12. package/docs/guide/best-practices-polling.mdx +154 -0
  13. package/docs/v7/api/dashcam.mdx +497 -0
  14. package/docs/v7/api/doubleClick.mdx +102 -0
  15. package/docs/v7/api/mouseDown.mdx +161 -0
  16. package/docs/v7/api/mouseUp.mdx +164 -0
  17. package/docs/v7/api/rightClick.mdx +123 -0
  18. package/docs/v7/getting-started/configuration.mdx +380 -0
  19. package/docs/v7/getting-started/quickstart.mdx +273 -140
  20. package/docs/v7/guides/best-practices.mdx +486 -0
  21. package/docs/v7/guides/caching-ai.mdx +215 -0
  22. package/docs/v7/guides/caching-selectors.mdx +292 -0
  23. package/docs/v7/guides/caching.mdx +366 -0
  24. package/docs/v7/guides/ci-cd/azure.mdx +587 -0
  25. package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
  26. package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
  27. package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
  28. package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
  29. package/docs/v7/guides/ci-cd/travis.mdx +438 -0
  30. package/docs/v7/guides/debugging.mdx +349 -0
  31. package/docs/v7/guides/faq.mdx +393 -0
  32. package/docs/v7/guides/performance.mdx +517 -0
  33. package/docs/v7/guides/troubleshooting.mdx +526 -0
  34. package/docs/v7/guides/vitest-plugin.mdx +477 -0
  35. package/docs/v7/guides/vitest.mdx +535 -0
  36. package/docs/v7/platforms/linux.mdx +308 -0
  37. package/docs/v7/platforms/macos.mdx +433 -0
  38. package/docs/v7/platforms/windows.mdx +430 -0
  39. package/docs/v7/presets/chrome-extension.mdx +223 -0
  40. package/docs/v7/presets/chrome.mdx +287 -0
  41. package/docs/v7/presets/electron.mdx +435 -0
  42. package/docs/v7/presets/vscode.mdx +398 -0
  43. package/docs/v7/presets/webapp.mdx +396 -0
  44. package/docs/v7/progressive-apis/CORE.md +459 -0
  45. package/docs/v7/progressive-apis/HOOKS.md +360 -0
  46. package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
  47. package/docs/v7/progressive-apis/PROVISION.md +266 -0
  48. package/interfaces/vitest-plugin.mjs +186 -100
  49. package/package.json +12 -1
  50. package/sdk.d.ts +335 -42
  51. package/sdk.js +756 -95
  52. package/src/core/Dashcam.js +469 -0
  53. package/src/core/index.d.ts +150 -0
  54. package/src/core/index.js +12 -0
  55. package/src/presets/index.mjs +331 -0
  56. package/src/vitest/extended.mjs +108 -0
  57. package/src/vitest/hooks.d.ts +119 -0
  58. package/src/vitest/hooks.mjs +298 -0
  59. package/src/vitest/index.mjs +64 -0
  60. package/src/vitest/lifecycle.mjs +277 -0
  61. package/src/vitest/utils.mjs +150 -0
  62. package/test/dashcam.test.js +137 -0
  63. package/testdriver/acceptance-sdk/assert.test.mjs +13 -31
  64. package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
  65. package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
  66. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +7 -19
  67. package/testdriver/acceptance-sdk/element-not-found.test.mjs +6 -19
  68. package/testdriver/acceptance-sdk/exec-js.test.mjs +6 -18
  69. package/testdriver/acceptance-sdk/exec-output.test.mjs +8 -20
  70. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +13 -25
  71. package/testdriver/acceptance-sdk/focus-window.test.mjs +8 -20
  72. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +5 -20
  73. package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
  74. package/testdriver/acceptance-sdk/hover-image.test.mjs +10 -19
  75. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +7 -19
  76. package/testdriver/acceptance-sdk/hover-text.test.mjs +5 -19
  77. package/testdriver/acceptance-sdk/match-image.test.mjs +7 -19
  78. package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
  79. package/testdriver/acceptance-sdk/press-keys.test.mjs +5 -19
  80. package/testdriver/acceptance-sdk/prompt.test.mjs +6 -18
  81. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +6 -20
  82. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +6 -18
  83. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +9 -23
  84. package/testdriver/acceptance-sdk/scroll.test.mjs +12 -21
  85. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +124 -352
  86. package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
  87. package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
  88. package/testdriver/acceptance-sdk/type.test.mjs +19 -58
  89. package/vitest.config.mjs +1 -0
  90. package/.vscode/mcp.json +0 -9
  91. package/MIGRATION.md +0 -389
  92. package/PLUGIN_MIGRATION.md +0 -222
  93. package/PROMPT_CACHE.md +0 -200
  94. package/SDK_LOGGING.md +0 -222
  95. package/SDK_MIGRATION.md +0 -474
  96. package/SDK_README.md +0 -1122
  97. package/debug-screenshot-1763401388589.png +0 -0
  98. package/examples/run-tests-with-recording.sh +0 -70
  99. package/examples/screenshot-example.js +0 -63
  100. package/examples/sdk-awesome-logs-demo.js +0 -177
  101. package/examples/sdk-cache-thresholds.js +0 -96
  102. package/examples/sdk-element-properties.js +0 -155
  103. package/examples/sdk-simple-example.js +0 -65
  104. package/examples/test-recording-example.test.js +0 -166
  105. package/mcp-server/AI_GUIDELINES.md +0 -57
  106. package/test-find-api.js +0 -73
  107. package/test-prompt-cache.js +0 -96
  108. package/test-sandbox-render.js +0 -28
  109. package/test-sdk-methods.js +0 -15
  110. package/test-sdk-refactor.js +0 -53
  111. package/test-stack-trace.mjs +0 -57
  112. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Utility Functions for TestDriver Vitest Plugin
3
+ *
4
+ * General-purpose utilities for testing with TestDriver.
5
+ *
6
+ * @example
7
+ * import { retryAsync, setupEventLogging } from 'testdriverai/vitest';
8
+ */
9
+
10
+ /**
11
+ * Set up detailed event logging for debugging
12
+ * @param {TestDriver} client - TestDriver client
13
+ */
14
+ export function setupEventLogging(client) {
15
+ const emitter = client.getEmitter();
16
+
17
+ // Log all events
18
+ emitter.on("**", function (data) {
19
+ const event = this.event;
20
+ if (event.startsWith("log:debug")) return; // Skip debug logs
21
+ console.log(`[EVENT] ${event}`, data || "");
22
+ });
23
+
24
+ // Log command lifecycle
25
+ emitter.on("command:start", (data) => {
26
+ console.log("🚀 Command started:", data);
27
+ });
28
+
29
+ emitter.on("command:success", (data) => {
30
+ console.log("✅ Command succeeded:", data);
31
+ });
32
+
33
+ emitter.on("command:error", (data) => {
34
+ console.error("❌ Command error:", data);
35
+ });
36
+
37
+ // Log sandbox events
38
+ emitter.on("sandbox:connected", () => {
39
+ console.log("🔌 Sandbox connected");
40
+ });
41
+
42
+ emitter.on("sandbox:authenticated", () => {
43
+ console.log("🔐 Sandbox authenticated");
44
+ });
45
+
46
+ emitter.on("sandbox:error", (error) => {
47
+ console.error("⚠️ Sandbox error:", error);
48
+ });
49
+
50
+ // Log SDK API calls
51
+ emitter.on("sdk:request", (data) => {
52
+ console.log("📤 SDK Request:", data);
53
+ });
54
+
55
+ emitter.on("sdk:response", (data) => {
56
+ console.log("📥 SDK Response:", data);
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Retry an async function with configurable attempts and delay
62
+ * @param {Function} fn - Async function to retry
63
+ * @param {number} retries - Number of retries (default: 3)
64
+ * @param {number} delay - Delay between retries in ms (default: 1000)
65
+ * @returns {Promise} Result of successful execution
66
+ * @throws {Error} Last error if all retries fail
67
+ *
68
+ * @example
69
+ * const result = await retryAsync(
70
+ * () => testdriver.find("Flaky Element"),
71
+ * 5,
72
+ * 2000
73
+ * );
74
+ */
75
+ export async function retryAsync(fn, retries = 3, delay = 1000) {
76
+ let lastError;
77
+
78
+ for (let i = 0; i < retries; i++) {
79
+ try {
80
+ return await fn();
81
+ } catch (error) {
82
+ lastError = error;
83
+ if (i < retries - 1) {
84
+ await new Promise((resolve) => setTimeout(resolve, delay));
85
+ }
86
+ }
87
+ }
88
+
89
+ throw lastError;
90
+ }
91
+
92
+ /**
93
+ * Wait for a condition to be true
94
+ * @param {Function} condition - Function that returns boolean or Promise<boolean>
95
+ * @param {object} options - Wait options
96
+ * @param {number} options.timeout - Maximum time to wait in ms (default: 30000)
97
+ * @param {number} options.interval - Poll interval in ms (default: 500)
98
+ * @param {string} options.message - Error message if timeout (default: "Condition not met")
99
+ * @returns {Promise<boolean>} True if condition met
100
+ * @throws {Error} If timeout reached
101
+ *
102
+ * @example
103
+ * await waitFor(() => element.exists(), { timeout: 10000 });
104
+ */
105
+ export async function waitFor(condition, options = {}) {
106
+ const {
107
+ timeout = 30000,
108
+ interval = 500,
109
+ message = "Condition not met within timeout"
110
+ } = options;
111
+
112
+ const startTime = Date.now();
113
+
114
+ while (Date.now() - startTime < timeout) {
115
+ try {
116
+ const result = await condition();
117
+ if (result) {
118
+ return true;
119
+ }
120
+ } catch (error) {
121
+ // Condition threw, keep polling
122
+ }
123
+ await new Promise(resolve => setTimeout(resolve, interval));
124
+ }
125
+
126
+ throw new Error(`${message} (timeout: ${timeout}ms)`);
127
+ }
128
+
129
+ /**
130
+ * Sleep for a specified duration
131
+ * @param {number} ms - Milliseconds to sleep
132
+ * @returns {Promise<void>}
133
+ *
134
+ * @example
135
+ * await sleep(1000); // Wait 1 second
136
+ */
137
+ export function sleep(ms) {
138
+ return new Promise(resolve => setTimeout(resolve, ms));
139
+ }
140
+
141
+ /**
142
+ * Generate a unique test ID
143
+ * @param {string} prefix - Optional prefix for the ID
144
+ * @returns {string} Unique ID
145
+ */
146
+ export function generateTestId(prefix = "test") {
147
+ const timestamp = Date.now().toString(36);
148
+ const random = Math.random().toString(36).substring(2, 8);
149
+ return `${prefix}-${timestamp}-${random}`;
150
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Dashcam Class - Unit Tests
3
+ * Tests for the new Dashcam class
4
+ */
5
+
6
+ const { Dashcam } = require('../src/core');
7
+
8
+ // Mock TestDriver client
9
+ class MockTestDriver {
10
+ constructor(os = 'linux') {
11
+ this.os = os;
12
+ this.commands = [];
13
+ }
14
+
15
+ async exec(shell, command, timeout, silent) {
16
+ this.commands.push({ shell, command, timeout, silent });
17
+
18
+ // Mock responses
19
+ if (command.includes('npm prefix -g')) {
20
+ return this.os === 'windows'
21
+ ? 'C:\\Users\\testdriver\\AppData\\Roaming\\npm'
22
+ : '/usr/local';
23
+ }
24
+
25
+ if (command.includes('stop')) {
26
+ return 'Recording stopped\nReplay URL: https://app.testdriver.ai/replay/abc123?share=xyz';
27
+ }
28
+
29
+ return 'OK';
30
+ }
31
+
32
+ wait(ms) {
33
+ return new Promise(resolve => setTimeout(resolve, ms));
34
+ }
35
+ }
36
+
37
+ async function testDashcamCreation() {
38
+ console.log('🧪 Test: Dashcam creation');
39
+
40
+ const client = new MockTestDriver();
41
+ const dashcam = new Dashcam(client);
42
+
43
+ console.assert(dashcam.client === client, 'Client should be stored');
44
+ console.assert(dashcam.recording === false, 'Should not be recording initially');
45
+ console.assert(dashcam._authenticated === false, 'Should not be authenticated initially');
46
+
47
+ console.log('✅ Dashcam creation test passed');
48
+ }
49
+
50
+ async function testDashcamWithOptions() {
51
+ console.log('\n🧪 Test: Dashcam with options');
52
+
53
+ const client = new MockTestDriver();
54
+ const dashcam = new Dashcam(client, {
55
+ apiKey: 'custom-key',
56
+ autoStart: true,
57
+ logs: [
58
+ { type: 'file', path: '/tmp/test.log', name: 'Test Log' }
59
+ ]
60
+ });
61
+
62
+ console.assert(dashcam.apiKey === 'custom-key', 'Custom API key should be used');
63
+ console.assert(dashcam.autoStart === true, 'Auto start should be enabled');
64
+ console.assert(dashcam.logs.length === 1, 'Logs should be stored');
65
+
66
+ console.log('✅ Dashcam options test passed');
67
+ }
68
+
69
+ async function testErrorOnMissingClient() {
70
+ console.log('\n🧪 Test: Error when client missing');
71
+
72
+ try {
73
+ new Dashcam();
74
+ console.error('❌ Should have thrown error');
75
+ process.exit(1);
76
+ } catch (error) {
77
+ console.assert(
78
+ error.message.includes('TestDriver client'),
79
+ 'Error message should mention TestDriver client'
80
+ );
81
+ }
82
+
83
+ console.log('✅ Missing client error test passed');
84
+ }
85
+
86
+ async function testStopReturnsUrl() {
87
+ console.log('\n🧪 Test: Stop returns URL');
88
+
89
+ const client = new MockTestDriver();
90
+ const dashcam = new Dashcam(client);
91
+
92
+ dashcam.recording = true; // Simulate recording state
93
+ const url = await dashcam.stop();
94
+
95
+ console.assert(url !== null, 'URL should be returned');
96
+ console.assert(url.includes('replay'), 'URL should contain replay');
97
+ console.assert(dashcam.recording === false, 'Should stop recording');
98
+
99
+ console.log('✅ Stop returns URL test passed');
100
+ console.log(' URL:', url);
101
+ }
102
+
103
+ async function testIsRecording() {
104
+ console.log('\n🧪 Test: isRecording method');
105
+
106
+ const client = new MockTestDriver();
107
+ const dashcam = new Dashcam(client);
108
+
109
+ let recording = await dashcam.isRecording();
110
+ console.assert(recording === false, 'Should not be recording initially');
111
+
112
+ dashcam.recording = true;
113
+ recording = await dashcam.isRecording();
114
+ console.assert(recording === true, 'Should return recording state');
115
+
116
+ console.log('✅ isRecording test passed');
117
+ }
118
+
119
+ async function runAllTests() {
120
+ console.log('🚀 Running Dashcam class tests...\n');
121
+
122
+ try {
123
+ await testDashcamCreation();
124
+ await testDashcamWithOptions();
125
+ await testErrorOnMissingClient();
126
+ await testStopReturnsUrl();
127
+ await testIsRecording();
128
+
129
+ console.log('\n✅ All tests passed!');
130
+ } catch (error) {
131
+ console.error('\n❌ Test failed:', error.message);
132
+ console.error(error.stack);
133
+ process.exit(1);
134
+ }
135
+ }
136
+
137
+ runAllTests();
@@ -3,42 +3,24 @@
3
3
  * Converted from: testdriver/acceptance/assert.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  describe("Assert Test", () => {
14
- let testdriver;
15
-
16
- beforeEach(async (context) => {
17
- // Create a new client for each test
18
- testdriver = createTestClient({
19
- task: context.task,
20
- });
21
-
22
- await setupTest(testdriver);
23
- }, 600000);
24
-
25
- afterEach(async (context) => {
26
- // Teardown after each test, passing the individual test context
27
- const sessionInfo = await teardownTest(testdriver, {
28
- task: context.task,
10
+ it("should assert the testdriver login page shows", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true, newSandbox: true });
12
+
13
+ // provision.chrome() automatically calls ready() and starts dashcam
14
+ await testdriver.provision.chrome({
15
+ url: 'http://testdriver-sandbox.vercel.app/login',
29
16
  });
30
- console.log(
31
- `[Test] Teardown complete, dashcam URL: ${sessionInfo.dashcamUrl}`,
32
- );
33
- });
34
17
 
35
- it("should assert the testdriver login page shows", async () => {
36
18
  // Assert the TestDriver.ai Sandbox login page is displayed
37
- // const result = await testdriver.assert(
38
- // "the TestDriver.ai Sandbox login page is displayed",
39
- // );
19
+ const result = await testdriver.assert(
20
+ "the TestDriver.ai Sandbox login page is displayed",
21
+ );
40
22
 
41
- // expect(result).toBeTruthy();
42
- return true;
23
+ expect(result).toBeTruthy();
43
24
  });
44
25
  });
26
+
@@ -0,0 +1,56 @@
1
+ /**
2
+ * TestDriver SDK - Auto Cache Key Demo
3
+ *
4
+ * This test demonstrates the auto-generated cache key feature.
5
+ * When no cacheKey is provided, TestDriver will automatically generate
6
+ * one based on the hash of this test file.
7
+ */
8
+
9
+ import { describe, expect, it } from "vitest";
10
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
11
+
12
+ describe("Auto Cache Key Demo", () => {
13
+ it("should use auto-generated cache key based on file hash", async (context) => {
14
+ // NOTE: No cacheKey is provided here!
15
+ // TestDriver will automatically generate one from the hash of this file
16
+ const testdriver = TestDriver(context, {
17
+ headless: true,
18
+ newSandbox: true
19
+ // cacheKey NOT specified - will be auto-generated
20
+ });
21
+
22
+ // The cache key should be auto-generated
23
+ console.log('Auto-generated cache key:', testdriver.options.cacheKey);
24
+ expect(testdriver.options.cacheKey).toBeTruthy();
25
+ expect(testdriver.options.cacheKey).toMatch(/^[0-9a-f]{16}$/); // 16 hex chars
26
+
27
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
28
+
29
+ // First find - will be cached with auto-generated key
30
+ const signInButton1 = await testdriver.find(
31
+ "Sign In, black button below the password field"
32
+ );
33
+
34
+ // Second find - should hit cache because it's the same file (same cache key)
35
+ const signInButton2 = await testdriver.find(
36
+ "Sign In, black button below the password field"
37
+ );
38
+
39
+ expect(signInButton1.found()).toBe(true);
40
+ expect(signInButton2.found()).toBe(true);
41
+ });
42
+
43
+ it("should use same auto-generated cache key for multiple tests in the same file", async (context) => {
44
+ // This test is in the same file, so it should get the same auto-generated cache key
45
+ const testdriver = TestDriver(context, {
46
+ headless: true,
47
+ newSandbox: true
48
+ });
49
+
50
+ console.log('Auto-generated cache key (test 2):', testdriver.options.cacheKey);
51
+ expect(testdriver.options.cacheKey).toBeTruthy();
52
+
53
+ // If you modify this file, the hash (and therefore cache key) will change,
54
+ // invalidating the cache for this test file
55
+ });
56
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Chrome Extension Loading Demo
3
+ * Demonstrates how to load Chrome extensions using Chrome for Testing
4
+ *
5
+ * This test shows how to launch Chrome with a specific extension loaded
6
+ * by using its Chrome Web Store extension ID.
7
+ */
8
+
9
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
10
+ import TestDriver from "../../sdk.js";
11
+ import {
12
+ runPostrun,
13
+ runPrerunChromeExtension
14
+ } from "./setup/lifecycleHelpers.mjs";
15
+
16
+ describe("Chrome Extension Loading", () => {
17
+ let client;
18
+ let dashcamUrl;
19
+
20
+ beforeAll(async () => {
21
+ // Initialize TestDriver client
22
+ client = await TestDriver.create({
23
+ apiKey: process.env.TD_API_KEY,
24
+ apiRoot: process.env.TD_API_ROOT,
25
+ os: "linux",
26
+ verbosity: 1,
27
+ });
28
+
29
+ // Run prerun with uBlock Origin extension loaded
30
+ // Extension ID: cjpalhdlnbpafiamejdnhcphjbkeiagm
31
+ await runPrerunChromeExtension(client, "cjpalhdlnbpafiamejdnhcphjbkeiagm");
32
+ });
33
+
34
+ afterAll(async () => {
35
+ if (client) {
36
+ dashcamUrl = await runPostrun(client);
37
+ await client.cleanup();
38
+ }
39
+ });
40
+
41
+ it("should load Chrome with extension and verify functionality", async () => {
42
+ // Focus Chrome browser
43
+ await client.focusApplication("Google Chrome");
44
+
45
+ // Verify the page loaded
46
+ const pageElement = await client.find("TestDriver.ai Sandbox");
47
+ expect(pageElement.found()).toBe(true);
48
+
49
+ // Test basic interaction to ensure Chrome is working with the extension
50
+ const signInButton = await client.find(
51
+ "Sign In, black button below the password field",
52
+ );
53
+ await signInButton.click();
54
+
55
+ // Verify error message appears
56
+ const result = await client.assert(
57
+ "an error shows that fields are required",
58
+ );
59
+ expect(result).toBeTruthy();
60
+
61
+ console.log("✅ Chrome extension loaded successfully!");
62
+ if (dashcamUrl) {
63
+ console.log("🎥 Dashcam URL:", dashcamUrl);
64
+ }
65
+ });
66
+
67
+ it("should demonstrate extension interaction", async () => {
68
+ // You can add specific tests here to interact with the extension
69
+ // For example, if testing uBlock Origin, you might:
70
+ // 1. Navigate to a page with ads
71
+ // 2. Verify ads are blocked
72
+ // 3. Access the extension's popup or settings
73
+
74
+ await client.focusApplication("Google Chrome");
75
+
76
+ // Example: Navigate to extension management page to verify it's loaded
77
+ await client.exec(
78
+ "sh",
79
+ `xdotool key --clearmodifiers ctrl+shift+e`,
80
+ 5000,
81
+ true
82
+ );
83
+
84
+ // Wait a moment for the extensions page to potentially load
85
+ await new Promise((resolve) => setTimeout(resolve, 2000));
86
+
87
+ console.log("✅ Extension interaction test completed");
88
+ });
89
+ });
@@ -3,31 +3,19 @@
3
3
  * Converted from: testdriver/acceptance/drag-and-drop.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  const isLinux = (process.env.TD_OS || "linux") === "linux";
14
10
 
15
11
  describe("Drag and Drop Test", () => {
16
- let testdriver;
17
-
18
- beforeEach(async (context) => {
19
- testdriver = createTestClient({ task: context.task });
20
-
21
- await setupTest(testdriver);
22
- });
23
-
24
- afterEach(async (context) => {
25
- await teardownTest(testdriver, { task: context.task });
26
- });
27
-
28
12
  it.skipIf(isLinux)(
29
13
  'should drag "New Text Document" to "Recycle Bin"',
30
- async () => {
14
+ async (context) => {
15
+ const testdriver = TestDriver(context, { headless: true });
16
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
17
+
18
+ //
31
19
  // Show the desktop
32
20
  await testdriver.pressKeys(["win", "d"]);
33
21
 
@@ -3,28 +3,15 @@
3
3
  * Tests that finding a non-existent element returns properly without timing out
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  describe("Element Not Found Test", () => {
14
- let testdriver;
10
+ it("should handle non-existent element gracefully without timing out", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should handle non-existent element gracefully without timing out", async () => {
27
- await testdriver.focusApplication("Google Chrome");
14
+ //
28
15
 
29
16
  // Try to find an element that definitely doesn't exist
30
17
  const element = await testdriver.find(
@@ -3,27 +3,15 @@
3
3
  * Converted from: testdriver/acceptance/exec-js.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  describe("Exec JavaScript Test", () => {
14
- let testdriver;
10
+ it("should fetch user data from API and enter email", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should fetch user data from API and enter email", async () => {
14
+ //
27
15
  // Execute JavaScript to fetch user data
28
16
  const userEmail = await testdriver.exec(
29
17
  "js",
@@ -3,29 +3,17 @@
3
3
  * Converted from: testdriver/acceptance/exec-output.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  describe("Exec Output Test", () => {
14
- let testdriver;
15
-
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it.skipIf(() => testdriver.os === "linux")(
10
+ it.skipIf(process.env.TD_OS === "linux")(
27
11
  "should set date using PowerShell and navigate to calendar",
28
- async () => {
12
+ async (context) => {
13
+ const testdriver = TestDriver(context, { headless: true });
14
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
+
16
+ //
29
17
  // Generate date in query string format
30
18
  const queryString = await testdriver.exec(
31
19
  "pwsh",
@@ -3,33 +3,21 @@
3
3
  * Converted from: testdriver/acceptance/exec-shell.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
12
8
 
13
9
  describe("Exec PowerShell Test", () => {
14
- let testdriver;
15
-
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it.skipIf(() => testdriver.os === "linux")(
10
+ it.skipIf(process.env.TD_OS === "linux")(
27
11
  "should generate random email using PowerShell and enter it",
28
- async () => {
12
+ async (context) => {
13
+ const testdriver = TestDriver(context, { headless: true });
14
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
+
16
+ //
29
17
  // Generate random email using PowerShell
30
- const randomEmail = await testdriver.exec(
31
- "pwsh",
32
- `
18
+ const randomEmail = await testdriver.exec({
19
+ language: "pwsh",
20
+ code: `
33
21
  # Random email generator in PowerShell
34
22
 
35
23
  # Arrays of possible names and domains
@@ -49,8 +37,8 @@ $email = "$first.$last$number@$domain".ToLower()
49
37
  # Output
50
38
  Write-Output "$email"
51
39
  `,
52
- 10000,
53
- );
40
+ timeout: 10000,
41
+ });
54
42
 
55
43
  // Enter the email in username field
56
44
  const usernameField = await testdriver.find(