testdriverai 6.2.2 → 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 (300) 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/AGENTS.md +550 -0
  5. package/CODEOWNERS +0 -1
  6. package/README.md +126 -0
  7. package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
  8. package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
  9. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  10. package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
  11. package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
  12. package/_testdriver/lifecycle/prerun.yaml +15 -0
  13. package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
  14. package/agent/index.js +300 -85
  15. package/agent/interface.js +15 -0
  16. package/agent/lib/cache.js +142 -0
  17. package/agent/lib/commander.js +1 -39
  18. package/agent/lib/commands.js +910 -296
  19. package/agent/lib/redraw.js +129 -41
  20. package/agent/lib/sandbox.js +29 -6
  21. package/agent/lib/sdk.js +22 -0
  22. package/agent/lib/system.js +0 -3
  23. package/agent/lib/validation.js +1 -7
  24. package/debug-locate-response.js +82 -0
  25. package/debugger/index.html +15 -4
  26. package/docs/ARCHITECTURE.md +424 -0
  27. package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
  28. package/docs/MIGRATION.md +425 -0
  29. package/docs/PRESETS.md +210 -0
  30. package/docs/QUICK_START_TEST_RECORDING.md +215 -0
  31. package/docs/SDK_AWESOME_LOGS.md +468 -0
  32. package/docs/TEST_RECORDING.md +388 -0
  33. package/docs/docs.json +286 -152
  34. package/docs/guide/best-practices-polling.mdx +154 -0
  35. package/docs/sdk-browser-rendering.md +167 -0
  36. package/docs/v6/getting-started/self-hosting.mdx +407 -0
  37. package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
  38. package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
  39. package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
  40. package/docs/v6/overview/comparison.mdx +101 -0
  41. package/docs/v7/README.md +135 -0
  42. package/docs/v7/api/ai.mdx +205 -0
  43. package/docs/v7/api/assert.mdx +285 -0
  44. package/docs/v7/api/assertions.mdx +403 -0
  45. package/docs/v7/api/click.mdx +287 -0
  46. package/docs/v7/api/client.mdx +322 -0
  47. package/docs/v7/api/dashcam.mdx +497 -0
  48. package/docs/v7/api/doubleClick.mdx +102 -0
  49. package/docs/v7/api/elements.mdx +479 -0
  50. package/docs/v7/api/exec.mdx +346 -0
  51. package/docs/v7/api/find.mdx +316 -0
  52. package/docs/v7/api/focusApplication.mdx +294 -0
  53. package/docs/v7/api/hover.mdx +279 -0
  54. package/docs/v7/api/mouseDown.mdx +161 -0
  55. package/docs/v7/api/mouseUp.mdx +164 -0
  56. package/docs/v7/api/pressKeys.mdx +349 -0
  57. package/docs/v7/api/rightClick.mdx +123 -0
  58. package/docs/v7/api/sandbox.mdx +404 -0
  59. package/docs/v7/api/scroll.mdx +300 -0
  60. package/docs/v7/api/type.mdx +314 -0
  61. package/docs/v7/commands/assert.mdx +45 -0
  62. package/docs/v7/commands/exec.mdx +282 -0
  63. package/docs/v7/commands/focus-application.mdx +44 -0
  64. package/docs/v7/commands/hover-image.mdx +69 -0
  65. package/docs/v7/commands/hover-text.mdx +47 -0
  66. package/docs/v7/commands/if.mdx +53 -0
  67. package/docs/v7/commands/match-image.mdx +67 -0
  68. package/docs/v7/commands/press-keys.mdx +87 -0
  69. package/docs/v7/commands/remember.mdx +49 -0
  70. package/docs/v7/commands/run.mdx +44 -0
  71. package/docs/v7/commands/scroll-until-image.mdx +66 -0
  72. package/docs/v7/commands/scroll-until-text.mdx +60 -0
  73. package/docs/v7/commands/scroll.mdx +69 -0
  74. package/docs/v7/commands/type.mdx +45 -0
  75. package/docs/v7/commands/wait-for-image.mdx +54 -0
  76. package/docs/v7/commands/wait-for-text.mdx +48 -0
  77. package/docs/v7/commands/wait.mdx +45 -0
  78. package/docs/v7/getting-started/configuration.mdx +380 -0
  79. package/docs/v7/getting-started/quickstart.mdx +332 -0
  80. package/docs/v7/guides/best-practices.mdx +486 -0
  81. package/docs/v7/guides/caching-ai.mdx +215 -0
  82. package/docs/v7/guides/caching-selectors.mdx +292 -0
  83. package/docs/v7/guides/caching.mdx +366 -0
  84. package/docs/v7/guides/ci-cd/azure.mdx +587 -0
  85. package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
  86. package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
  87. package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
  88. package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
  89. package/docs/v7/guides/ci-cd/travis.mdx +438 -0
  90. package/docs/v7/guides/debugging.mdx +349 -0
  91. package/docs/v7/guides/faq.mdx +393 -0
  92. package/docs/v7/guides/migration.mdx +562 -0
  93. package/docs/v7/guides/performance.mdx +517 -0
  94. package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
  95. package/docs/v7/guides/troubleshooting.mdx +526 -0
  96. package/docs/v7/guides/vitest-plugin.mdx +477 -0
  97. package/docs/v7/guides/vitest.mdx +535 -0
  98. package/docs/v7/platforms/linux.mdx +308 -0
  99. package/docs/v7/platforms/macos.mdx +433 -0
  100. package/docs/v7/platforms/windows.mdx +430 -0
  101. package/docs/v7/playwright.mdx +342 -0
  102. package/docs/v7/presets/chrome-extension.mdx +223 -0
  103. package/docs/v7/presets/chrome.mdx +287 -0
  104. package/docs/v7/presets/electron.mdx +435 -0
  105. package/docs/v7/presets/vscode.mdx +398 -0
  106. package/docs/v7/presets/webapp.mdx +396 -0
  107. package/docs/v7/progressive-apis/CORE.md +459 -0
  108. package/docs/v7/progressive-apis/HOOKS.md +360 -0
  109. package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
  110. package/docs/v7/progressive-apis/PROVISION.md +266 -0
  111. package/eslint.config.js +19 -1
  112. package/interfaces/cli/lib/base.js +10 -4
  113. package/interfaces/logger.js +2 -1
  114. package/interfaces/shared-test-state.mjs +69 -0
  115. package/interfaces/vitest-plugin.mjs +830 -0
  116. package/package.json +29 -5
  117. package/schema.json +8 -29
  118. package/scripts/view-test-results.mjs +96 -0
  119. package/sdk-log-formatter.js +714 -0
  120. package/sdk.d.ts +1028 -0
  121. package/sdk.js +2567 -0
  122. package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
  123. package/setup/aws/cloudformation.yaml +9 -2
  124. package/src/core/Dashcam.js +469 -0
  125. package/src/core/index.d.ts +150 -0
  126. package/src/core/index.js +12 -0
  127. package/src/presets/index.mjs +331 -0
  128. package/src/vitest/extended.mjs +108 -0
  129. package/src/vitest/hooks.d.ts +119 -0
  130. package/src/vitest/hooks.mjs +298 -0
  131. package/src/vitest/index.mjs +64 -0
  132. package/src/vitest/lifecycle.mjs +277 -0
  133. package/src/vitest/utils.mjs +150 -0
  134. package/test/dashcam.test.js +137 -0
  135. package/test/mcp-example-test.yaml +27 -0
  136. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
  137. package/testdriver/acceptance-sdk/README.md +128 -0
  138. package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
  139. package/testdriver/acceptance-sdk/assert.test.mjs +26 -0
  140. package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
  141. package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
  142. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +58 -0
  143. package/testdriver/acceptance-sdk/element-not-found.test.mjs +25 -0
  144. package/testdriver/acceptance-sdk/exec-js.test.mjs +43 -0
  145. package/testdriver/acceptance-sdk/exec-output.test.mjs +59 -0
  146. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +57 -0
  147. package/testdriver/acceptance-sdk/focus-window.test.mjs +36 -0
  148. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +26 -0
  149. package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
  150. package/testdriver/acceptance-sdk/hover-image.test.mjs +34 -0
  151. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +38 -0
  152. package/testdriver/acceptance-sdk/hover-text.test.mjs +27 -0
  153. package/testdriver/acceptance-sdk/match-image.test.mjs +36 -0
  154. package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
  155. package/testdriver/acceptance-sdk/press-keys.test.mjs +50 -0
  156. package/testdriver/acceptance-sdk/prompt.test.mjs +33 -0
  157. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +38 -0
  158. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +39 -0
  159. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +28 -0
  160. package/testdriver/acceptance-sdk/scroll.test.mjs +41 -0
  161. package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
  162. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +420 -0
  163. package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
  164. package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
  165. package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
  166. package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
  167. package/testdriver/acceptance-sdk/type.test.mjs +45 -0
  168. package/verify-element-api.js +89 -0
  169. package/verify-types.js +0 -0
  170. package/vitest.config.example.js +19 -0
  171. package/vitest.config.mjs +66 -0
  172. package/vitest.config.mjs.bak +44 -0
  173. package/.github/workflows/acceptance-v6.yml +0 -169
  174. package/.vscode/mcp.json +0 -9
  175. package/docs/overview/comparison.mdx +0 -82
  176. package/testdriver/lifecycle/prerun.yaml +0 -17
  177. /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
  178. /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
  179. /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
  180. /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
  181. /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
  182. /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
  183. /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
  184. /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
  185. /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
  186. /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
  187. /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
  188. /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
  189. /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
  190. /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
  191. /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
  192. /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
  193. /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
  194. /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
  195. /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
  196. /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
  197. /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
  198. /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
  199. /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
  200. /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
  201. /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
  202. /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
  203. /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
  204. /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
  205. /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
  206. /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
  207. /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
  208. /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
  209. /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
  210. /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
  211. /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
  212. /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
  213. /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
  214. /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
  215. /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
  216. /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
  217. /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
  218. /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
  219. /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
  220. /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
  221. /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
  222. /package/docs/{account → v6/account}/dashboard.mdx +0 -0
  223. /package/docs/{account → v6/account}/enterprise.mdx +0 -0
  224. /package/docs/{account → v6/account}/pricing.mdx +0 -0
  225. /package/docs/{account → v6/account}/projects.mdx +0 -0
  226. /package/docs/{account → v6/account}/team.mdx +0 -0
  227. /package/docs/{action → v6/action}/ami.mdx +0 -0
  228. /package/docs/{action → v6/action}/performance.mdx +0 -0
  229. /package/docs/{action → v6/action}/secrets.mdx +0 -0
  230. /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
  231. /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
  232. /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
  233. /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
  234. /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
  235. /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
  236. /package/docs/{cli → v6/cli}/overview.mdx +0 -0
  237. /package/docs/{commands → v6/commands}/assert.mdx +0 -0
  238. /package/docs/{commands → v6/commands}/exec.mdx +0 -0
  239. /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
  240. /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
  241. /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
  242. /package/docs/{commands → v6/commands}/if.mdx +0 -0
  243. /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
  244. /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
  245. /package/docs/{commands → v6/commands}/remember.mdx +0 -0
  246. /package/docs/{commands → v6/commands}/run.mdx +0 -0
  247. /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
  248. /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
  249. /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
  250. /package/docs/{commands → v6/commands}/type.mdx +0 -0
  251. /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
  252. /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
  253. /package/docs/{commands → v6/commands}/wait.mdx +0 -0
  254. /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
  255. /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
  256. /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
  257. /package/docs/{features → v6/features}/generation.mdx +0 -0
  258. /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
  259. /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
  260. /package/docs/{features → v6/features}/selectorless.mdx +0 -0
  261. /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
  262. /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
  263. /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
  264. /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
  265. /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
  266. /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
  267. /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
  268. /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
  269. /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
  270. /package/docs/{guide → v6/guide}/code.mdx +0 -0
  271. /package/docs/{guide → v6/guide}/locating.mdx +0 -0
  272. /package/docs/{guide → v6/guide}/protips.mdx +0 -0
  273. /package/docs/{guide → v6/guide}/variables.mdx +0 -0
  274. /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
  275. /package/docs/{importing → v6/importing}/csv.mdx +0 -0
  276. /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
  277. /package/docs/{importing → v6/importing}/jira.mdx +0 -0
  278. /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
  279. /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
  280. /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
  281. /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
  282. /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
  283. /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
  284. /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
  285. /package/docs/{overview → v6/overview}/faq.mdx +0 -0
  286. /package/docs/{overview → v6/overview}/performance.mdx +0 -0
  287. /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
  288. /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
  289. /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
  290. /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
  291. /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
  292. /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
  293. /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
  294. /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
  295. /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
  296. /package/docs/{security → v6/security}/action.mdx +0 -0
  297. /package/docs/{security → v6/security}/agent.mdx +0 -0
  298. /package/docs/{security → v6/security}/platform.mdx +0 -0
  299. /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
  300. /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
