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.
- package/AGENTS.md +550 -0
- package/CODEOWNERS +0 -1
- package/README.md +126 -0
- package/agent/index.js +43 -18
- package/agent/lib/commands.js +794 -135
- package/agent/lib/redraw.js +124 -39
- package/agent/lib/sandbox.js +10 -1
- package/agent/lib/sdk.js +21 -0
- package/docs/MIGRATION.md +425 -0
- package/docs/PRESETS.md +210 -0
- package/docs/docs.json +91 -37
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/quickstart.mdx +273 -140
- package/docs/v7/guides/best-practices.mdx +486 -0
- package/docs/v7/guides/caching-ai.mdx +215 -0
- package/docs/v7/guides/caching-selectors.mdx +292 -0
- package/docs/v7/guides/caching.mdx +366 -0
- package/docs/v7/guides/ci-cd/azure.mdx +587 -0
- package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
- package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
- package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
- package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
- package/docs/v7/guides/ci-cd/travis.mdx +438 -0
- package/docs/v7/guides/debugging.mdx +349 -0
- package/docs/v7/guides/faq.mdx +393 -0
- package/docs/v7/guides/performance.mdx +517 -0
- package/docs/v7/guides/troubleshooting.mdx +526 -0
- package/docs/v7/guides/vitest-plugin.mdx +477 -0
- package/docs/v7/guides/vitest.mdx +535 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +287 -0
- package/docs/v7/presets/electron.mdx +435 -0
- package/docs/v7/presets/vscode.mdx +398 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/docs/v7/progressive-apis/CORE.md +459 -0
- package/docs/v7/progressive-apis/HOOKS.md +360 -0
- package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
- package/docs/v7/progressive-apis/PROVISION.md +266 -0
- package/interfaces/vitest-plugin.mjs +186 -100
- package/package.json +12 -1
- package/sdk.d.ts +335 -42
- package/sdk.js +756 -95
- package/src/core/Dashcam.js +469 -0
- package/src/core/index.d.ts +150 -0
- package/src/core/index.js +12 -0
- package/src/presets/index.mjs +331 -0
- package/src/vitest/extended.mjs +108 -0
- package/src/vitest/hooks.d.ts +119 -0
- package/src/vitest/hooks.mjs +298 -0
- package/src/vitest/index.mjs +64 -0
- package/src/vitest/lifecycle.mjs +277 -0
- package/src/vitest/utils.mjs +150 -0
- package/test/dashcam.test.js +137 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +13 -31
- package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
- package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +7 -19
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +6 -19
- package/testdriver/acceptance-sdk/exec-js.test.mjs +6 -18
- package/testdriver/acceptance-sdk/exec-output.test.mjs +8 -20
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +13 -25
- package/testdriver/acceptance-sdk/focus-window.test.mjs +8 -20
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +5 -20
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +10 -19
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +7 -19
- package/testdriver/acceptance-sdk/hover-text.test.mjs +5 -19
- package/testdriver/acceptance-sdk/match-image.test.mjs +7 -19
- package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +5 -19
- package/testdriver/acceptance-sdk/prompt.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +6 -20
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +9 -23
- package/testdriver/acceptance-sdk/scroll.test.mjs +12 -21
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +124 -352
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
- package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
- package/testdriver/acceptance-sdk/type.test.mjs +19 -58
- package/vitest.config.mjs +1 -0
- package/.vscode/mcp.json +0 -9
- package/MIGRATION.md +0 -389
- package/PLUGIN_MIGRATION.md +0 -222
- package/PROMPT_CACHE.md +0 -200
- package/SDK_LOGGING.md +0 -222
- package/SDK_MIGRATION.md +0 -474
- package/SDK_README.md +0 -1122
- package/debug-screenshot-1763401388589.png +0 -0
- package/examples/run-tests-with-recording.sh +0 -70
- package/examples/screenshot-example.js +0 -63
- package/examples/sdk-awesome-logs-demo.js +0 -177
- package/examples/sdk-cache-thresholds.js +0 -96
- package/examples/sdk-element-properties.js +0 -155
- package/examples/sdk-simple-example.js +0 -65
- package/examples/test-recording-example.test.js +0 -166
- package/mcp-server/AI_GUIDELINES.md +0 -57
- package/test-find-api.js +0 -73
- package/test-prompt-cache.js +0 -96
- package/test-sandbox-render.js +0 -28
- package/test-sdk-methods.js +0 -15
- package/test-sdk-refactor.js +0 -53
- package/test-stack-trace.mjs +0 -57
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
|
@@ -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>
|