dd-trace 5.103.0 → 5.104.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 (90) hide show
  1. package/index.d.ts +25 -3
  2. package/package.json +4 -3
  3. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
  4. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  5. package/packages/datadog-instrumentations/src/cucumber.js +103 -30
  6. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  7. package/packages/datadog-instrumentations/src/graphql.js +0 -5
  8. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  9. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
  14. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  15. package/packages/datadog-instrumentations/src/ioredis.js +3 -3
  16. package/packages/datadog-instrumentations/src/jest.js +33 -36
  17. package/packages/datadog-instrumentations/src/kafkajs.js +25 -6
  18. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  19. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  20. package/packages/datadog-instrumentations/src/mocha/main.js +272 -91
  21. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  22. package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
  23. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  24. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  25. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  26. package/packages/datadog-instrumentations/src/pg.js +1 -1
  27. package/packages/datadog-instrumentations/src/playwright.js +22 -5
  28. package/packages/datadog-instrumentations/src/router.js +4 -2
  29. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  30. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +26 -19
  31. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  32. package/packages/datadog-plugin-graphql/src/utils.js +4 -1
  33. package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
  34. package/packages/datadog-plugin-mongodb-core/src/index.js +54 -19
  35. package/packages/datadog-plugin-redis/src/index.js +37 -2
  36. package/packages/datadog-plugin-undici/src/index.js +19 -0
  37. package/packages/datadog-plugin-vitest/src/index.js +19 -7
  38. package/packages/datadog-shimmer/src/shimmer.js +35 -0
  39. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  40. package/packages/dd-trace/src/appsec/index.js +10 -3
  41. package/packages/dd-trace/src/appsec/reporter.js +19 -5
  42. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  43. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  44. package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -0
  45. package/packages/dd-trace/src/config/supported-configurations.json +9 -0
  46. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  47. package/packages/dd-trace/src/datastreams/context.js +4 -2
  48. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
  49. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  50. package/packages/dd-trace/src/exporters/common/request.js +3 -1
  51. package/packages/dd-trace/src/id.js +17 -4
  52. package/packages/dd-trace/src/lambda/handler.js +2 -4
  53. package/packages/dd-trace/src/llmobs/sdk.js +10 -0
  54. package/packages/dd-trace/src/log/writer.js +3 -1
  55. package/packages/dd-trace/src/noop/span.js +3 -1
  56. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  57. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  58. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  59. package/packages/dd-trace/src/plugins/ci_plugin.js +3 -13
  60. package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
  61. package/packages/dd-trace/src/plugins/tracing.js +5 -3
  62. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  63. package/packages/dd-trace/src/plugins/util/test.js +82 -0
  64. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  65. package/packages/dd-trace/src/scope.js +7 -5
  66. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  67. package/vendor/dist/opentracing/LICENSE +0 -201
  68. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  69. package/vendor/dist/opentracing/constants.d.ts +0 -61
  70. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  71. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  72. package/vendor/dist/opentracing/functions.d.ts +0 -20
  73. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  74. package/vendor/dist/opentracing/index.d.ts +0 -12
  75. package/vendor/dist/opentracing/index.js +0 -1
  76. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  77. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  78. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  79. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  80. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  81. package/vendor/dist/opentracing/noop.d.ts +0 -8
  82. package/vendor/dist/opentracing/reference.d.ts +0 -33
  83. package/vendor/dist/opentracing/span.d.ts +0 -147
  84. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  85. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  86. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  87. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  88. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  89. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  90. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -18,6 +18,7 @@ const {
18
18
  recordAttemptToFixExecution,
19
19
  logAttemptToFixTestExecution,
20
20
  logTestOptimizationSummary,
21
+ getTestOptimizationRequestResults,
21
22
  } = require('../../dd-trace/src/plugins/util/test')
22
23
  const log = require('../../dd-trace/src/log')
23
24
  const {
@@ -1075,9 +1076,24 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1075
1076
  log.error('Playwright session start error', e)
1076
1077
  }
1077
1078
 
