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.
Files changed (74) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/package.json +24 -5
  3. package/packages/datadog-instrumentations/src/cucumber.js +69 -5
  4. package/packages/datadog-instrumentations/src/express.js +3 -2
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  6. package/packages/datadog-instrumentations/src/hono.js +15 -4
  7. package/packages/datadog-instrumentations/src/jest.js +89 -63
  8. package/packages/datadog-instrumentations/src/mocha/main.js +18 -22
  9. package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
  10. package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
  11. package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
  12. package/packages/datadog-instrumentations/src/playwright.js +108 -18
  13. package/packages/datadog-instrumentations/src/router.js +53 -33
  14. package/packages/datadog-instrumentations/src/vitest.js +76 -30
  15. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
  16. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  17. package/packages/datadog-plugin-bullmq/src/consumer.js +3 -2
  18. package/packages/datadog-plugin-bullmq/src/producer.js +25 -11
  19. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +32 -9
  20. package/packages/datadog-plugin-cypress/src/support.js +22 -21
  21. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -1
  22. package/packages/datadog-plugin-graphql/src/utils.js +2 -2
  23. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  24. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  25. package/packages/datadog-plugin-memcached/src/index.js +1 -1
  26. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
  27. package/packages/datadog-plugin-playwright/src/index.js +6 -0
  28. package/packages/datadog-plugin-router/src/index.js +13 -0
  29. package/packages/dd-trace/index.js +4 -3
  30. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  31. package/packages/dd-trace/src/appsec/blocking.js +18 -6
  32. package/packages/dd-trace/src/appsec/graphql.js +1 -1
  33. package/packages/dd-trace/src/baggage.js +26 -13
  34. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
  35. package/packages/dd-trace/src/config/generated-config-types.d.ts +45 -69
  36. package/packages/dd-trace/src/config/index.js +13 -12
  37. package/packages/dd-trace/src/config/normalize-service.js +31 -0
  38. package/packages/dd-trace/src/config/supported-configurations.json +31 -76
  39. package/packages/dd-trace/src/debugger/config.js +1 -1
  40. package/packages/dd-trace/src/dogstatsd.js +5 -8
  41. package/packages/dd-trace/src/encode/0.4.js +1 -1
  42. package/packages/dd-trace/src/encode/tags-processors.js +3 -3
  43. package/packages/dd-trace/src/exporter.js +1 -1
  44. package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
  45. package/packages/dd-trace/src/heap_snapshots.js +4 -4
  46. package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
  47. package/packages/dd-trace/src/llmobs/sdk.js +21 -1
  48. package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
  49. package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
  50. package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
  51. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
  52. package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -8
  53. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -5
  54. package/packages/dd-trace/src/opentelemetry/metrics/index.js +6 -6
  55. package/packages/dd-trace/src/opentelemetry/span-helpers.js +170 -0
  56. package/packages/dd-trace/src/opentelemetry/span.js +14 -42
  57. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +1 -1
  58. package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
  59. package/packages/dd-trace/src/opentracing/propagation/text_map.js +44 -23
  60. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +42 -12
  61. package/packages/dd-trace/src/opentracing/span.js +4 -3
  62. package/packages/dd-trace/src/plugin_manager.js +6 -6
  63. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  64. package/packages/dd-trace/src/plugins/util/ci.js +119 -32
  65. package/packages/dd-trace/src/plugins/util/test.js +295 -29
  66. package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
  67. package/packages/dd-trace/src/propagation-hash/index.js +1 -1
  68. package/packages/dd-trace/src/proxy.js +9 -9
  69. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  70. package/packages/dd-trace/src/span_processor.js +1 -1
  71. package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
  72. package/packages/dd-trace/src/tracer_metadata.js +1 -1
  73. package/vendor/dist/path-to-regexp/LICENSE +0 -21
  74. 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
- collectDynamicNamesFromTraces,
18
- logDynamicNamesWarning,
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 substract the errors of attempt to fix tests (quarantined or disabled) from the total number of failures
150
- // We subtract the errors from quarantined tests from the total number of failures
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 + numFailedRetriedQuarantinedOrDisabledTests
168
- this.failures -= numFailedQuarantinedTests + numFailedRetriedQuarantinedOrDisabledTests
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
- logDynamicNamesWarning(newTestsWithDynamicNames)
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
- collectDynamicNamesFromTraces(payload, newTestsWithDynamicNames)
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
- if (config.isTestManagementTestsEnabled && getTestProperties(test, config.testManagementTests).isQuarantined) {
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 { getTestSuitePath, DYNAMIC_NAME_RE } = require('../../../dd-trace/src/plugins/util/test')
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, regardless of its actual execution result or active retry features,
276
- // the final status of its last execution should be reported as 'skip'.
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 getOnTestEndHandler (config) {
299
- return async function (test) {
300
- const ctx = getTestContext(test)
301
- const status = getTestStatus(test)
311
+ function getTestFinishInfo (test, status, config, error) {
312
+ let hasFailedAllRetries = false
313
+ let attemptToFixPassed = false
314
+ let attemptToFixFailed = false
302
315
 
303
- // After finishing it might take a bit for the snapshot to be handled.
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
- if (testsStatuses.get(testName)) {
320
- testsStatuses.get(testName).push(status)
321
- } else {
322
- testsStatuses.set(testName, [status])
323
- }
324
- const testStatuses = testsStatuses.get(testName)
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
- const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
327
- const isLastEfdRetry = testStatuses.length === config.earlyFlakeDetectionNumRetries + 1
328
- const isLastAtrAttempt = getIsLastRetry(test) || (config.isFlakyTestRetriesEnabled && status === 'pass')
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
- // Needed for the getFinalStatus call. This is because EFD does NOT tag as
331
- // EFD retry the first run of the test. It only tags as retries the clones
332
- const isEfdRetry = test._ddIsEfdRetry || (test._ddIsNew && config.isEarlyFlakeDetectionEnabled)
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
- if (test._ddIsAttemptToFix && isLastAttempt) {
335
- if (testStatuses.includes('fail')) {
336
- attemptToFixFailed = true
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
- // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
351
- if (config.isFlakyTestRetriesEnabled && !test._ddIsAttemptToFix && !test._ddIsEfdRetry &&
352
- getIsLastRetry(test) && testStatuses.every(status => status === 'fail')) {
353
- hasFailedAllRetries = true
354
- }
344
+ if (test._ddIsEfdRetry && isLastEfdRetry &&
345
+ testStatuses.every(status => status === 'fail')) {
346
+ hasFailedAllRetries = true
347
+ }
355
348
 
356
- const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
357
- const isAtrRetry = config.isFlakyTestRetriesEnabled &&
358
- !test._ddIsAttemptToFix &&
359
- !test._ddIsEfdRetry
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
- const { isFlakyTestRetriesEnabled } = config
362
- const { _ddIsAttemptToFix, _ddIsQuarantined, _ddIsDisabled } = test
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
- const finalStatus = getFinalStatus({
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
- hasFailedAllRetries,
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
- hasFailedAllRetries: ctx.hasFailedAllRetries,
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
- // if it's a hook and it has failed, 'test end' will not be called
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 }