@@ -0,0 +1,664 @@
1
+ ---
2
+ title: "Jenkins"
3
+ description: "Run TestDriver tests in Jenkins"
4
+ icon: "jenkins"
5
+ ---
6
+
7
+ ## Pipeline Setup
8
+
9
+ Create `Jenkinsfile`:
10
+
11
+ ```groovy
12
+ pipeline {
13
+ agent {
14
+ docker {
15
+ image 'node:18'
16
+ }
17
+ }
18
+
19
+ environment {
20
+ TD_API_KEY = credentials('td-api-key')
21
+ }
22
+
23
+ stages {
24
+ stage('Install') {
25
+ steps {
26
+ sh 'npm ci'
27
+ }
28
+ }
29
+
30
+ stage('Test') {
31
+ steps {
32
+ sh 'npx vitest'
33
+ }
34
+ }
35
+ }
36
+
37
+ post {
38
+ always {
39
+ archiveArtifacts artifacts: 'test-results/**', allowEmptyArchive: true
40
+ junit 'test-results/junit.xml'
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Add API Key
47
+
48
+ ### Using Credentials Plugin
49
+
50
+ 1. Go to **Manage Jenkins** → **Credentials**
51
+ 2. Select domain (usually "Global")
52
+ 3. Click **Add Credentials**
53
+ 4. Kind: **Secret text**
54
+ 5. Secret: Your API key from [dashboard.testdriver.ai](https://dashboard.testdriver.ai)
55
+ 6. ID: `td-api-key`
56
+ 7. Description: "TestDriver API Key"
57
+ 8. Click **OK**
58
+
59
+ ### Using Environment Variables
60
+
61
+ Or set directly in Jenkins:
62
+
63
+ 1. Go to **Manage Jenkins** → **Configure System**
64
+ 2. Find **Global properties**
65
+ 3. Check **Environment variables**
66
+ 4. Add:
67
+ - Name: `TD_API_KEY`
68
+ - Value: Your API key
69
+ 5. Click **Save**
70
+
71
+ ## Parallel Tests
72
+
73
+ Run tests in parallel stages:
74
+
75
+ ```groovy
76
+ pipeline {
77
+ agent {
78
+ docker {
79
+ image 'node:18'
80
+ }
81
+ }
82
+
83
+ environment {
84
+ TD_API_KEY = credentials('td-api-key')
85
+ }
86
+
87
+ stages {
88
+ stage('Install') {
89
+ steps {
90
+ sh 'npm ci'
91
+ }
92
+ }
93
+
94
+ stage('Test') {
95
+ parallel {
96
+ stage('Shard 1') {
97
+ steps {
98
+ sh 'npx vitest --shard=1/4'
99
+ }
100
+ }
101
+ stage('Shard 2') {
102
+ steps {
103
+ sh 'npx vitest --shard=2/4'
104
+ }
105
+ }
106
+ stage('Shard 3') {
107
+ steps {
108
+ sh 'npx vitest --shard=3/4'
109
+ }
110
+ }
111
+ stage('Shard 4') {
112
+ steps {
113
+ sh 'npx vitest --shard=4/4'
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ post {
121
+ always {
122
+ junit 'test-results/junit.xml'
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Save Dashcam URLs
129
+
130
+ Extract replay URLs from test output:
131
+
132
+ ```groovy
133
+ pipeline {
134
+ agent {
135
+ docker {
136
+ image 'node:18'
137
+ }
138
+ }
139
+
140
+ environment {
141
+ TD_API_KEY = credentials('td-api-key')
142
+ }
143
+
144
+ stages {
145
+ stage('Install') {
146
+ steps {
147
+ sh 'npm ci'
148
+ }
149
+ }
150
+
151
+ stage('Test') {
152
+ steps {
153
+ sh '''
154
+ npx vitest 2>&1 | tee test-output.log
155
+ grep -o 'https://dashcam.testdriver.ai/[a-zA-Z0-9-]*' test-output.log > dashcam-urls.txt || true
156
+ '''
157
+ }
158
+ }
159
+ }
160
+
161
+ post {
162
+ always {
163
+ archiveArtifacts artifacts: 'test-results/**, test-output.log, dashcam-urls.txt', allowEmptyArchive: true
164
+ junit 'test-results/junit.xml'
165
+ }
166
+ failure {
167
+ script {
168
+ def urls = readFile('dashcam-urls.txt').trim()
169
+ echo "Dashcam URLs:\n${urls}"
170
+ }
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Scheduled Builds
177
+
178
+ Configure build triggers:
179
+
180
+ ```groovy
181
+ pipeline {
182
+ triggers {
183
+ // Poll SCM every 6 hours
184
+ pollSCM('H */6 * * *')
185
+
186
+ // Or schedule directly (2 AM daily)
187
+ cron('0 2 * * *')
188
+ }
189
+
190
+ agent {
191
+ docker {
192
+ image 'node:18'
193
+ }
194
+ }
195
+
196
+ environment {
197
+ TD_API_KEY = credentials('td-api-key')
198
+ }
199
+
200
+ stages {
201
+ stage('Test') {
202
+ steps {
203
+ sh 'npm ci'
204
+ sh 'npx vitest'
205
+ }
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## Multiple Node Versions
212
+
213
+ Test across Node.js versions:
214
+
215
+ ```groovy
216
+ pipeline {
217
+ agent none
218
+
219
+ environment {
220
+ TD_API_KEY = credentials('td-api-key')
221
+ }
222
+
223
+ stages {
224
+ stage('Test') {
225
+ matrix {
226
+ axes {
227
+ axis {
228
+ name 'NODE_VERSION'
229
+ values '16', '18', '20'
230
+ }
231
+ }
232
+ agent {
233
+ docker {
234
+ image "node:${NODE_VERSION}"
235
+ }
236
+ }
237
+ stages {
238
+ stage('Install') {
239
+ steps {
240
+ sh 'npm ci'
241
+ }
242
+ }
243
+ stage('Run Tests') {
244
+ steps {
245
+ sh 'npx vitest'
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ post {
254
+ always {
255
+ junit 'test-results/junit.xml'
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ ## Conditional Execution
262
+
263
+ Skip tests on documentation changes:
264
+
265
+ ```groovy
266
+ pipeline {
267
+ agent {
268
+ docker {
269
+ image 'node:18'
270
+ }
271
+ }
272
+
273
+ stages {
274
+ stage('Check Changes') {
275
+ steps {
276
+ script {
277
+ def changes = sh(
278
+ script: 'git diff --name-only HEAD~1',
279
+ returnStdout: true
280
+ ).trim()
281
+
282
+ if (changes ==~ /.*\.(md|txt)$/) {
283
+ echo "Only docs changed, skipping tests"
284
+ currentBuild.result = 'SUCCESS'
285
+ return
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ stage('Test') {
292
+ when {
293
+ expression { currentBuild.result != 'SUCCESS' }
294
+ }
295
+ steps {
296
+ sh 'npm ci'
297
+ sh 'npx vitest'
298
+ }
299
+ }
300
+ }
301
+ }
302
+ ```
303
+
304
+ ## Retry Failed Tests
305
+
306
+ Automatically retry on failure:
307
+
308
+ ```groovy
309
+ pipeline {
310
+ agent {
311
+ docker {
312
+ image 'node:18'
313
+ }
314
+ }
315
+
316
+ environment {
317
+ TD_API_KEY = credentials('td-api-key')
318
+ }
319
+
320
+ stages {
321
+ stage('Test') {
322
+ steps {
323
+ retry(3) {
324
+ sh 'npm ci'
325
+ sh 'npx vitest'
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ ```
332
+
333
+ Or retry specific stages:
334
+
335
+ ```groovy
336
+ stage('Test') {
337
+ steps {
338
+ sh 'npm ci'
339
+ script {
340
+ try {
341
+ sh 'npx vitest'
342
+ } catch (Exception e) {
343
+ echo 'First attempt failed, retrying...'
344
+ sh 'npx vitest'
345
+ }
346
+ }
347
+ }
348
+ }
349
+ ```
350
+
351
+ ## Build Parameters
352
+
353
+ Allow manual parameter input:
354
+
355
+ ```groovy
356
+ pipeline {
357
+ agent {
358
+ docker {
359
+ image 'node:18'
360
+ }
361
+ }
362
+
363
+ parameters {
364
+ choice(
365
+ name: 'ENVIRONMENT',
366
+ choices: ['staging', 'production'],
367
+ description: 'Environment to test'
368
+ )
369
+ booleanParam(
370
+ name: 'PARALLEL',
371
+ defaultValue: true,
372
+ description: 'Run tests in parallel'
373
+ )
374
+ }
375
+
376
+ environment {
377
+ TD_API_KEY = credentials('td-api-key')
378
+ TEST_URL = "${params.ENVIRONMENT == 'production' ? 'https://example.com' : 'https://staging.example.com'}"
379
+ }
380
+
381
+ stages {
382
+ stage('Test') {
383
+ steps {
384
+ sh 'npm ci'
385
+ script {
386
+ if (params.PARALLEL) {
387
+ sh 'npx vitest --maxConcurrency=5'
388
+ } else {
389
+ sh 'npx vitest --maxConcurrency=1'
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+ ```
397
+
398
+ ## Notifications
399
+
400
+ Send notifications on failure:
401
+
402
+ ```groovy
403
+ pipeline {
404
+ agent {
405
+ docker {
406
+ image 'node:18'
407
+ }
408
+ }
409
+
410
+ environment {
411
+ TD_API_KEY = credentials('td-api-key')
412
+ }
413
+
414
+ stages {
415
+ stage('Test') {
416
+ steps {
417
+ sh 'npm ci'
418
+ sh 'npx vitest'
419
+ }
420
+ }
421
+ }
422
+
423
+ post {
424
+ failure {
425
+ emailext(
426
+ subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
427
+ body: """
428
+ Build failed: ${env.BUILD_URL}
429
+
430
+ Check Dashcam URLs in artifacts.
431
+ """,
432
+ to: 'team@example.com'
433
+ )
434
+ }
435
+ success {
436
+ echo 'Tests passed!'
437
+ }
438
+ }
439
+ }
440
+ ```
441
+
442
+ ## Timeout
443
+
444
+ Set build timeout:
445
+
446
+ ```groovy
447
+ pipeline {
448
+ agent {
449
+ docker {
450
+ image 'node:18'
451
+ }
452
+ }
453
+
454
+ options {
455
+ timeout(time: 30, unit: 'MINUTES')
456
+ timestamps()
457
+ buildDiscarder(logRotator(numToKeepStr: '10'))
458
+ }
459
+
460
+ stages {
461
+ stage('Test') {
462
+ steps {
463
+ timeout(time: 20, unit: 'MINUTES') {
464
+ sh 'npm ci'
465
+ sh 'npx vitest'
466
+ }
467
+ }
468
+ }
469
+ }
470
+ }
471
+ ```
472
+
473
+ ## Workspace Cleanup
474
+
475
+ Clean workspace before/after build:
476
+
477
+ ```groovy
478
+ pipeline {
479
+ agent {
480
+ docker {
481
+ image 'node:18'
482
+ }
483
+ }
484
+
485
+ options {
486
+ skipDefaultCheckout()
487
+ }
488
+
489
+ stages {
490
+ stage('Checkout') {
491
+ steps {
492
+ cleanWs()
493
+ checkout scm
494
+ }
495
+ }
496
+
497
+ stage('Test') {
498
+ steps {
499
+ sh 'npm ci'
500
+ sh 'npx vitest'
501
+ }
502
+ }
503
+ }
504
+
505
+ post {
506
+ always {
507
+ cleanWs()
508
+ }
509
+ }
510
+ }
511
+ ```
512
+
513
+ ## Troubleshooting
514
+
515
+ ### Docker permission denied
516
+
517
+ If using Docker agent on self-hosted Jenkins:
518
+
519
+ ```bash
520
+ # Add Jenkins user to docker group
521
+ sudo usermod -aG docker jenkins
522
+ sudo systemctl restart jenkins
523
+ ```
524
+
525
+ ### API key not found
526
+
527
+ Debug credentials:
528
+
529
+ ```groovy
530
+ stage('Debug') {
531
+ steps {
532
+ sh '''
533
+ echo "Has TD_API_KEY: ${TD_API_KEY:+yes}"
534
+ node --version
535
+ npm --version
536
+ '''
537
+ }
538
+ }
539
+ ```
540
+
541
+ ### Tests timeout
542
+
543
+ Increase timeout:
544
+
545
+ ```groovy
546
+ options {
547
+ timeout(time: 60, unit: 'MINUTES')
548
+ }
549
+ ```
550
+
551
+ ## Complete Example
552
+
553
+ Full-featured Jenkinsfile:
554
+
555
+ ```groovy
556
+ pipeline {
557
+ agent {
558
+ docker {
559
+ image 'node:18'
560
+ args '-v /var/run/docker.sock:/var/run/docker.sock'
561
+ }
562
+ }
563
+
564
+ options {
565
+ timeout(time: 30, unit: 'MINUTES')
566
+ timestamps()
567
+ buildDiscarder(logRotator(numToKeepStr: '30'))
568
+ disableConcurrentBuilds()
569
+ }
570
+
571
+ triggers {
572
+ cron('0 2 * * *') // 2 AM daily
573
+ }
574
+
575
+ environment {
576
+ TD_API_KEY = credentials('td-api-key')
577
+ CI = 'true'
578
+ }
579
+
580
+ stages {
581
+ stage('Install') {
582
+ steps {
583
+ sh 'npm ci'
584
+ }
585
+ }
586
+
587
+ stage('Test') {
588
+ parallel {
589
+ stage('Shard 1/4') {
590
+ steps {
591
+ sh 'npx vitest --shard=1/4 2>&1 | tee test-output-1.log'
592
+ }
593
+ }
594
+ stage('Shard 2/4') {
595
+ steps {
596
+ sh 'npx vitest --shard=2/4 2>&1 | tee test-output-2.log'
597
+ }
598
+ }
599
+ stage('Shard 3/4') {
600
+ steps {
601
+ sh 'npx vitest --shard=3/4 2>&1 | tee test-output-3.log'
602
+ }
603
+ }
604
+ stage('Shard 4/4') {
605
+ steps {
606
+ sh 'npx vitest --shard=4/4 2>&1 | tee test-output-4.log'
607
+ }
608
+ }
609
+ }
610
+ }
611
+
612
+ stage('Extract Dashcam URLs') {
613
+ steps {
614
+ sh '''
615
+ cat test-output-*.log | \
616
+ grep -o 'https://dashcam.testdriver.ai/[a-zA-Z0-9-]*' > dashcam-urls.txt || true
617
+ '''
618
+ }
619
+ }
620
+ }
621
+
622
+ post {
623
+ always {
624
+ archiveArtifacts(
625
+ artifacts: 'test-results/**, test-output-*.log, dashcam-urls.txt',
626
+ allowEmptyArchive: true
627
+ )
628
+ junit(
629
+ testResults: 'test-results/junit.xml',
630
+ allowEmptyResults: true
631
+ )
632
+ }
633
+ failure {
634
+ script {
635
+ def urls = readFile('dashcam-urls.txt').trim()
636
+ echo "❌ Tests failed. Dashcam URLs:\n${urls}"
637
+ }
638
+ }
639
+ success {
640
+ echo '✅ All tests passed!'
641
+ }
642
+ }
643
+ }
644
+ ```
645
+
646
+ ## See Also
647
+
648
+ <CardGroup cols={2}>
649
+ <Card title="CI/CD Overview" icon="arrows-spin" href="/v7/guides/ci-cd/overview">
650
+ CI/CD concepts
651
+ </Card>
652
+
653
+ <Card title="Performance" icon="gauge" href="/v7/guides/performance">
654
+ Optimize tests
655
+ </Card>
656
+
657
+ <Card title="Troubleshooting" icon="circle-question" href="/v7/guides/troubleshooting">
658
+ Common issues
659
+ </Card>
660
+
661
+ <Card title="Jenkins Docs" icon="book" href="https://www.jenkins.io/doc/">
662
+ Official docs
663
+ </Card>
664
+ </CardGroup>