1078
- if (isKnownTestsEnabled && satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)) {
1079
+ const isTestOptimizationSupported = satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)
1080
+ const shouldGetKnownTests = isKnownTestsEnabled && isTestOptimizationSupported
1081
+ const shouldGetTestManagementTests = isTestManagementTestsEnabled && isTestOptimizationSupported
1082
+
1083
+ const {
1084
+ knownTestsResponse,
1085
+ testManagementTestsResponse,
1086
+ } = await getTestOptimizationRequestResults({
1087
+ isKnownTestsEnabled: shouldGetKnownTests,
1088
+ isTestManagementTestsEnabled: shouldGetTestManagementTests,
1089
+ getKnownTests: () => getChannelPromise(knownTestsCh),
1090
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
1091
+ })
1092
+
1093
+ if (shouldGetKnownTests) {
1079
1094
  try {
1080
- const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh)
1095
+ const { err, knownTests: receivedKnownTests } =
1096
+ knownTestsResponse || await getChannelPromise(knownTestsCh)
1081
1097
  if (err) {
1082
1098
  isEarlyFlakeDetectionEnabled = false
1083
1099
  isKnownTestsEnabled = false
@@ -1096,9 +1112,10 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1096
1112
  }
1097
1113
  }
1098
1114
 
1099
- if (isTestManagementTestsEnabled && satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)) {
1115
+ if (shouldGetTestManagementTests) {
1100
1116
  try {
1101
- const { err, testManagementTests: receivedTestManagementTests } = await getChannelPromise(testManagementTestsCh)
1117
+ const { err, testManagementTests: receivedTestManagementTests } =
1118
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
1102
1119
  if (err) {
1103
1120
  isTestManagementTestsEnabled = false
1104
1121
  } else {
@@ -1110,7 +1127,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1110
1127
  }
1111
1128
  }
1112
1129
 
1113
- if (isImpactedTestsEnabled && satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)) {
1130
+ if (isImpactedTestsEnabled && isTestOptimizationSupported) {
1114
1131
  try {
1115
1132
  const { err, modifiedFiles: receivedModifiedFiles } = await getChannelPromise(modifiedFilesCh)
1116
1133
  if (err) {
@@ -112,8 +112,10 @@ function createWrapRouterMethod (name, compile) {
112
112
  }
113
113
  }
114
114
 
115
- function wrapNext (req, next) {
116
- return shimmer.wrapFunction(next, next => function (error) {
115
+ function wrapNext (req, originalNext) {
116
+ // Per layer dispatch, N per request. `shimmer.wrapCallback` preserves
117
+ // only `name` + `length`; see its JSDoc for the full contract.
118
+ return shimmer.wrapCallback(originalNext, next => function (error) {
117
119
  if (error && error !== 'route' && error !== 'router') {
118
120
  errorChannel.publish({ req, error })
119
121
  }
@@ -19,6 +19,7 @@ const {
19
19
  collectTestOptimizationSummariesFromTraces,
20
20
  logAttemptToFixTestExecution,
21
21
  logTestOptimizationSummary,
22
+ getTestOptimizationRequestResults,
22
23
  } = require('../../dd-trace/src/plugins/util/test')
23
24
  const { addHook, channel } = require('./helpers/instrument')
24
25
 
@@ -43,6 +44,7 @@ const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
43
44
  // test session hooks
44
45
  const testSessionStartCh = channel('ci:vitest:session:start')
45
46
  const testSessionFinishCh = channel('ci:vitest:session:finish')
47
+ const testSessionConfigurationCh = channel('ci:vitest:session:configuration')
46
48
  const libraryConfigurationCh = channel('ci:vitest:library-configuration')
47
49
  const knownTestsCh = channel('ci:vitest:known-tests')
48
50
  const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
@@ -74,6 +76,9 @@ let isRetryReasonEfd = false
74
76
  let isRetryReasonAttemptToFix = false
75
77
  const switchedStatuses = new WeakSet()
76
78
  const workerProcesses = new WeakSet()
79
+ const mainProcessSetupPromises = new WeakMap()
80
+ const coverageWrappedProviders = new WeakSet()
81
+ const finishWrappedContexts = new WeakSet()
77
82
  let isFlakyTestRetriesEnabled = false
78
83
  let flakyTestRetriesCount = 0
79
84
  let isEarlyFlakeDetectionEnabled = false
@@ -134,6 +139,9 @@ function getProvidedContext () {
134
139
  _ddFlakyTestRetriesCount: flakyTestRetriesCount,
135
140
  _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
136
141
  _ddModifiedFiles: modifiedFiles,
142
+ _ddTestSessionId: testSessionId,
143
+ _ddTestModuleId: testModuleId,
144
+ _ddTestCommand: testCommand,
137
145
  } = globalThis.__vitest_worker__.providedContext
138
146
 
139
147
  return {
@@ -150,6 +158,9 @@ function getProvidedContext () {
150
158
  flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
151
159
  isImpactedTestsEnabled,
152
160
  modifiedFiles,
161
+ testSessionId,
162
+ testModuleId,
163
+ testCommand,
153
164
  }
154
165
  } catch {
155
166
  log.error('Vitest workers could not parse provided context, so some features will not work.')
@@ -167,6 +178,9 @@ function getProvidedContext () {
167
178
  flakyTestRetriesCount: 0,
168
179
  isImpactedTestsEnabled: false,
169
180
  modifiedFiles: {},
181
+ testSessionId: undefined,
182
+ testModuleId: undefined,
183
+ testCommand: undefined,
170
184
  }
171
185
  }
172
186
  }
@@ -217,6 +231,10 @@ function getTestRunnerExport (testPackage) {
217
231
  return findExportByName(testPackage, 'VitestTestRunner') || findExportByName(testPackage, 'TestRunner')
218
232
  }
219
233
 
234
+ function getVitestExport (vitestPackage) {
235
+ return findExportByName(vitestPackage, 'Vitest')
236
+ }
237
+
220
238
  function getForksPoolWorkerExport (vitestPackage) {
221
239
  return findExportByName(vitestPackage, 'ForksPoolWorker')
222
240
  }
@@ -329,170 +347,223 @@ function wrapBeforeEachCleanupResult (task, result) {
329
347
  return result
330
348
  }
331
349
 
332
- function getSortWrapper (sort, frameworkVersion) {
333
- return async function () {
334
- if (!testSessionFinishCh.hasSubscribers) {
335
- return sort.apply(this, arguments)
336
- }
337
- // There isn't any other async function that we seem to be able to hook into
338
- // So we will use the sort from BaseSequencer. This means that a custom sequencer
339
- // will not work. This will be a known limitation.
350
+ function getWorkspaceProject (ctx) {
351
+ return ctx.getCoreWorkspaceProject
352
+ ? ctx.getCoreWorkspaceProject()
353
+ : ctx.getRootProject()
354
+ }
355
+
356
+ function setProvidedContext (ctx, values, warningMessage) {
357
+ try {
358
+ Object.assign(getWorkspaceProject(ctx)._provided, values)
359
+ } catch {
360
+ log.warn(warningMessage)
361
+ }
362
+ }
363
+
364
+ function getTestFilepathsFromSpecifications (testSpecifications) {
365
+ if (!Array.isArray(testSpecifications) || !testSpecifications.length) {
366
+ return
367
+ }
368
+
369
+ return testSpecifications.map(testSpecification => {
370
+ const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
371
+ return testFile?.moduleId || testFile?.filepath || testFile
372
+ })
373
+ }
374
+
375
+ function getTestFilepaths (ctx, testSpecifications) {
376
+ const testFilepaths = getTestFilepathsFromSpecifications(testSpecifications)
377
+ if (testFilepaths) {
378
+ return testFilepaths
379
+ }
380
+
381
+ const getFilePaths = ctx.getTestFilepaths || ctx._globTestFilepaths
382
+ return getFilePaths.call(ctx)
383
+ }
384
+
385
+ function wrapCoverageProvider (ctx) {
386
+ const { coverageProvider } = ctx
387
+ if (!coverageProvider?.generateCoverage || coverageWrappedProviders.has(coverageProvider)) {
388
+ return
389
+ }
390
+ coverageWrappedProviders.add(coverageProvider)
391
+
392
+ // Capture coverage root directory from config (default is 'coverage' in cwd)
393
+ try {
394
+ const coverageConfig = ctx.config?.coverage
395
+ const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
396
+ const rootDir = ctx.config?.root || process.cwd()
397
+ coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
398
+ } catch {
399
+ // Fallback to cwd if we can't get config
400
+ coverageRootDir = process.cwd()
401
+ }
402
+
403
+ shimmer.wrap(coverageProvider, 'generateCoverage', generateCoverage => async function () {
404
+ const totalCodeCoverage = await generateCoverage.apply(this, arguments)
340
405
 
341
406
  try {
342
- const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
343
- if (!err) {
344
- isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
345
- flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
346
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
347
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
348
- earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
349
- isDiEnabled = libraryConfig.isDiEnabled
350
- isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
351
- isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
352
- testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
353
- isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
354
- }
407
+ testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
355
408
  } catch {
356
- isFlakyTestRetriesEnabled = false
357
- isEarlyFlakeDetectionEnabled = false
358
- isDiEnabled = false
359
- isKnownTestsEnabled = false
360
- isImpactedTestsEnabled = false
409
+ // ignore errors
361
410
  }
411
+ return totalCodeCoverage
412
+ })
413
+ }
362
414
 
363
- if (isFlakyTestRetriesEnabled && !this.ctx.config.retry && flakyTestRetriesCount > 0) {
364
- this.ctx.config.retry = flakyTestRetriesCount
365
- try {
366
- const workspaceProject = this.ctx.getCoreWorkspaceProject
367
- ? this.ctx.getCoreWorkspaceProject()
368
- : this.ctx.getRootProject()
369
- workspaceProject._provided._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
370
- workspaceProject._provided._ddFlakyTestRetriesCount = flakyTestRetriesCount
371
- } catch {
372
- log.warn('Could not send library configuration to workers.')
373
- }
374
- }
415
+ function wrapSessionFinish (ctx) {
416
+ if (finishWrappedContexts.has(ctx)) {
417
+ return
418
+ }
419
+ finishWrappedContexts.add(ctx)
375
420
 
376
- if (isKnownTestsEnabled) {
377
- const knownTestsResponse = await getChannelPromise(knownTestsCh)
378
- if (knownTestsResponse.err) {
379
- isEarlyFlakeDetectionEnabled = false
380
- } else {
381
- const knownTests = knownTestsResponse.knownTests
382
- const getFilePaths = this.ctx.getTestFilepaths || this.ctx._globTestFilepaths
383
-
384
- const testFilepaths = await getFilePaths.call(this.ctx)
385
-
386
- if (isValidKnownTests(knownTests)) {
387
- isEarlyFlakeDetectionFaultyCh.publish({
388
- knownTests: knownTests.vitest,
389
- testFilepaths,
390
- onDone: (isFaulty) => {
391
- isEarlyFlakeDetectionFaulty = isFaulty
392
- },
393
- })
394
- if (isEarlyFlakeDetectionFaulty) {
395
- isEarlyFlakeDetectionEnabled = false
396
- log.warn('New test detection is disabled because the number of new tests is too high.')
397
- } else {
398
- // TODO: use this to pass session and module IDs to the worker, instead of polluting process.env
399
- // Note: setting this.ctx.config.provide directly does not work because it's cached
400
- try {
401
- const workspaceProject = this.ctx.getCoreWorkspaceProject
402
- ? this.ctx.getCoreWorkspaceProject()
403
- : this.ctx.getRootProject()
404
- workspaceProject._provided._ddIsKnownTestsEnabled = isKnownTestsEnabled
405
- workspaceProject._provided._ddKnownTests = knownTests
406
- workspaceProject._provided._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
407
- workspaceProject._provided._ddEarlyFlakeDetectionNumRetries =
408
- getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries)
409
- workspaceProject._provided._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
410
- } catch {
411
- log.warn('Could not send known tests to workers so Early Flake Detection will not work.')
412
- }
413
- }
414
- } else {
415
- isEarlyFlakeDetectionFaulty = true
416
- isEarlyFlakeDetectionEnabled = false
417
- }
418
- }
419
- }
421
+ shimmer.wrap(ctx, 'exit', getFinishWrapper)
422
+ shimmer.wrap(ctx, 'close', getFinishWrapper)
423
+ }
420
424
 
421
- if (isDiEnabled) {
422
- try {
423
- const workspaceProject = this.ctx.getCoreWorkspaceProject
424
- ? this.ctx.getCoreWorkspaceProject()
425
- : this.ctx.getRootProject()
426
- workspaceProject._provided._ddIsDiEnabled = isDiEnabled
427
- } catch {
428
- log.warn('Could not send Dynamic Instrumentation configuration to workers.')
429
- }
430
- }
425
+ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
426
+ if (!testSessionFinishCh.hasSubscribers) {
427
+ return
428
+ }
431
429
 
432
- if (isTestManagementTestsEnabled) {
433
- const { err, testManagementTests: receivedTestManagementTests } = await getChannelPromise(testManagementTestsCh)
434
- if (err) {
435
- isTestManagementTestsEnabled = false
436
- log.error('Could not get test management tests.')
437
- } else {
438
- const testManagementTests = receivedTestManagementTests
439
- try {
440
- const workspaceProject = this.ctx.getCoreWorkspaceProject
441
- ? this.ctx.getCoreWorkspaceProject()
442
- : this.ctx.getRootProject()
443
- workspaceProject._provided._ddIsTestManagementTestsEnabled = isTestManagementTestsEnabled
444
- workspaceProject._provided._ddTestManagementAttemptToFixRetries = testManagementAttemptToFixRetries
445
- workspaceProject._provided._ddTestManagementTests = testManagementTests
446
- } catch {
447
- log.warn('Could not send test management tests to workers so Test Management will not work.')
448
- }
449
- }
430
+ try {
431
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
432
+ if (!err) {
433
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
434
+ flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
435
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
436
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
437
+ earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
438
+ isDiEnabled = libraryConfig.isDiEnabled
439
+ isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
440
+ isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
441
+ testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
442
+ isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
450
443
  }
444
+ } catch {
445
+ isFlakyTestRetriesEnabled = false
446
+ isEarlyFlakeDetectionEnabled = false
447
+ isDiEnabled = false
448
+ isKnownTestsEnabled = false
449
+ isImpactedTestsEnabled = false
450
+ }
451
451
 
452
- if (isImpactedTestsEnabled) {
453
- const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
454
- if (err) {
455
- log.error('Could not get modified tests.')
456
- } else {
457
- try {
458
- const workspaceProject = this.ctx.getCoreWorkspaceProject
459
- ? this.ctx.getCoreWorkspaceProject()
460
- : this.ctx.getRootProject()
461
- workspaceProject._provided._ddIsImpactedTestsEnabled = isImpactedTestsEnabled
462
- workspaceProject._provided._ddModifiedFiles = modifiedFiles
463
- } catch {
464
- log.warn('Could not send modified tests to workers so Impacted Tests will not work.')
452
+ if (testSessionConfigurationCh.hasSubscribers) {
453
+ const { testSessionId, testModuleId, testCommand } = await getChannelPromise(
454
+ testSessionConfigurationCh,
455
+ frameworkVersion
456
+ )
457
+ setProvidedContext(ctx, {
458
+ _ddTestSessionId: testSessionId,
459
+ _ddTestModuleId: testModuleId,
460
+ _ddTestCommand: testCommand,
461
+ }, 'Could not send test session configuration to workers.')
462
+ }
463
+
464
+ const {
465
+ knownTestsResponse,
466
+ testManagementTestsResponse,
467
+ } = await getTestOptimizationRequestResults({
468
+ isKnownTestsEnabled,
469
+ isTestManagementTestsEnabled,
470
+ getKnownTests: () => getChannelPromise(knownTestsCh),
471
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
472
+ })
473
+
474
+ if (isFlakyTestRetriesEnabled && !ctx.config.retry && flakyTestRetriesCount > 0) {
475
+ ctx.config.retry = flakyTestRetriesCount
476
+ setProvidedContext(ctx, {
477
+ _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
478
+ _ddFlakyTestRetriesCount: flakyTestRetriesCount,
479
+ }, 'Could not send library configuration to workers.')
480
+ }
481
+
482
+ if (isKnownTestsEnabled) {
483
+ const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
484
+ if (currentKnownTestsResponse.err) {
485
+ isEarlyFlakeDetectionEnabled = false
486
+ } else {
487
+ const knownTests = currentKnownTestsResponse.knownTests
488
+ const testFilepaths = await getTestFilepaths(ctx, testSpecifications)
489
+
490
+ if (isValidKnownTests(knownTests)) {
491
+ isEarlyFlakeDetectionFaultyCh.publish({
492
+ knownTests: knownTests.vitest,
493
+ testFilepaths,
494
+ onDone: (isFaulty) => {
495
+ isEarlyFlakeDetectionFaulty = isFaulty
496
+ },
497
+ })
498
+ if (isEarlyFlakeDetectionFaulty) {
499
+ isEarlyFlakeDetectionEnabled = false
500
+ log.warn('New test detection is disabled because the number of new tests is too high.')
501
+ } else {
502
+ setProvidedContext(ctx, {
503
+ _ddIsKnownTestsEnabled: isKnownTestsEnabled,
504
+ _ddKnownTests: knownTests,
505
+ _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
506
+ _ddEarlyFlakeDetectionNumRetries:
507
+ getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
508
+ _ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
509
+ }, 'Could not send known tests to workers so Early Flake Detection will not work.')
465
510
  }
511
+ } else {
512
+ isEarlyFlakeDetectionFaulty = true
513
+ isEarlyFlakeDetectionEnabled = false
466
514
  }
467
515
  }
516
+ }
468
517
 
469
- if (this.ctx.coverageProvider?.generateCoverage) {
470
- // Capture coverage root directory from config (default is 'coverage' in cwd)
471
- try {
472
- const coverageConfig = this.ctx.config?.coverage
473
- const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
474
- const rootDir = this.ctx.config?.root || process.cwd()
475
- coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
476
- } catch {
477
- // Fallback to cwd if we can't get config
478
- coverageRootDir = process.cwd()
479
- }
518
+ if (isDiEnabled) {
519
+ setProvidedContext(ctx, {
520
+ _ddIsDiEnabled: isDiEnabled,
521
+ }, 'Could not send Dynamic Instrumentation configuration to workers.')
522
+ }
480
523
 
481
- shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () {
482
- const totalCodeCoverage = await generateCoverage.apply(this, arguments)
524
+ if (isTestManagementTestsEnabled) {
525
+ const { err, testManagementTests: receivedTestManagementTests } =
526
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
527
+ if (err) {
528
+ isTestManagementTestsEnabled = false
529
+ log.error('Could not get test management tests.')
530
+ } else {
531
+ setProvidedContext(ctx, {
532
+ _ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
533
+ _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
534
+ _ddTestManagementTests: receivedTestManagementTests,
535
+ }, 'Could not send test management tests to workers so Test Management will not work.')
536
+ }
537
+ }
483
538
 
484
- try {
485
- testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
486
- } catch {
487
- // ignore errors
488
- }
489
- return totalCodeCoverage
490
- })
539
+ if (isImpactedTestsEnabled) {
540
+ const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
541
+ if (err) {
542
+ log.error('Could not get modified tests.')
543
+ } else {
544
+ setProvidedContext(ctx, {
545
+ _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
546
+ _ddModifiedFiles: modifiedFiles,
547
+ }, 'Could not send modified tests to workers so Impacted Tests will not work.')
491
548
  }
549
+ }
492
550
 
493
- shimmer.wrap(this.ctx, 'exit', getFinishWrapper)
494
- shimmer.wrap(this.ctx, 'close', getFinishWrapper)
551
+ wrapCoverageProvider(ctx)
552
+ wrapSessionFinish(ctx)
553
+ }
495
554
 
555
+ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
556
+ let setupPromise = mainProcessSetupPromises.get(ctx)
557
+ if (!setupPromise) {
558
+ setupPromise = runMainProcessSetup(ctx, frameworkVersion, testSpecifications)
559
+ mainProcessSetupPromises.set(ctx, setupPromise)
560
+ }
561
+ return setupPromise
562
+ }
563
+
564
+ function getSortWrapper (sort, frameworkVersion) {
565
+ return async function () {
566
+ await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
496
567
  return sort.apply(this, arguments)
497
568
  }
498
569
  }
@@ -504,6 +575,11 @@ function getFinishWrapper (exitOrClose) {
504
575
  return exitOrClose.apply(this, arguments)
505
576
  }
506
577
  isClosed = true
578
+
579
+ if (!testSessionFinishCh.hasSubscribers) {
580
+ return exitOrClose.apply(this, arguments)
581
+ }
582
+
507
583
  let onFinish
508
584
 
509
585
  const flushPromise = new Promise(resolve => {
@@ -555,6 +631,17 @@ function getCliOrStartVitestWrapper (frameworkVersion) {
555
631
  }
556
632
  }
557
633
 
634
+ function wrapVitestRunFiles (Vitest, frameworkVersion) {
635
+ if (!Vitest?.prototype?.runFiles) {
636
+ return
637
+ }
638
+
639
+ shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
640
+ await ensureMainProcessSetup(this, frameworkVersion, testSpecifications)
641
+ return runFiles.apply(this, arguments)
642
+ })
643
+ }
644
+
558
645
  function getCreateCliWrapper (vitestPackage, frameworkVersion) {
559
646
  const createCliExport = findExportByName(vitestPackage, 'createCLI')
560
647
  if (!createCliExport) {
@@ -658,6 +745,11 @@ function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
658
745
  const startVitestExport = findExportByName(cliApiPackage, 'startVitest')
659
746
  shimmer.wrap(cliApiPackage, startVitestExport.key, getCliOrStartVitestWrapper(frameworkVersion))
660
747
 
748
+ const vitest = getVitestExport(cliApiPackage)
749
+ if (vitest) {
750
+ wrapVitestRunFiles(vitest.value, frameworkVersion)
751
+ }
752
+
661
753
  const forksPoolWorker = getForksPoolWorkerExport(cliApiPackage)
662
754
  if (forksPoolWorker) {
663
755
  // function is async
@@ -921,7 +1013,6 @@ function wrapVitestTestRunner (VitestTestRunner) {
921
1013
  // Here we finish the earlier iteration,
922
1014
  // as long as it's not the _last_ iteration (which will be finished normally)
923
1015
 
924
- // TODO: check test duration (not to repeat if it's too slow)
925
1016
  const ctx = taskToCtx.get(task)
926
1017
  if (ctx) {
927
1018
  if (lastExecutionStatus === 'fail') {
@@ -1269,8 +1360,15 @@ addHook({
1269
1360
  }
1270
1361
  // From >=3.0.1, the first arguments changes from a string to an object containing the filepath
1271
1362
  const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
1272
-
1273
- const testSuiteCtx = { testSuiteAbsolutePath, frameworkVersion }
1363
+ const providedContext = getProvidedContext()
1364
+
1365
+ const testSuiteCtx = {
1366
+ testSuiteAbsolutePath,
1367
+ frameworkVersion,
1368
+ testSessionId: providedContext.testSessionId,
1369
+ testModuleId: providedContext.testModuleId,
1370
+ testCommand: providedContext.testCommand,
1371
+ }
1274
1372
  testSuiteStartCh.runStores(testSuiteCtx, () => {})
1275
1373
  const startTestsResponse = await startTests.apply(this, arguments)
1276
1374
 
@@ -1288,7 +1386,6 @@ addHook({
1288
1386
  // We have to trick vitest into thinking that the test has passed
1289
1387
  // but we want to report it as failed if it did fail
1290
1388
  const isSwitchedStatus = switchedStatuses.has(task)
1291
- const providedContext = getProvidedContext()
1292
1389
 
1293
1390
  if (result) {
1294
1391
  const { state, duration, errors } = result
@@ -70,6 +70,7 @@ const {
70
70
  getMaxEfdRetryCount,
71
71
  getPullRequestBaseBranch,
72
72
  TEST_FINAL_STATUS,
73
+ getTestOptimizationRequestResults,
73
74
  } = require('../../dd-trace/src/plugins/util/test')
74
75
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
75
76
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
@@ -787,19 +788,29 @@ class CypressPlugin {
787
788
  this.frameworkVersion = getCypressVersion(details)
788
789
  this.rootDir = getRootDir(details)
789
790
 
791
+ const {
792
+ knownTestsResponse,
793
+ testManagementTestsResponse,
794
+ skippableSuitesResponse: skippableTestsRequestResponse,
795
+ } = await getTestOptimizationRequestResults({
796
+ isKnownTestsEnabled: this.isKnownTestsEnabled,
797
+ isTestManagementTestsEnabled: this.isTestManagementTestsEnabled,
798
+ isSuitesSkippingEnabled: this.isSuitesSkippingEnabled,
799
+ getKnownTests: () => getKnownTests(this.tracer, this.testConfiguration),
800
+ getTestManagementTests: () => getTestManagementTests(this.tracer, this.testConfiguration),
801
+ getSkippableSuites: () => getSkippableTests(this.tracer, this.testConfiguration),
802
+ })
803
+
790
804
  if (this.isKnownTestsEnabled) {
791
- const knownTestsResponse = await getKnownTests(
792
- this.tracer,
793
- this.testConfiguration
794
- )
795
- if (knownTestsResponse.err) {
796
- log.error('Cypress known tests response error', knownTestsResponse.err)
805
+ const currentKnownTestsResponse = knownTestsResponse || await getKnownTests(this.tracer, this.testConfiguration)
806
+ if (currentKnownTestsResponse.err) {
807
+ log.error('Cypress known tests response error', currentKnownTestsResponse.err)
797
808
  this._pendingRequestErrorTags.push({ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_KNOWN_TESTS, value: 'true' })
798
809
  this.isEarlyFlakeDetectionEnabled = false
799
810
  this.isKnownTestsEnabled = false
800
811
  } else {
801
- if (knownTestsResponse.knownTests?.[TEST_FRAMEWORK_NAME]) {
802
- this.knownTestsByTestSuite = knownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]
812
+ if (currentKnownTestsResponse.knownTests?.[TEST_FRAMEWORK_NAME]) {
813
+ this.knownTestsByTestSuite = currentKnownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]
803
814
  } else {
804
815
  this.isEarlyFlakeDetectionEnabled = false
805
816
  this.isKnownTestsEnabled = false
@@ -823,10 +834,8 @@ class CypressPlugin {
823
834
  }
824
835
 
825
836
  if (this.isSuitesSkippingEnabled) {
826
- const skippableTestsResponse = await getSkippableTests(
827
- this.tracer,
828
- this.testConfiguration
829
- )
837
+ const skippableTestsResponse =
838
+ skippableTestsRequestResponse || await getSkippableTests(this.tracer, this.testConfiguration)
830
839
  if (skippableTestsResponse.err) {
831
840
  log.error('Cypress skippable tests response error', skippableTestsResponse.err)
832
841
  this._pendingRequestErrorTags.push({ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, value: 'true' })
@@ -839,19 +848,17 @@ class CypressPlugin {
839
848
  }
840
849
 
841
850
  if (this.isTestManagementTestsEnabled) {
842
- const testManagementTestsResponse = await getTestManagementTests(
843
- this.tracer,
844
- this.testConfiguration
845
- )
846
- if (testManagementTestsResponse.err) {
847
- log.error('Cypress test management tests response error', testManagementTestsResponse.err)
851
+ const currentTestManagementTestsResponse =
852
+ testManagementTestsResponse || await getTestManagementTests(this.tracer, this.testConfiguration)
853
+ if (currentTestManagementTestsResponse.err) {
854
+ log.error('Cypress test management tests response error', currentTestManagementTestsResponse.err)
848
855
  this._pendingRequestErrorTags.push({
849
856
  tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_TEST_MANAGEMENT_TESTS,
850
857
  value: 'true',
851
858
  })
852
859
  this.isTestManagementTestsEnabled = false
853
860
  } else {
854
- this.testManagementTests = testManagementTestsResponse.testManagementTests
861
+ this.testManagementTests = currentTestManagementTestsResponse.testManagementTests
855
862
  }
856
863
  }
857
864