testdriverai 6.2.2 → 7.0.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 (264) hide show
  1. package/.github/workflows/acceptance-linux.yml +75 -0
  2. package/.github/workflows/acceptance-sdk-tests.yml +133 -0
  3. package/.vscode/settings.json +5 -1
  4. package/MIGRATION.md +389 -0
  5. package/PLUGIN_MIGRATION.md +222 -0
  6. package/PROMPT_CACHE.md +200 -0
  7. package/SDK_LOGGING.md +222 -0
  8. package/SDK_MIGRATION.md +474 -0
  9. package/SDK_README.md +1122 -0
  10. package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
  11. package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
  12. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  13. package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
  14. package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
  15. package/_testdriver/lifecycle/prerun.yaml +15 -0
  16. package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
  17. package/agent/index.js +258 -68
  18. package/agent/interface.js +15 -0
  19. package/agent/lib/cache.js +142 -0
  20. package/agent/lib/commander.js +1 -39
  21. package/agent/lib/commands.js +143 -188
  22. package/agent/lib/redraw.js +6 -3
  23. package/agent/lib/sandbox.js +19 -5
  24. package/agent/lib/sdk.js +1 -0
  25. package/agent/lib/system.js +0 -3
  26. package/agent/lib/validation.js +1 -7
  27. package/debug-locate-response.js +82 -0
  28. package/debug-screenshot-1763401388589.png +0 -0
  29. package/debugger/index.html +15 -4
  30. package/docs/ARCHITECTURE.md +424 -0
  31. package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
  32. package/docs/QUICK_START_TEST_RECORDING.md +215 -0
  33. package/docs/SDK_AWESOME_LOGS.md +468 -0
  34. package/docs/TEST_RECORDING.md +388 -0
  35. package/docs/docs.json +232 -152
  36. package/docs/sdk-browser-rendering.md +167 -0
  37. package/docs/v6/getting-started/self-hosting.mdx +407 -0
  38. package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
  39. package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
  40. package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
  41. package/docs/v6/overview/comparison.mdx +101 -0
  42. package/docs/v7/README.md +135 -0
  43. package/docs/v7/api/ai.mdx +205 -0
  44. package/docs/v7/api/assert.mdx +285 -0
  45. package/docs/v7/api/assertions.mdx +403 -0
  46. package/docs/v7/api/click.mdx +287 -0
  47. package/docs/v7/api/client.mdx +322 -0
  48. package/docs/v7/api/elements.mdx +479 -0
  49. package/docs/v7/api/exec.mdx +346 -0
  50. package/docs/v7/api/find.mdx +316 -0
  51. package/docs/v7/api/focusApplication.mdx +294 -0
  52. package/docs/v7/api/hover.mdx +279 -0
  53. package/docs/v7/api/pressKeys.mdx +349 -0
  54. package/docs/v7/api/sandbox.mdx +404 -0
  55. package/docs/v7/api/scroll.mdx +300 -0
  56. package/docs/v7/api/type.mdx +314 -0
  57. package/docs/v7/commands/assert.mdx +45 -0
  58. package/docs/v7/commands/exec.mdx +282 -0
  59. package/docs/v7/commands/focus-application.mdx +44 -0
  60. package/docs/v7/commands/hover-image.mdx +69 -0
  61. package/docs/v7/commands/hover-text.mdx +47 -0
  62. package/docs/v7/commands/if.mdx +53 -0
  63. package/docs/v7/commands/match-image.mdx +67 -0
  64. package/docs/v7/commands/press-keys.mdx +87 -0
  65. package/docs/v7/commands/remember.mdx +49 -0
  66. package/docs/v7/commands/run.mdx +44 -0
  67. package/docs/v7/commands/scroll-until-image.mdx +66 -0
  68. package/docs/v7/commands/scroll-until-text.mdx +60 -0
  69. package/docs/v7/commands/scroll.mdx +69 -0
  70. package/docs/v7/commands/type.mdx +45 -0
  71. package/docs/v7/commands/wait-for-image.mdx +54 -0
  72. package/docs/v7/commands/wait-for-text.mdx +48 -0
  73. package/docs/v7/commands/wait.mdx +45 -0
  74. package/docs/v7/getting-started/quickstart.mdx +199 -0
  75. package/docs/v7/guides/migration.mdx +562 -0
  76. package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
  77. package/docs/v7/playwright.mdx +342 -0
  78. package/eslint.config.js +19 -1
  79. package/examples/run-tests-with-recording.sh +70 -0
  80. package/examples/screenshot-example.js +63 -0
  81. package/examples/sdk-awesome-logs-demo.js +177 -0
  82. package/examples/sdk-cache-thresholds.js +96 -0
  83. package/examples/sdk-element-properties.js +155 -0
  84. package/examples/sdk-simple-example.js +65 -0
  85. package/examples/test-recording-example.test.js +166 -0
  86. package/interfaces/cli/lib/base.js +10 -4
  87. package/interfaces/logger.js +2 -1
  88. package/interfaces/shared-test-state.mjs +69 -0
  89. package/interfaces/vitest-plugin.mjs +744 -0
  90. package/mcp-server/AI_GUIDELINES.md +57 -0
  91. package/package.json +18 -5
  92. package/schema.json +8 -29
  93. package/scripts/view-test-results.mjs +96 -0
  94. package/sdk-log-formatter.js +714 -0
  95. package/sdk.d.ts +735 -0
  96. package/sdk.js +1906 -0
  97. package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
  98. package/setup/aws/cloudformation.yaml +9 -2
  99. package/test/mcp-example-test.yaml +27 -0
  100. package/test-find-api.js +73 -0
  101. package/test-prompt-cache.js +96 -0
  102. package/test-sandbox-render.js +28 -0
  103. package/test-sdk-methods.js +15 -0
  104. package/test-sdk-refactor.js +53 -0
  105. package/test-stack-trace.mjs +57 -0
  106. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
  107. package/testdriver/acceptance-sdk/README.md +128 -0
  108. package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
  109. package/testdriver/acceptance-sdk/assert.test.mjs +44 -0
  110. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +70 -0
  111. package/testdriver/acceptance-sdk/element-not-found.test.mjs +38 -0
  112. package/testdriver/acceptance-sdk/exec-js.test.mjs +55 -0
  113. package/testdriver/acceptance-sdk/exec-output.test.mjs +71 -0
  114. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +69 -0
  115. package/testdriver/acceptance-sdk/focus-window.test.mjs +48 -0
  116. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +41 -0
  117. package/testdriver/acceptance-sdk/hover-image.test.mjs +43 -0
  118. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +50 -0
  119. package/testdriver/acceptance-sdk/hover-text.test.mjs +41 -0
  120. package/testdriver/acceptance-sdk/match-image.test.mjs +48 -0
  121. package/testdriver/acceptance-sdk/press-keys.test.mjs +64 -0
  122. package/testdriver/acceptance-sdk/prompt.test.mjs +45 -0
  123. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +52 -0
  124. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +51 -0
  125. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +42 -0
  126. package/testdriver/acceptance-sdk/scroll.test.mjs +50 -0
  127. package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
  128. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +239 -0
  129. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +648 -0
  130. package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
  131. package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
  132. package/testdriver/acceptance-sdk/type.test.mjs +84 -0
  133. package/verify-element-api.js +89 -0
  134. package/verify-types.js +0 -0
  135. package/vitest.config.example.js +19 -0
  136. package/vitest.config.mjs +65 -0
  137. package/vitest.config.mjs.bak +44 -0
  138. package/.github/workflows/acceptance-v6.yml +0 -169
  139. package/docs/overview/comparison.mdx +0 -82
  140. package/testdriver/lifecycle/prerun.yaml +0 -17
  141. /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
  142. /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
  143. /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
  144. /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
  145. /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
  146. /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
  147. /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
  148. /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
  149. /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
  150. /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
  151. /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
  152. /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
  153. /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
  154. /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
  155. /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
  156. /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
  157. /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
  158. /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
  159. /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
  160. /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
  161. /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
  162. /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
  163. /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
  164. /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
  165. /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
  166. /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
  167. /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
  168. /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
  169. /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
  170. /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
  171. /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
  172. /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
  173. /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
  174. /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
  175. /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
  176. /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
  177. /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
  178. /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
  179. /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
  180. /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
  181. /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
  182. /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
  183. /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
  184. /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
  185. /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
  186. /package/docs/{account → v6/account}/dashboard.mdx +0 -0
  187. /package/docs/{account → v6/account}/enterprise.mdx +0 -0
  188. /package/docs/{account → v6/account}/pricing.mdx +0 -0
  189. /package/docs/{account → v6/account}/projects.mdx +0 -0
  190. /package/docs/{account → v6/account}/team.mdx +0 -0
  191. /package/docs/{action → v6/action}/ami.mdx +0 -0
  192. /package/docs/{action → v6/action}/performance.mdx +0 -0
  193. /package/docs/{action → v6/action}/secrets.mdx +0 -0
  194. /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
  195. /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
  196. /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
  197. /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
  198. /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
  199. /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
  200. /package/docs/{cli → v6/cli}/overview.mdx +0 -0
  201. /package/docs/{commands → v6/commands}/assert.mdx +0 -0
  202. /package/docs/{commands → v6/commands}/exec.mdx +0 -0
  203. /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
  204. /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
  205. /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
  206. /package/docs/{commands → v6/commands}/if.mdx +0 -0
  207. /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
  208. /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
  209. /package/docs/{commands → v6/commands}/remember.mdx +0 -0
  210. /package/docs/{commands → v6/commands}/run.mdx +0 -0
  211. /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
  212. /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
  213. /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
  214. /package/docs/{commands → v6/commands}/type.mdx +0 -0
  215. /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
  216. /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
  217. /package/docs/{commands → v6/commands}/wait.mdx +0 -0
  218. /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
  219. /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
  220. /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
  221. /package/docs/{features → v6/features}/generation.mdx +0 -0
  222. /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
  223. /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
  224. /package/docs/{features → v6/features}/selectorless.mdx +0 -0
  225. /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
  226. /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
  227. /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
  228. /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
  229. /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
  230. /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
  231. /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
  232. /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
  233. /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
  234. /package/docs/{guide → v6/guide}/code.mdx +0 -0
  235. /package/docs/{guide → v6/guide}/locating.mdx +0 -0
  236. /package/docs/{guide → v6/guide}/protips.mdx +0 -0
  237. /package/docs/{guide → v6/guide}/variables.mdx +0 -0
  238. /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
  239. /package/docs/{importing → v6/importing}/csv.mdx +0 -0
  240. /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
  241. /package/docs/{importing → v6/importing}/jira.mdx +0 -0
  242. /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
  243. /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
  244. /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
  245. /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
  246. /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
  247. /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
  248. /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
  249. /package/docs/{overview → v6/overview}/faq.mdx +0 -0
  250. /package/docs/{overview → v6/overview}/performance.mdx +0 -0
  251. /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
  252. /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
  253. /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
  254. /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
  255. /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
  256. /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
  257. /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
  258. /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
  259. /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
  260. /package/docs/{security → v6/security}/action.mdx +0 -0
  261. /package/docs/{security → v6/security}/agent.mdx +0 -0
  262. /package/docs/{security → v6/security}/platform.mdx +0 -0
  263. /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
  264. /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
