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,234 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { chrome } from "../../src/presets/index.mjs";
3
+
4
+ /**
5
+ * CHECKPOINT SYSTEM GUIDE FOR TESTDRIVER TESTS
6
+ * ============================================
7
+ *
8
+ * When newSandbox is set to false in chrome(), the sandbox persists between test runs.
9
+ * This allows you to skip completed steps when debugging or developing tests.
10
+ *
11
+ * HOW TO USE CHECKPOINTS:
12
+ *
13
+ * 1. Run your test normally until it fails at a certain step
14
+ * 2. Find the checkpoint comment just before the failing section (e.g., "// CHECKPOINT: Login Complete")
15
+ * 3. Comment out all code BEFORE that checkpoint using /* ... *\/ block comments
16
+ * 4. Re-run the test - it will resume from that checkpoint using the existing sandbox state
17
+ * 5. Once the test passes completely, uncomment all sections to verify end-to-end
18
+ *
19
+ * CHECKPOINT LOCATIONS IN THIS TEST:
20
+ * - CHECKPOINT 1: Cookie banner handled
21
+ * - CHECKPOINT 2: Login complete
22
+ * - CHECKPOINT 3: Patients page loaded
23
+ * - CHECKPOINT 4: Patient selected/created
24
+ * - CHECKPOINT 5: Patient profile opened
25
+ *
26
+ * IMPORTANT: Always set newSandbox: false in chrome() options to enable this feature!
27
+ *
28
+ * FOR AI ASSISTANTS:
29
+ * When helping users debug TestDriver tests:
30
+ * - If a test fails at a certain step, suggest commenting out earlier checkpoints
31
+ * - Remind them to set newSandbox: false
32
+ * - After fixes, remind them to uncomment all code for full end-to-end validation
33
+ */
34
+
35
+ // Helper function to poll for an element until it's found
36
+ async function waitForElement(testdriver, description, maxAttempts = 10, delayMs = 1000) {
37
+ for (let i = 0; i < maxAttempts; i++) {
38
+ try {
39
+ const element = await testdriver.find(description);
40
+ if (element.found()) {
41
+ return element;
42
+ }
43
+ } catch (e) {
44
+ if (i === maxAttempts - 1) throw e;
45
+ }
46
+ await new Promise(resolve => setTimeout(resolve, delayMs));
47
+ }
48
+ throw new Error(`Element not found after ${maxAttempts} attempts: ${description}`);
49
+ }
50
+
51
+ describe("Sully.ai Patient Management Workflow", () => {
52
+ it("should complete full patient workflow: login, view patients, update note, and edit profile", async (context) => {
53
+ const { testdriver } = await chrome(context, {
54
+ url: 'https://app.sully.ai',
55
+ redrawThreshold: { enabled: true }
56
+ });
57
+
58
+ // ========================================================================
59
+ // CHECKPOINT 1: Cookie Banner
60
+ // If test fails after this point, comment out this section (lines above)
61
+ // ========================================================================
62
+
63
+ // Handle cookie banner if present
64
+ try {
65
+ const acceptCookies = await testdriver.find("Accept All button for cookies");
66
+ await acceptCookies.click();
67
+ } catch {
68
+ console.log("No cookie banner or already dismissed");
69
+ }
70
+
71
+ // Wait for login page to load by polling for email field
72
+ console.log("Waiting for login page to load...");
73
+ const emailField = await waitForElement(testdriver, "Email input field");
74
+ console.log("Found email field");
75
+ await emailField.click();
76
+ await testdriver.type("razeen+testdriver@sully.ai");
77
+
78
+ const passwordField = await testdriver.find("Password input field");
79
+ await passwordField.click();
80
+ await testdriver.type("plmokn7@A", {secret: true});
81
+
82
+ const loginButton = await testdriver.find("Login button");
83
+ await loginButton.click();
84
+
85
+ // ========================================================================
86
+ // CHECKPOINT 2: Login Complete
87
+ // If test fails after this point, comment out all code above this checkpoint
88
+ // ========================================================================
89
+
90
+ // Wait for navigation to complete after login
91
+ console.log("Waiting for login to process...");
92
+ await new Promise(resolve => setTimeout(resolve, 5000));
93
+
94
+ // Take a screenshot to see where we are
95
+ await testdriver.screenshot();
96
+ console.log("Screenshot taken after login");
97
+
98
+ // Verify login successful
99
+ const loginSuccess = await testdriver.assert("user is logged in and on the main application page or dashboard");
100
+ expect(loginSuccess).toBeTruthy();
101
+
102
+ // Poll for dashboard element to ensure login completed
103
+ console.log("Looking for Patients link...");
104
+ await waitForElement(testdriver, "Patients button or link in the navigation menu", 15, 1000);
105
+
106
+ // Dismiss password save dialog first if it appears
107
+ try {
108
+ const neverButton = await testdriver.find("Never button or Never save button");
109
+ await neverButton.click();
110
+ console.log("Dismissed password save dialog");
111
+ } catch {
112
+ console.log("Password save dialog not found");
113
+ }
114
+
115
+ await testdriver.find("save profile changes popup or banner").click();
116
+
117
+ // Click Patients
118
+ const patientsLink = await testdriver.find("Patients navigation link or menu item");
119
+ await patientsLink.click();
120
+
121
+ // ========================================================================
122
+ // CHECKPOINT 3: Patients Page Loaded
123
+ // If test fails after this point, comment out all code above this checkpoint
124
+ // ========================================================================
125
+
126
+ // Approach 1: Search for existing patient
127
+ const existingPatientInput = await testdriver.find("Patient search input box");
128
+ await existingPatientInput.click();
129
+
130
+ // Type a search term to find a patient
131
+ await testdriver.type("Patient Test");
132
+
133
+ // Poll for search results to appear
134
+ const firstPatient = await waitForElement(testdriver, "first patient name in the dropdown or list", 5, 500);
135
+ await firstPatient.click();
136
+
137
+ console.log("Found patient via search");
138
+
139
+ // Verify patient visit page or details are loaded
140
+ const patientLoaded = await testdriver.assert("we are on a patient recording page");
141
+ expect(patientLoaded).toBeTruthy();
142
+
143
+ // ========================================================================
144
+ // CHECKPOINT 4: Patient Selected/Created
145
+ // If test fails after this point, comment out all code above this checkpoint
146
+ // ========================================================================
147
+
148
+ await testdriver.find('input box at the bottom of the screen').click();
149
+ await testdriver.type("This is a test note added by TestDriver automation.");
150
+
151
+ // Regenerate note - click make note more concise
152
+ console.log("Looking for note regeneration option...");
153
+ try {
154
+ const makeNoteConcise = await testdriver.find("Make note more concise button or option");
155
+ await makeNoteConcise.click();
156
+ console.log("Clicked make note more concise");
157
+
158
+ // Wait for note to regenerate
159
+ await new Promise(resolve => setTimeout(resolve, 3000));
160
+
161
+ // Verify note regeneration completed
162
+ const noteRegenerated = await testdriver.assert("note has been regenerated or updated");
163
+ expect(noteRegenerated).toBeTruthy();
164
+ } catch {
165
+ console.log("Could not find or click make note more concise option");
166
+ }
167
+
168
+ // Look for kebab menu on the right to access patient profile
169
+ console.log("Looking for kebab menu on the right...");
170
+
171
+ // Try to find the menu - it might be a three-dot menu or settings icon on the right side
172
+ const kebabMenu = await testdriver.find("three dots menu or kebab menu on the right side of the page");
173
+ await kebabMenu.click();
174
+
175
+ // Wait for menu to open
176
+ await new Promise(resolve => setTimeout(resolve, 2000));
177
+
178
+ // Look for patient profile option
179
+ const patientProfile = await testdriver.find("Patient Profile or Edit Profile option in menu");
180
+ await patientProfile.click();
181
+
182
+ // Wait for patient profile form to load
183
+ await new Promise(resolve => setTimeout(resolve, 3000));
184
+
185
+ // Verify patient profile form is displayed
186
+ const profileForm = await testdriver.assert("patient profile form or edit patient page is visible");
187
+ expect(profileForm).toBeTruthy();
188
+
189
+ // ========================================================================
190
+ // CHECKPOINT 5: Patient Profile Opened
191
+ // If test fails after this point, comment out all code above this checkpoint
192
+ // ========================================================================
193
+
194
+ // Fill out patient details
195
+ console.log("Filling out patient details...");
196
+ try {
197
+ // Look for phone number field as an example
198
+ const phoneField = await testdriver.find("phone number field or mobile number input");
199
+ await phoneField.click();
200
+ await testdriver.type("555-0123");
201
+ console.log("Filled phone number");
202
+
203
+ // Look for email field
204
+ try {
205
+ const emailFieldProfile = await testdriver.find("email address field in the patient profile");
206
+ await emailFieldProfile.click();
207
+ await testdriver.type("patient@example.com");
208
+ console.log("Filled email address");
209
+ } catch {
210
+ console.log("Email field not found");
211
+ }
212
+
213
+ // Look for and click the save button
214
+ const saveButton = await testdriver.find("Save button or Submit button");
215
+ await saveButton.click();
216
+ console.log("Clicked save button");
217
+
218
+ // Wait for save to complete
219
+ await new Promise(resolve => setTimeout(resolve, 2000));
220
+
221
+ // Verify save was successful
222
+ const saveSuccess = await testdriver.assert("changes were saved successfully or save confirmation appears");
223
+ expect(saveSuccess).toBeTruthy();
224
+ } catch (error) {
225
+ console.log("Could not complete patient profile form:", error.message);
226
+ }
227
+
228
+ // Take final screenshot
229
+ const finalScreenshot = await testdriver.screenshot();
230
+ expect(finalScreenshot).toBeDefined();
231
+
232
+ console.log("โœ… Test completed successfully! Remember to uncomment all checkpoints for final validation.");
233
+ });
234
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * TestDriver SDK - Console Log Test
3
+ * Tests that console.log statements are captured and sent to dashcam
4
+ */
5
+
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
8
+
9
+ describe("Console Log Test", () => {
10
+ it("should capture console logs and send them to dashcam", async (context) => {
11
+ console.log("๐ŸŽฌ Test starting - this should appear in dashcam");
12
+
13
+ const testdriver = TestDriver(context, { headless: true });
14
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
+
16
+ console.log("โœ… Chrome launched successfully");
17
+
18
+ // Give Chrome a moment to fully render the UI
19
+ await new Promise(resolve => setTimeout(resolve, 2000));
20
+
21
+ console.log("๐Ÿ” Looking for Sign In button");
22
+
23
+ // Find and click the sign in button
24
+ const signInButton = await testdriver.find(
25
+ "Sign In, black button below the password field",
26
+ );
27
+
28
+ console.log("๐Ÿ‘† Clicking Sign In button");
29
+ await signInButton.click();
30
+
31
+ console.log("๐Ÿงช Asserting error message appears");
32
+
33
+ // Assert error shows
34
+ const result = await testdriver.assert(
35
+ "an error shows that fields are required",
36
+ );
37
+
38
+ console.log("โœ… Test completed successfully - all logs should be in dashcam");
39
+
40
+ expect(result).toBeTruthy();
41
+ });
42
+ });
@@ -1,62 +1,12 @@
1
- import {
2
- afterAll,
3
- afterEach,
4
- beforeAll,
5
- beforeEach,
6
- describe,
7
- expect,
8
- it,
9
- } from "vitest";
10
- import {
11
- addDashcamLog,
12
- authDashcam,
13
- createTestClient,
14
- launchChrome,
15
- setupTest,
16
- startDashcam,
17
- stopDashcam,
18
- teardownTest,
19
- waitForPage,
20
- } from "./setup/testHelpers.mjs";
1
+ import { describe, expect, it } from "vitest";
2
+ import { TestDriver } from "../../src/vitest/hooks.mjs";
21
3
 
22
- describe.sequential("Type Test", () => {
23
- let testdriver;
4
+ describe("Type Test", () => {
5
+ it("should enter standard_user in username field", async (context) => {
6
+ const testdriver = TestDriver(context, { headless: true });
7
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
24
8
 
25
- beforeAll(async () => {
26
- testdriver = createTestClient({ task: { id: "type-test-suite" } });
27
- await setupTest(testdriver, { prerun: false }); // Skip prerun, we'll handle dashcam manually
28
-
29
- // One-time dashcam setup (auth and add logs)
30
- await authDashcam(testdriver);
31
- await addDashcamLog(testdriver);
32
- await launchChrome(testdriver);
33
- await waitForPage(testdriver, "TestDriver.ai Sandbox");
34
- });
35
-
36
- beforeEach(async () => {
37
- await startDashcam(testdriver);
38
- }, 60000);
39
-
40
- afterEach(async (context) => {
41
- // Stop dashcam first to get the URL
42
- const dashcamUrl = await stopDashcam(testdriver);
43
- console.log("๐Ÿ“ค Dashcam URL:", dashcamUrl);
44
-
45
- // Use teardownTest to track results, but skip postrun (already stopped dashcam) and disconnect
46
- await teardownTest(testdriver, {
47
- task: context.task,
48
- dashcamUrl: dashcamUrl, // Pass the dashcam URL we already got
49
- postrun: false, // Skip postrun since we manually stopped dashcam
50
- disconnect: false, // Don't disconnect, we'll do that in afterAll
51
- });
52
- });
53
-
54
- afterAll(async () => {
55
- await testdriver.disconnect();
56
- });
57
-
58
- it("should enter standard_user in username field", async () => {
59
- await testdriver.focusApplication("Google Chrome");
9
+ //
60
10
  const usernameField = await testdriver.find(
61
11
  "Username, input field for username",
62
12
  );
@@ -69,7 +19,18 @@ describe.sequential("Type Test", () => {
69
19
  expect(result).toBeTruthy();
70
20
  });
71
21
 
72
- it("should show validation message when clicking Sign In without password", async () => {
22
+ it("should show validation message when clicking Sign In without password", async (context) => {
23
+ const testdriver = TestDriver(context, { headless: true });
24
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
25
+
26
+ // First fill in username
27
+ const usernameField = await testdriver.find(
28
+ "Username, input field for username",
29
+ );
30
+ await usernameField.click();
31
+ await testdriver.type("standard_user");
32
+
33
+ //
73
34
  const signInButton = await testdriver.find(
74
35
  "Sign in, black button below the password field",
75
36
  );
package/vitest.config.mjs CHANGED
@@ -10,6 +10,7 @@ export default defineConfig({
10
10
  testDriverPlugin({
11
11
  apiKey: process.env.TD_API_KEY,
12
12
  apiRoot: process.env.TD_API_ROOT || "https://testdriver-api.onrender.com",
13
+ newSandbox: true,
13
14
  }),
14
15
  ],
15
16
 
package/.vscode/mcp.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "servers": {
3
- "testdriverai": {
4
- "type": "stdio",
5
- "command": "node",
6
- "args": ["/Users/ianjennings/.mcp/testdriver/src/index.js"]
7
- }
8
- }
9
- }