dd-trace 5.99.0 → 5.100.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/LICENSE-3rdparty.csv +0 -1
- package/package.json +24 -5
- package/packages/datadog-instrumentations/src/cucumber.js +69 -5
- package/packages/datadog-instrumentations/src/express.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/hono.js +15 -4
- package/packages/datadog-instrumentations/src/jest.js +89 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +18 -22
- package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
- package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
- package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
- package/packages/datadog-instrumentations/src/playwright.js +108 -18
- package/packages/datadog-instrumentations/src/router.js +53 -33
- package/packages/datadog-instrumentations/src/vitest.js +76 -30
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-bullmq/src/consumer.js +3 -2
- package/packages/datadog-plugin-bullmq/src/producer.js +25 -11
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +32 -9
- package/packages/datadog-plugin-cypress/src/support.js +22 -21
- package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/utils.js +2 -2
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-memcached/src/index.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
- package/packages/datadog-plugin-playwright/src/index.js +6 -0
- package/packages/datadog-plugin-router/src/index.js +13 -0
- package/packages/dd-trace/index.js +4 -3
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/blocking.js +18 -6
- package/packages/dd-trace/src/appsec/graphql.js +1 -1
- package/packages/dd-trace/src/baggage.js +26 -13
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +45 -69
- package/packages/dd-trace/src/config/index.js +13 -12
- package/packages/dd-trace/src/config/normalize-service.js +31 -0
- package/packages/dd-trace/src/config/supported-configurations.json +31 -76
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +3 -3
- package/packages/dd-trace/src/exporter.js +1 -1
- package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
- package/packages/dd-trace/src/heap_snapshots.js +4 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
- package/packages/dd-trace/src/llmobs/sdk.js +21 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
- package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
- package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -8
- package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -5
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +6 -6
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +170 -0
- package/packages/dd-trace/src/opentelemetry/span.js +14 -42
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +44 -23
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +42 -12
- package/packages/dd-trace/src/opentracing/span.js +4 -3
- package/packages/dd-trace/src/plugin_manager.js +6 -6
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +119 -32
- package/packages/dd-trace/src/plugins/util/test.js +295 -29
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
- package/packages/dd-trace/src/propagation-hash/index.js +1 -1
- package/packages/dd-trace/src/proxy.js +9 -9
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
- package/packages/dd-trace/src/tracer_metadata.js +1 -1
- package/vendor/dist/path-to-regexp/LICENSE +0 -21
- package/vendor/dist/path-to-regexp/index.js +0 -1
|
@@ -14,8 +14,8 @@ const {
|
|
|
14
14
|
mergeCoverage,
|
|
15
15
|
resetCoverage,
|
|
16
16
|
getIsFaultyEarlyFlakeDetection,
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
collectTestOptimizationSummariesFromTraces,
|
|
18
|
+
logTestOptimizationSummary,
|
|
19
19
|
} = require('../../../dd-trace/src/plugins/util/test')
|
|
20
20
|
|
|
21
21
|
const {
|
|
@@ -34,9 +34,9 @@ const {
|
|
|
34
34
|
testsQuarantined,
|
|
35
35
|
getTestFullName,
|
|
36
36
|
getRunTestsWrapper,
|
|
37
|
-
testsAttemptToFix,
|
|
38
|
-
testsStatuses,
|
|
39
37
|
newTestsWithDynamicNames,
|
|
38
|
+
attemptToFixExecutions,
|
|
39
|
+
loggedAttemptToFixTests,
|
|
40
40
|
} = require('./utils')
|
|
41
41
|
|
|
42
42
|
require('./common')
|
|
@@ -146,26 +146,17 @@ function getOnEndHandler (isParallel) {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
// We
|
|
150
|
-
//
|
|
149
|
+
// We subtract the errors from quarantined tests from the total number of failures.
|
|
150
|
+
// Attempt-to-fix tests ignore quarantine/disabled suppression and keep their framework result.
|
|
151
151
|
if (config.isTestManagementTestsEnabled) {
|
|
152
152
|
let numFailedQuarantinedTests = 0
|
|
153
|
-
let numFailedRetriedQuarantinedOrDisabledTests = 0
|
|
154
|
-
for (const test of testsAttemptToFix) {
|
|
155
|
-
const testName = getTestFullName(test)
|
|
156
|
-
const testProperties = getTestProperties(test, config.testManagementTests)
|
|
157
|
-
if (isTestFailed(test) && (testProperties.isQuarantined || testProperties.isDisabled)) {
|
|
158
|
-
const numFailedTests = testsStatuses.get(testName).filter(status => status === 'fail').length
|
|
159
|
-
numFailedRetriedQuarantinedOrDisabledTests += numFailedTests
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
153
|
for (const test of testsQuarantined) {
|
|
163
154
|
if (isTestFailed(test)) {
|
|
164
155
|
numFailedQuarantinedTests++
|
|
165
156
|
}
|
|
166
157
|
}
|
|
167
|
-
this.stats.failures -= numFailedQuarantinedTests
|
|
168
|
-
this.failures -= numFailedQuarantinedTests
|
|
158
|
+
this.stats.failures -= numFailedQuarantinedTests
|
|
159
|
+
this.failures -= numFailedQuarantinedTests
|
|
169
160
|
}
|
|
170
161
|
|
|
171
162
|
// Recompute status after EFD and quarantine adjustments have reduced failure counts
|
|
@@ -211,7 +202,8 @@ function getOnEndHandler (isParallel) {
|
|
|
211
202
|
isParallel,
|
|
212
203
|
})
|
|
213
204
|
|
|
214
|
-
|
|
205
|
+
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
206
|
+
loggedAttemptToFixTests.clear()
|
|
215
207
|
}
|
|
216
208
|
}
|
|
217
209
|
|
|
@@ -467,9 +459,9 @@ addHook({
|
|
|
467
459
|
this.on('retry', getOnTestRetryHandler(config))
|
|
468
460
|
|
|
469
461
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
470
|
-
this.on('hook end', getOnHookEndHandler())
|
|
462
|
+
this.on('hook end', getOnHookEndHandler(config))
|
|
471
463
|
|
|
472
|
-
this.on('fail', getOnFailHandler(true))
|
|
464
|
+
this.on('fail', getOnFailHandler(true, config))
|
|
473
465
|
|
|
474
466
|
this.on('pending', getOnPendingHandler())
|
|
475
467
|
|
|
@@ -557,7 +549,10 @@ function onMessage (message) {
|
|
|
557
549
|
if (Array.isArray(message)) {
|
|
558
550
|
const [messageCode, payload] = message
|
|
559
551
|
if (messageCode === MOCHA_WORKER_TRACE_PAYLOAD_CODE) {
|
|
560
|
-
|
|
552
|
+
collectTestOptimizationSummariesFromTraces(payload, {
|
|
553
|
+
newTestsWithDynamicNames,
|
|
554
|
+
attemptToFixExecutions,
|
|
555
|
+
})
|
|
561
556
|
workerReportTraceCh.publish(payload)
|
|
562
557
|
}
|
|
563
558
|
}
|
|
@@ -771,7 +766,8 @@ addHook({
|
|
|
771
766
|
}
|
|
772
767
|
}
|
|
773
768
|
// `testsQuarantined` is filled in the worker process, so we need to use the test results to fill it here too.
|
|
774
|
-
|
|
769
|
+
const testProperties = getTestProperties(test, config.testManagementTests)
|
|
770
|
+
if (config.isTestManagementTestsEnabled && testProperties.isQuarantined && !testProperties.isAttemptToFix) {
|
|
775
771
|
testsQuarantined.add(test)
|
|
776
772
|
}
|
|
777
773
|
}
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
// Capture real timers at module load time, before any test can install fake timers.
|
|
4
4
|
const realSetTimeout = setTimeout
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
getTestSuitePath,
|
|
8
|
+
DYNAMIC_NAME_RE,
|
|
9
|
+
recordAttemptToFixExecution,
|
|
10
|
+
logAttemptToFixTestExecution,
|
|
11
|
+
} = require('../../../dd-trace/src/plugins/util/test')
|
|
7
12
|
const { channel } = require('../helpers/instrument')
|
|
8
13
|
const shimmer = require('../../../datadog-shimmer')
|
|
9
14
|
|
|
@@ -30,6 +35,8 @@ const newTestsWithDynamicNames = new Set()
|
|
|
30
35
|
const testsAttemptToFix = new Set()
|
|
31
36
|
const testsQuarantined = new Set()
|
|
32
37
|
const testsStatuses = new Map()
|
|
38
|
+
const attemptToFixExecutions = new Map()
|
|
39
|
+
const loggedAttemptToFixTests = new Set()
|
|
33
40
|
|
|
34
41
|
function getAfterEachHooks (testOrHook) {
|
|
35
42
|
const hooks = []
|
|
@@ -229,6 +236,13 @@ function getOnTestHandler (isMain) {
|
|
|
229
236
|
if (testInfo.hasDynamicName) {
|
|
230
237
|
newTestsWithDynamicNames.add(`${getTestSuitePath(test.file, process.cwd())} › ${test.fullTitle()}`)
|
|
231
238
|
}
|
|
239
|
+
if (isAttemptToFix) {
|
|
240
|
+
logAttemptToFixTestExecution(
|
|
241
|
+
getTestSuitePath(test.file, process.cwd()),
|
|
242
|
+
test.fullTitle(),
|
|
243
|
+
loggedAttemptToFixTests
|
|
244
|
+
)
|
|
245
|
+
}
|
|
232
246
|
// We want to store the result of the new tests
|
|
233
247
|
if (isNew) {
|
|
234
248
|
const testFullName = getTestFullName(test)
|
|
@@ -272,9 +286,8 @@ function getFinalStatus ({
|
|
|
272
286
|
return
|
|
273
287
|
}
|
|
274
288
|
|
|
275
|
-
// If the test is quarantined or disabled,
|
|
276
|
-
|
|
277
|
-
if (isQuarantined || isDisabled) {
|
|
289
|
+
// If the test is quarantined or disabled, its final status is skip unless attempt-to-fix takes precedence.
|
|
290
|
+
if (!isAttemptToFix && (isQuarantined || isDisabled)) {
|
|
278
291
|
return 'skip'
|
|
279
292
|
}
|
|
280
293
|
|
|
@@ -295,116 +308,126 @@ function getFinalStatus ({
|
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
|
|
298
|
-
function
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
311
|
+
function getTestFinishInfo (test, status, config, error) {
|
|
312
|
+
let hasFailedAllRetries = false
|
|
313
|
+
let attemptToFixPassed = false
|
|
314
|
+
let attemptToFixFailed = false
|
|
302
315
|
|
|
303
|
-
|
|
304
|
-
// This means that tests retried with DI are BREAKPOINT_HIT_GRACE_PERIOD_MS slower at least.
|
|
305
|
-
if (test._ddShouldWaitForHitProbe || test._retriedTest?._ddShouldWaitForHitProbe) {
|
|
306
|
-
await new Promise((resolve) => {
|
|
307
|
-
realSetTimeout(() => {
|
|
308
|
-
resolve()
|
|
309
|
-
}, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
let hasFailedAllRetries = false
|
|
314
|
-
let attemptToFixPassed = false
|
|
315
|
-
let attemptToFixFailed = false
|
|
316
|
-
|
|
317
|
-
const testName = getTestFullName(test)
|
|
316
|
+
const testName = getTestFullName(test)
|
|
318
317
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
318
|
+
if (testsStatuses.get(testName)) {
|
|
319
|
+
testsStatuses.get(testName).push(status)
|
|
320
|
+
} else {
|
|
321
|
+
testsStatuses.set(testName, [status])
|
|
322
|
+
}
|
|
323
|
+
const testStatuses = testsStatuses.get(testName)
|
|
325
324
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
325
|
+
const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
|
|
326
|
+
const isLastEfdRetry = testStatuses.length === config.earlyFlakeDetectionNumRetries + 1
|
|
327
|
+
const isLastAtrAttempt = getIsLastRetry(test) || (config.isFlakyTestRetriesEnabled && status === 'pass')
|
|
329
328
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
// Needed for the getFinalStatus call. This is because EFD does NOT tag as
|
|
330
|
+
// EFD retry the first run of the test. It only tags as retries the clones
|
|
331
|
+
const isEfdRetry = test._ddIsEfdRetry || (test._ddIsNew && config.isEarlyFlakeDetectionEnabled)
|
|
333
332
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
if (testStatuses.every(status => status === 'fail')) {
|
|
339
|
-
hasFailedAllRetries = true
|
|
340
|
-
} else if (testStatuses.every(status => status === 'pass')) {
|
|
341
|
-
attemptToFixPassed = true
|
|
342
|
-
}
|
|
333
|
+
if (test._ddIsAttemptToFix && isLastAttempt) {
|
|
334
|
+
if (testStatuses.includes('fail')) {
|
|
335
|
+
attemptToFixFailed = true
|
|
343
336
|
}
|
|
344
|
-
|
|
345
|
-
if (test._ddIsEfdRetry && isLastEfdRetry &&
|
|
346
|
-
testStatuses.every(status => status === 'fail')) {
|
|
337
|
+
if (testStatuses.every(status => status === 'fail')) {
|
|
347
338
|
hasFailedAllRetries = true
|
|
339
|
+
} else if (testStatuses.every(status => status === 'pass')) {
|
|
340
|
+
attemptToFixPassed = true
|
|
348
341
|
}
|
|
342
|
+
}
|
|
349
343
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
344
|
+
if (test._ddIsEfdRetry && isLastEfdRetry &&
|
|
345
|
+
testStatuses.every(status => status === 'fail')) {
|
|
346
|
+
hasFailedAllRetries = true
|
|
347
|
+
}
|
|
355
348
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
// ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
|
|
350
|
+
if (config.isFlakyTestRetriesEnabled && !test._ddIsAttemptToFix && !test._ddIsEfdRetry &&
|
|
351
|
+
getIsLastRetry(test) && testStatuses.every(status => status === 'fail')) {
|
|
352
|
+
hasFailedAllRetries = true
|
|
353
|
+
}
|
|
360
354
|
|
|
361
|
-
|
|
362
|
-
|
|
355
|
+
const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
|
|
356
|
+
const isAtrRetry = config.isFlakyTestRetriesEnabled &&
|
|
357
|
+
!test._ddIsAttemptToFix &&
|
|
358
|
+
!test._ddIsEfdRetry
|
|
359
|
+
|
|
360
|
+
const { isFlakyTestRetriesEnabled } = config
|
|
361
|
+
const { _ddIsAttemptToFix, _ddIsQuarantined, _ddIsDisabled } = test
|
|
362
|
+
|
|
363
|
+
const finalStatus = getFinalStatus({
|
|
364
|
+
status,
|
|
365
|
+
hasFailedAllRetries,
|
|
366
|
+
isFlakyTestRetriesEnabled,
|
|
367
|
+
isLastAtrAttempt,
|
|
368
|
+
isEfdRetry,
|
|
369
|
+
isLastEfdRetry,
|
|
370
|
+
isAttemptToFix: _ddIsAttemptToFix,
|
|
371
|
+
isLastAttemptToFix: isLastAttempt,
|
|
372
|
+
attemptToFixPassed,
|
|
373
|
+
isQuarantined: _ddIsQuarantined,
|
|
374
|
+
isDisabled: _ddIsDisabled,
|
|
375
|
+
})
|
|
363
376
|
|
|
364
|
-
|
|
377
|
+
if (_ddIsAttemptToFix) {
|
|
378
|
+
recordAttemptToFixExecution(attemptToFixExecutions, {
|
|
379
|
+
testSuite: getTestSuitePath(test.file, process.cwd()),
|
|
380
|
+
testName: test.fullTitle(),
|
|
365
381
|
status,
|
|
366
|
-
hasFailedAllRetries,
|
|
367
|
-
isFlakyTestRetriesEnabled,
|
|
368
|
-
isLastAtrAttempt,
|
|
369
|
-
isEfdRetry,
|
|
370
|
-
isLastEfdRetry,
|
|
371
|
-
isAttemptToFix: _ddIsAttemptToFix,
|
|
372
|
-
isLastAttemptToFix: isLastAttempt,
|
|
373
|
-
attemptToFixPassed,
|
|
374
|
-
isQuarantined: _ddIsQuarantined,
|
|
375
382
|
isDisabled: _ddIsDisabled,
|
|
383
|
+
isQuarantined: _ddIsQuarantined,
|
|
376
384
|
})
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
hasFailedAllRetries,
|
|
389
|
+
attemptToFixPassed,
|
|
390
|
+
attemptToFixFailed,
|
|
391
|
+
isAttemptToFixRetry,
|
|
392
|
+
isAtrRetry,
|
|
393
|
+
finalStatus,
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getOnTestEndHandler (config) {
|
|
398
|
+
return async function (test) {
|
|
399
|
+
const ctx = getTestContext(test)
|
|
400
|
+
const status = getTestStatus(test)
|
|
401
|
+
|
|
402
|
+
// After finishing it might take a bit for the snapshot to be handled.
|
|
403
|
+
// This means that tests retried with DI are BREAKPOINT_HIT_GRACE_PERIOD_MS slower at least.
|
|
404
|
+
if (test._ddShouldWaitForHitProbe || test._retriedTest?._ddShouldWaitForHitProbe) {
|
|
405
|
+
await new Promise((resolve) => {
|
|
406
|
+
realSetTimeout(() => {
|
|
407
|
+
resolve()
|
|
408
|
+
}, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
409
|
+
})
|
|
410
|
+
}
|
|
377
411
|
|
|
378
412
|
// If there are afterEach to be run, we don't finish the test yet.
|
|
379
413
|
// Disabled tests (marked pending by us) are finished immediately without waiting for afterEach hooks.
|
|
380
414
|
// In older mocha versions, pending tests don't run afterEach hooks, so we can't rely on
|
|
381
415
|
// getOnHookEndHandler to finish the test. This mirrors Jest's approach where the skip handler
|
|
382
416
|
// directly sets finalStatus without waiting for hooks
|
|
383
|
-
if (ctx && (!getAfterEachHooks(test).length || test._ddIsDisabled)) {
|
|
417
|
+
if (ctx && (!getAfterEachHooks(test).length || (test._ddIsDisabled && !test._ddIsAttemptToFix))) {
|
|
418
|
+
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
384
419
|
testFinishCh.publish({
|
|
385
420
|
status,
|
|
386
421
|
hasBeenRetried: isMochaRetry(test),
|
|
387
422
|
isLastRetry: getIsLastRetry(test),
|
|
388
|
-
|
|
389
|
-
attemptToFixPassed,
|
|
390
|
-
attemptToFixFailed,
|
|
391
|
-
isAttemptToFixRetry,
|
|
392
|
-
isAtrRetry,
|
|
423
|
+
...testFinishInfo,
|
|
393
424
|
...ctx.currentStore,
|
|
394
|
-
finalStatus,
|
|
395
425
|
})
|
|
396
|
-
} else if (ctx) { // if there is an afterEach to run, let's store the finalStatus for getOnHookEndHandler
|
|
397
|
-
ctx.finalStatus = finalStatus
|
|
398
|
-
ctx.hasFailedAllRetries = hasFailedAllRetries
|
|
399
|
-
ctx.attemptToFixPassed = attemptToFixPassed
|
|
400
|
-
ctx.attemptToFixFailed = attemptToFixFailed
|
|
401
|
-
ctx.isAttemptToFixRetry = isAttemptToFixRetry
|
|
402
|
-
ctx.isAtrRetry = isAtrRetry
|
|
403
426
|
}
|
|
404
427
|
}
|
|
405
428
|
}
|
|
406
429
|
|
|
407
|
-
function getOnHookEndHandler () {
|
|
430
|
+
function getOnHookEndHandler (config) {
|
|
408
431
|
return function (hook) {
|
|
409
432
|
const test = hook.ctx.currentTest
|
|
410
433
|
const afterEachHooks = getAfterEachHooks(hook)
|
|
@@ -415,18 +438,14 @@ function getOnHookEndHandler () {
|
|
|
415
438
|
const ctx = getTestContext(test)
|
|
416
439
|
// Disabled tests are already finished in getOnTestEndHandler,
|
|
417
440
|
// skip to avoid double-publishing
|
|
418
|
-
if (ctx && !test._ddIsDisabled) {
|
|
441
|
+
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
442
|
+
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
419
443
|
testFinishCh.publish({
|
|
420
444
|
status,
|
|
421
445
|
hasBeenRetried: isMochaRetry(test),
|
|
422
446
|
isLastRetry: getIsLastRetry(test),
|
|
423
|
-
|
|
424
|
-
attemptToFixPassed: ctx.attemptToFixPassed,
|
|
425
|
-
attemptToFixFailed: ctx.attemptToFixFailed,
|
|
426
|
-
isAttemptToFixRetry: ctx.isAttemptToFixRetry,
|
|
427
|
-
isAtrRetry: ctx.isAtrRetry,
|
|
447
|
+
...testFinishInfo,
|
|
428
448
|
...ctx.currentStore,
|
|
429
|
-
finalStatus: ctx.finalStatus,
|
|
430
449
|
})
|
|
431
450
|
}
|
|
432
451
|
}
|
|
@@ -434,7 +453,7 @@ function getOnHookEndHandler () {
|
|
|
434
453
|
}
|
|
435
454
|
}
|
|
436
455
|
|
|
437
|
-
function getOnFailHandler (isMain) {
|
|
456
|
+
function getOnFailHandler (isMain, config) {
|
|
438
457
|
return function (testOrHook, err) {
|
|
439
458
|
const testFile = testOrHook.file
|
|
440
459
|
let test = testOrHook
|
|
@@ -451,15 +470,12 @@ function getOnFailHandler (isMain) {
|
|
|
451
470
|
err.message = `${testOrHook.fullTitle()}: ${err.message}`
|
|
452
471
|
testContext.err = err
|
|
453
472
|
errorCh.runStores(testContext, () => {})
|
|
454
|
-
|
|
455
|
-
// quarantined and disabled tests always report 'skip'
|
|
456
|
-
// as final status, even when hooks fail
|
|
457
|
-
const isSkippedByManagement = test._ddIsQuarantined || test._ddIsDisabled
|
|
473
|
+
const testFinishInfo = getTestFinishInfo(test, 'fail', config, err)
|
|
458
474
|
testFinishCh.publish({
|
|
459
475
|
status: 'fail',
|
|
460
476
|
hasBeenRetried: isMochaRetry(test),
|
|
477
|
+
...testFinishInfo,
|
|
461
478
|
...testContext.currentStore,
|
|
462
|
-
finalStatus: isSkippedByManagement ? 'skip' : 'fail',
|
|
463
479
|
})
|
|
464
480
|
} else {
|
|
465
481
|
testContext.err = err
|
|
@@ -627,4 +643,6 @@ module.exports = {
|
|
|
627
643
|
testsQuarantined,
|
|
628
644
|
testsAttemptToFix,
|
|
629
645
|
testsStatuses,
|
|
646
|
+
attemptToFixExecutions,
|
|
647
|
+
loggedAttemptToFixTests,
|
|
630
648
|
}
|
|
@@ -84,9 +84,9 @@ addHook({
|
|
|
84
84
|
this.on('retry', getOnTestRetryHandler(config))
|
|
85
85
|
|
|
86
86
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
87
|
-
this.on('hook end', getOnHookEndHandler())
|
|
87
|
+
this.on('hook end', getOnHookEndHandler(config))
|
|
88
88
|
|
|
89
|
-
this.on('fail', getOnFailHandler(false))
|
|
89
|
+
this.on('fail', getOnFailHandler(false, config))
|
|
90
90
|
|
|
91
91
|
this.on('pending', getOnPendingHandler())
|
|
92
92
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { addHook } = require('./helpers/instrument')
|
|
4
|
+
|
|
5
|
+
/** @type {((pattern: string | RegExp) => RegExp | undefined) | undefined} */
|
|
6
|
+
let compileToRegexp
|
|
7
|
+
|
|
8
|
+
addHook({ name: 'path-to-regexp', versions: ['*'] }, moduleExports => {
|
|
9
|
+
// 0.1.x and 6.x: `module.exports = (path, ...) => RegExp`.
|
|
10
|
+
// 7.x: `module.exports = { pathToRegexp(path, ...) => RegExp }`.
|
|
11
|
+
// 8.x: `module.exports = { pathToRegexp(path, ...) => { regexp, keys } }`.
|
|
12
|
+
const compile = typeof moduleExports === 'function'
|
|
13
|
+
? moduleExports
|
|
14
|
+
: (typeof moduleExports?.pathToRegexp === 'function' ? moduleExports.pathToRegexp : undefined)
|
|
15
|
+
|
|
16
|
+
if (compile !== undefined) {
|
|
17
|
+
compileToRegexp = pattern => {
|
|
18
|
+
let result
|
|
19
|
+
try {
|
|
20
|
+
result = compile(pattern)
|
|
21
|
+
} catch {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
const regex = result?.regexp ?? result
|
|
25
|
+
if (regex instanceof RegExp) return regex
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return moduleExports
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns whatever path-to-regexp compile adapter the host most recently
|
|
34
|
+
* loaded. Capture this once at addHook fire time so each express/router
|
|
35
|
+
* instance keeps the dialect that was current when its routes were wrapped;
|
|
36
|
+
* a later host load that swaps the global compile won't retroactively change
|
|
37
|
+
* already-wrapped routers. `undefined` when the host has not loaded
|
|
38
|
+
* path-to-regexp yet, or never if it does not depend on it.
|
|
39
|
+
*/
|
|
40
|
+
function getCompileToRegexp () {
|
|
41
|
+
return compileToRegexp
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { getCompileToRegexp }
|