@@ -0,0 +1,342 @@
1
+ ---
2
+ title: "Get Started with TestDriver & Playwright"
3
+ sidebarTitle: "Playwright"
4
+ tag: "NEW"
5
+ description: "In this guide, you'll setup your TestDriver account, create a new Playwright project, and leverage TestDriver's AI to convert tests to natural language."
6
+ icon: "masks-theater"
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ `@testdriver.ai/playwright` is a backwards-compatible wrapper around `@playwright/test` that uses TestDriver's Vision AI to:
12
+
13
+ - Make [natural language assertions](#assertions-with-expect-tomatchprompt)
14
+ - [Replace brittle selectors](#locating-elements-with-testdriver-locate) with natural language
15
+ - [Perform actions](#performing-actions-with-testdriver-act) with a prompt
16
+ - Test with an [automated agent](#agentic-tests-with-test-agent)
17
+
18
+ We'll be incrementally converting Playwright's example test from the sample code:
19
+
20
+ ```typescript tests/example.spec.ts icon=square-js
21
+ // Before
22
+ test("get started link", async ({ page }) => {
23
+ await page.goto("https://playwright.dev/");
24
+ await page.getByRole("link", { name: "Get started" }).click();
25
+ await expect(
26
+ page.getByRole("heading", { name: "Installation" }),
27
+ ).toBeVisible();
28
+ });
29
+ ```
30
+
31
+ To using natural language with TestDriver:
32
+
33
+ ```typescript tests/example.spec.ts icon=square-js
34
+ // After
35
+ import { test } from "@testdriver.ai/playwright";
36
+
37
+ test.describe("get started link", () => {
38
+ test.beforeEach(async ({ page }) => page.goto("https://playwright.dev/"));
39
+ test.agent(`
40
+ - Click the 'Get started' link
41
+ - Verify the 'Installation' heading is visible
42
+ `);
43
+ });
44
+ ```
45
+
46
+ ## Prerequisites
47
+
48
+ <Steps>
49
+ <Step title="Create a TestDriver Account">
50
+ You will need a [Free TestDriver Account](https://app.testdriver.ai/team) to get an API key.
51
+
52
+ <Card title="Sign Up for TestDriver" icon="user-plus" horizontal href="https://app.testdriver.ai/team">
53
+
54
+ </Card>
55
+
56
+ </Step>
57
+ <Step title="Set up your environment">
58
+ Copy your API key from [the TestDriver dashboard](https://app.testdriver.ai/team), and set it as an environment variable.
59
+
60
+ <Tabs>
61
+ <Tab title="macOS / Linux">
62
+ ```bash Export an environment variable on macOS or Linux systems
63
+ export TD_API_KEY="your_api_key_here"
64
+ ```
65
+ </Tab>
66
+ <Tab title="Windows">
67
+ ```powershell Export an environment variable in PowerShell
68
+ setx TD_API_KEY "your_api_key_here"
69
+ ```
70
+ </Tab>
71
+ </Tabs>
72
+
73
+ </Step>
74
+ </Steps>
75
+
76
+ ## Setup Playwright
77
+
78
+ <Steps>
79
+ <Step title="Initialize Playwright">
80
+ <Info>
81
+ This is a condensed version of [Playwright's Installation Instructions](https://playwright.dev/docs/intro).
82
+
83
+ **If you're new to Playwright, you should follow their guide first.**
84
+ </Info>
85
+ In a new folder or an existing, run:
86
+
87
+ <Tabs>
88
+ <Tab title="npm">
89
+ ```bash
90
+ npm init playwright@latest
91
+ ```
92
+ </Tab>
93
+ <Tab title="yarn">
94
+ ```bash
95
+ yarn create playwright
96
+ ```
97
+ </Tab>
98
+ <Tab title="pnpm">
99
+ ```bash
100
+ pnpm create playwright
101
+ ```
102
+ </Tab>
103
+ </Tabs>
104
+ Select the following options when prompted:
105
+
106
+ ```console
107
+ ✔ Do you want to use TypeScript or JavaScript?
108
+ > TypeScript
109
+ ✔ Where to put your end-to-end tests?
110
+ > tests
111
+ ✔ Add a GitHub Actions workflow? (y/N)
112
+ > N
113
+ ✔ Install Playwright browsers (can be done manually via 'npx playwright install')? (Y/n)
114
+ > Y
115
+ ```
116
+
117
+ Then, confirm Playwright works by running:
118
+
119
+ <Tabs>
120
+ <Tab title="npm">
121
+ ```bash
122
+ npx playwright test
123
+ ```
124
+ </Tab>
125
+ <Tab title="yarn">
126
+ ```bash
127
+ yarn playwright test
128
+ ```
129
+ </Tab>
130
+ <Tab title="pnpm">
131
+ ```bash
132
+ pnpm exec playwright test
133
+ ```
134
+ </Tab>
135
+ </Tabs>
136
+
137
+ </Step>
138
+ </Steps>
139
+
140
+ ## Setup `@testdriver.ai/playwright`
141
+
142
+ <Steps>
143
+ <Step title="Install TestDriver">
144
+ `@testdriver.ai/playwright` as a backwards-compatible wrapper around `@playwright/test`:
145
+
146
+ <Tabs>
147
+ <Tab title="npm">
148
+ ```bash
149
+ npm install @testdriver.ai/playwright
150
+ ```
151
+ </Tab>
152
+ <Tab title="yarn">
153
+ ```bash
154
+ yarn add @testdriver.ai/playwright
155
+ ```
156
+ </Tab>
157
+ <Tab title="pnpm">
158
+ ```bash
159
+ pnpm add @testdriver.ai/playwright
160
+ ```
161
+ </Tab>
162
+ </Tabs>
163
+
164
+ </Step>
165
+ <Step title="Run Playwright">
166
+ Before we start using TestDriver in our tests, run Playwright in [UI Mode](https://playwright.dev/docs/test-ui-mode):
167
+
168
+ <Tabs>
169
+ <Tab title="npm">
170
+ ```bash
171
+ npx playwright test --ui
172
+ ```
173
+ </Tab>
174
+ <Tab title="yarn">
175
+ ```bash
176
+ yarn playwright test --ui
177
+ ```
178
+ </Tab>
179
+ <Tab title="pnpm">
180
+ ```bash
181
+ pnpm exec playwright test --ui
182
+ ```
183
+ </Tab>
184
+ </Tabs>
185
+ ![Playwright UI Mode](https://playwright.dev/assets/ideal-img/ui-mode.4e54d6b.3598.png)
186
+
187
+ Clicking the ▶️ button should successfully run the tests in the UI,
188
+ just as they did before with `playwright test` in the CLI.
189
+
190
+ </Step>
191
+ <Step title="Import TestDriver">
192
+ For the sake of simplicity, we'll be working with one test file for now.
193
+
194
+ Open `tests/example.spec.ts` in your editor & rename the `@playwright/test`
195
+ import to `@testdriver.ai/playwright`:
196
+
197
+ ```typescript tests/example.spec.ts icon=square-js
198
+ import { test, expect } from '@playwright/test'; // [!code --]
199
+ import { test, expect } from '@testdriver.ai/playwright'; // [!code ++]
200
+ ```
201
+
202
+ Click the <Icon icon="play" /> button to run the test and verify everything still works.
203
+
204
+ <Tip>
205
+ Click the <Icon icon="eye" /> button to automatically re-run tests on save.
206
+ </Tip>
207
+
208
+ </Step>
209
+ </Steps>
210
+
211
+ ## Usage
212
+
213
+ Because TestDriver uses AI vision instead of selectors, we can use natural language for
214
+ [assertions](#assertions-with-expect-tomatchprompt),
215
+ [locating](#locating-elements-with-testdriver-locate),
216
+ performing [actions](#performing-actions-with-testdriver-act),
217
+ or even having an [agent](#agentic-tests-with-test-agent) test for you!
218
+
219
+ ### Assertions with `expect.toMatchPrompt`
220
+
221
+ Replace `toBeVisible` with `toMatchPrompt` to assert that the element is visible on the screen:
222
+
223
+ ```typescript tests/example.spec.ts icon=square-js
224
+ // [!code --:2]
225
+ // Expects page to have a heading with the name of Installation.
226
+ await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible();
227
+ // [!code ++:2]
228
+ await expect(page).toMatchPrompt("'Installation' heading is visible");
229
+ ```
230
+
231
+ Before, the test needed code comments to describe what the assertion is _actually checking_.
232
+
233
+ With `toMatchPrompt`, natural language acts as a description, selector, and assertion in one
234
+
235
+ <Tip>
236
+ TestDriver can reduce the amount of complexity in your tests, but you can
237
+ still "opt-in" to Playwright assertions and selectors if you need to (e.g.
238
+ validating accessibility with `page.getByRole`).
239
+ </Tip>
240
+
241
+ ### Locating elements with `testdriver.locate`
242
+
243
+ TestDriver can replace `data-testid`s, `getByRole`, and CSS selectors with natural language.
244
+
245
+ First, update your test to get access to the `testdriver` fixture:
246
+
247
+ ```typescript tests/example.spec.ts icon=square-js
248
+ // [!code --]
249
+ test('get started link', async ({ page, }) => {
250
+ // [!code ++]
251
+ test('get started link', async ({ page, testdriver }) => {
252
+ ```
253
+
254
+ Then, replace `getByRole` with `testdriver.locate`:
255
+
256
+ ```typescript tests/example.spec.ts icon=square-js
257
+ test('get started link', async ({ page, testdriver }) => {
258
+ await page.goto('https://playwright.dev/');
259
+
260
+ // [!code --:2]
261
+ // Click the get started link.
262
+ await page.getByRole("link", { name: "Get started" }).click();
263
+ // [!code ++:2]
264
+ const link = await testdriver(page).locate("Get started link");
265
+ await link.click();
266
+
267
+ await expect(page).toMatchPrompt("'Installation' heading is visible");
268
+ ```
269
+
270
+ Now, our test uses natural language to both describe & locate the element.
271
+
272
+ <Tip>
273
+ In the example above, you can still use Playwright to assert that the element is indeed a link for accessibility:
274
+
275
+ ```typescript tests/example.spec.ts icon=square-js
276
+ const link = await testdriver(page).locate("Get started link");
277
+ // [!code ++]
278
+ expect(link).toHaveRole("link");
279
+ await link.click();
280
+ ```
281
+
282
+ This way you can write user-centric tests _and_ validate the implementation.
283
+
284
+ </Tip>
285
+
286
+ ### Performing actions with `testdriver.act`
287
+
288
+ We can combine `locate` and `click` from the previous example into one line with `testdriver.act`:
289
+
290
+ ```typescript tests/example.spec.ts icon=square-js
291
+ test("get started link", async ({ page, testdriver }) => {
292
+ await page.goto("https://playwright.dev/");
293
+
294
+ // [!code --:2]
295
+ const link = await testdriver(page).locate("Get started link");
296
+ await link.click();
297
+ // [!code ++]
298
+ await testdriver(page).act("Click the 'Get started' link");
299
+
300
+ await expect(page).toMatchPrompt("'Installation' heading is visible");
301
+ });
302
+ ```
303
+
304
+ Now the test uses the page the way a user would!
305
+
306
+ ### Agentic tests with `test.agent`
307
+
308
+ TestDriver can automatically perform the entire test for you with an AI agent:
309
+
310
+ ```typescript tests/example.spec.ts icon=square-js
311
+ // [!code --:6]
312
+ test("get started link", async ({ page, testdriver }) => {
313
+ await page.goto("https://playwright.dev/");
314
+ await testdriver(page).act("Click the 'Get started' link");
315
+ await expect(page).toMatchPrompt("'Installation' heading is visible");
316
+ });
317
+
318
+ // [!code ++:7]
319
+ test.describe("get started link", () => {
320
+ test.beforeEach(async ({ page }) => page.goto("https://playwright.dev/"));
321
+ test.agent(`
322
+ - Click the 'Get started' link
323
+ - Verify the 'Installation' heading is visible
324
+ `);
325
+ });
326
+ ```
327
+
328
+ Instead of writing the test implementation, we've used [`test.describe`](https://playwright.dev/docs/api/class-test#test-describe) to describe the test still,
329
+ but replaced the `test` itself with `test.agent`.
330
+
331
+ <Tip>
332
+ Use `test.beforeEach` to prepare the page for the agent (e.g.
333
+ [`page.goto`](https://playwright.dev/docs/api/class-page#page-goto), calling
334
+ an API to create a user). Use
335
+ [`test.afterEach`](https://playwright.dev/docs/api/class-test#test-after-each)
336
+ to clean up after the agent (e.g. `page.close`) or perform additional logic
337
+ (e.g. clearing the session).
338
+ </Tip>
339
+
340
+ ## Conclusion
341
+
342
+ With `@testdriver.ai/playwright`, you can use as much or as little of Playwright's _or_ TestDriver's API as you need to validate correctness. It's up to you!
package/eslint.config.js CHANGED
@@ -35,9 +35,27 @@ module.exports = [
35
35
  },
36
36
  },
37
37
  },
38
+ {
39
+ // Config for ES Module files (.mjs) - used in SDK tests
40
+ files: ["**/*.mjs"],
41
+ languageOptions: {
42
+ sourceType: "module",
43
+ ecmaVersion: 2022,
44
+ globals: {
45
+ ...globals.node,
46
+ },
47
+ },
48
+ },
38
49
  {
39
50
  // this needs to be it's own object for some reason
40
51
  // https://github.com/eslint/eslint/issues/17400
41
- ignores: ["agent/lib/subimage/**", "node_modules/**", ".git"],
52
+ ignores: [
53
+ "agent/lib/subimage/**",
54
+ "node_modules/**",
55
+ ".git",
56
+ "test-results/**",
57
+ "examples/test-recording-example.test.js",
58
+ "vitest.config.example.js",
59
+ ],
42
60
  },
43
61
  ];
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+
3
+ # Example script showing how to run tests with TestDriver + Dashcam integration
4
+ # This script demonstrates the complete flow:
5
+ # 1. Start dashcam
6
+ # 2. Run tests with Vitest reporter
7
+ # 3. Stop and publish dashcam recording
8
+ # 4. View results in TestDriver dashboard
9
+
10
+ set -e
11
+
12
+ echo "======================================"
13
+ echo "TestDriver + Dashcam Test Recording"
14
+ echo "======================================"
15
+ echo ""
16
+
17
+ # Check for required environment variables
18
+ if [ -z "$TD_API_KEY" ]; then
19
+ echo "Error: TD_API_KEY environment variable is required"
20
+ echo "Get your API key from: https://app.testdriver.ai/settings/api-keys"
21
+ exit 1
22
+ fi
23
+
24
+ if [ -z "$DASHCAM_PROJECT_ID" ]; then
25
+ echo "Warning: DASHCAM_PROJECT_ID not set, replays won't be published"
26
+ fi
27
+
28
+ # Start Dashcam recording
29
+ echo "📹 Starting Dashcam..."
30
+ dashcam start
31
+
32
+ # Track test logs
33
+ echo "📝 Tracking test output..."
34
+ dashcam track --name="TestDriver Tests" --type=application --pattern="*.log"
35
+
36
+ echo ""
37
+
38
+ # Run tests with Vitest and TestDriver reporter
39
+ echo "🧪 Running tests..."
40
+ npx vitest run --config vitest.config.example.js
41
+
42
+ # Capture exit code
43
+ TEST_EXIT_CODE=$?
44
+
45
+ # Stop dashcam
46
+ echo ""
47
+ echo "🛑 Stopping Dashcam..."
48
+ dashcam stop
49
+
50
+ # Publish to dashcam project if configured
51
+ if [ -n "$DASHCAM_PROJECT_ID" ]; then
52
+ echo "📤 Publishing replay to project: $DASHCAM_PROJECT_ID"
53
+ REPLAY_URL=$(dashcam publish -p "$DASHCAM_PROJECT_ID" --json | jq -r '.replayUrl')
54
+ echo "✅ Replay URL: $REPLAY_URL"
55
+ echo ""
56
+ echo "View your test recording at:"
57
+ echo "$REPLAY_URL"
58
+ else
59
+ echo "⚠️ Skipping publish (DASHCAM_PROJECT_ID not set)"
60
+ fi
61
+
62
+ echo ""
63
+ echo "======================================"
64
+ echo "📊 View Results"
65
+ echo "======================================"
66
+ echo "Dashboard: https://app.testdriver.ai/dashboard/test-runs"
67
+ echo ""
68
+
69
+ # Exit with the same code as tests
70
+ exit $TEST_EXIT_CODE
@@ -0,0 +1,63 @@
1
+ /**
2
+ * TestDriver SDK - Screenshot Example
3
+ *
4
+ * This example demonstrates how to use the screenshot() method
5
+ * to capture screenshots of the sandbox environment.
6
+ */
7
+
8
+ const TestDriver = require("../sdk.js");
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+
12
+ async function main() {
13
+ // Initialize TestDriver SDK
14
+ const client = new TestDriver(process.env.TD_API_KEY);
15
+
16
+ try {
17
+ // Connect to sandbox
18
+ console.log("Connecting to sandbox...");
19
+ await client.connect();
20
+
21
+ // Navigate to a website
22
+ console.log("Opening Chrome and navigating to example.com...");
23
+ await client.focusApplication("Google Chrome");
24
+ const urlBar = await client.find("URL bar in Chrome");
25
+ await urlBar.click();
26
+ await client.type("https://example.com");
27
+ await client.pressKeys(["enter"]);
28
+
29
+ // Wait a moment for page to load
30
+ await client.wait(2000);
31
+
32
+ // Capture a screenshot
33
+ console.log("Capturing screenshot...");
34
+ const screenshot = await client.screenshot();
35
+
36
+ // Save screenshot to file
37
+ const outputPath = path.join(__dirname, "example-screenshot.png");
38
+ fs.writeFileSync(outputPath, Buffer.from(screenshot, "base64"));
39
+ console.log(`Screenshot saved to: ${outputPath}`);
40
+
41
+ // You can also capture with the mouse cursor visible
42
+ console.log("Capturing screenshot with mouse cursor...");
43
+ await client.hover(400, 300);
44
+ const screenshotWithMouse = await client.screenshot(1, false, true);
45
+
46
+ const outputPathWithMouse = path.join(
47
+ __dirname,
48
+ "example-screenshot-with-mouse.png",
49
+ );
50
+ fs.writeFileSync(
51
+ outputPathWithMouse,
52
+ Buffer.from(screenshotWithMouse, "base64"),
53
+ );
54
+ console.log(`Screenshot with mouse saved to: ${outputPathWithMouse}`);
55
+ } catch (error) {
56
+ console.error("Error:", error);
57
+ } finally {
58
+ // Clean up
59
+ await client.disconnect();
60
+ }
61
+ }
62
+
63
+ main();
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * TestDriver SDK - AWESOME Logs Demo 🎨
5
+ *
6
+ * This example showcases the beautiful, emoji-rich logging with great DX
7
+ * that makes your test output a joy to read!
8
+ *
9
+ * Run: TD_API_KEY=your_key node examples/sdk-awesome-logs-demo.js
10
+ */
11
+
12
+ const TestDriver = require("../sdk.js");
13
+ const { formatter } = require("../sdk-log-formatter.js");
14
+
15
+ (async () => {
16
+ try {
17
+ console.log(
18
+ formatter.formatHeader("TestDriver SDK - AWESOME Logs Demo", "🚀"),
19
+ );
20
+
21
+ // Create client with logging enabled
22
+ const client = new TestDriver(process.env.TD_API_KEY, {
23
+ os: "windows",
24
+ logging: true,
25
+ });
26
+
27
+ // Demo 1: Connection
28
+ console.log(
29
+ formatter.formatConnection("connect", {
30
+ sandboxId: "demo-sandbox-123",
31
+ os: "Windows",
32
+ }),
33
+ );
34
+
35
+ await client.connect({ headless: true });
36
+
37
+ // Demo 2: Navigation
38
+ console.log(
39
+ "\n" + formatter.formatAction("navigate", "https://example.com"),
40
+ );
41
+ await client.focusApplication("Google Chrome");
42
+ await client.type("https://example.com");
43
+ await client.pressKeys(["enter"]);
44
+
45
+ // Demo 3: Wait with loading indicator
46
+ console.log("\n" + formatter.formatWaiting("for page to load", 2000));
47
+ await new Promise((resolve) => setTimeout(resolve, 2000));
48
+
49
+ // Demo 4: Finding elements (this will use the real formatElementFound from sdk.js)
50
+ console.log("\n" + formatter.formatHeader("Finding Elements", "🔍"));
51
+
52
+ const heading = await client.find("heading that says Example Domain");
53
+ // The actual find() call will emit the formatted log automatically
54
+
55
+ if (heading.found()) {
56
+ await heading.click();
57
+ // The click will also emit a formatted log automatically
58
+ }
59
+
60
+ // Demo 5: Multiple actions
61
+ console.log("\n" + formatter.formatHeader("User Actions", "👆"));
62
+
63
+ const link = await client.find("More information link");
64
+ if (link.found()) {
65
+ await link.hover();
66
+ await link.click();
67
+ }
68
+
69
+ // Demo 6: Typing
70
+ console.log(
71
+ "\n" +
72
+ formatter.formatAction("type", "search query", {
73
+ text: "TestDriver AI",
74
+ }),
75
+ );
76
+
77
+ // Demo 7: Scroll
78
+ console.log("\n" + formatter.formatAction("scroll", "down the page"));
79
+ await client.scroll("down", 300);
80
+
81
+ // Demo 8: Assertions
82
+ console.log("\n" + formatter.formatHeader("Assertions", "✅"));
83
+ console.log(
84
+ formatter.formatAssertion("Page title is correct", true, {
85
+ duration: "45ms",
86
+ }),
87
+ );
88
+ console.log(
89
+ formatter.formatAssertion("Footer is visible", true, {
90
+ duration: "23ms",
91
+ }),
92
+ );
93
+ console.log(
94
+ formatter.formatAssertion("Login button exists", false, {
95
+ duration: "1234ms",
96
+ }),
97
+ );
98
+
99
+ // Demo 9: Cache status
100
+ console.log("\n" + formatter.formatHeader("Cache Performance", "⚡"));
101
+ console.log(
102
+ formatter.formatCacheStatus(true, {
103
+ similarity: 0.98,
104
+ strategy: "image",
105
+ }),
106
+ );
107
+ console.log(
108
+ formatter.formatCacheStatus(false, {
109
+ strategy: "text",
110
+ }),
111
+ );
112
+
113
+ // Demo 10: Screenshot
114
+ console.log(
115
+ "\n" +
116
+ formatter.formatScreenshot({
117
+ path: "/tmp/testdriver-debug/screenshot-123.png",
118
+ size: "245 KB",
119
+ }),
120
+ );
121
+
122
+ // Demo 11: Progress
123
+ console.log("\n" + formatter.formatHeader("Multi-step Process", "📈"));
124
+ for (let i = 1; i <= 5; i++) {
125
+ console.log(formatter.formatProgress(i, 5, `Processing step ${i}`));
126
+ await new Promise((resolve) => setTimeout(resolve, 300));
127
+ }
128
+
129
+ // Demo 12: Divider
130
+ console.log("\n" + formatter.formatDivider());
131
+
132
+ // Demo 13: Test summary
133
+ console.log(
134
+ formatter.formatSummary({
135
+ passed: 12,
136
+ failed: 2,
137
+ skipped: 1,
138
+ total: 15,
139
+ duration: "45.23s",
140
+ }),
141
+ );
142
+
143
+ // Demo 14: Error handling
144
+ console.log(formatter.formatHeader("Error Examples", "🚨"));
145
+ console.log(
146
+ formatter.formatError(
147
+ "Element not found",
148
+ new Error("Timeout after 5000ms"),
149
+ ),
150
+ );
151
+ console.log(
152
+ formatter.formatError(
153
+ "Connection failed",
154
+ new Error("Network unreachable"),
155
+ ),
156
+ );
157
+
158
+ // Demo 15: Test lifecycle
159
+ console.log(formatter.formatTestStart("Login Flow Test"));
160
+ await new Promise((resolve) => setTimeout(resolve, 1000));
161
+ console.log(formatter.formatTestEnd("Login Flow Test", true, 2345));
162
+
163
+ console.log(formatter.formatTestStart("Checkout Process"));
164
+ await new Promise((resolve) => setTimeout(resolve, 800));
165
+ console.log(formatter.formatTestEnd("Checkout Process", false, 5678));
166
+
167
+ // Final summary
168
+ console.log("\n" + formatter.formatHeader("Demo Complete!", "🎉"));
169
+ console.log("\n✨ Your test logs now look AWESOME! ✨\n");
170
+
171
+ await client.disconnect();
172
+ console.log("\n" + formatter.formatConnection("disconnect"));
173
+ } catch (error) {
174
+ console.error(formatter.formatError("Demo failed", error));
175
+ process.exit(1);
176
+ }
177
+ })();