dd-trace 4.44.0 → 4.45.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 (35) hide show
  1. package/index.d.ts +2 -1
  2. package/package.json +3 -3
  3. package/packages/datadog-instrumentations/src/body-parser.js +14 -2
  4. package/packages/datadog-instrumentations/src/cucumber.js +10 -0
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -2
  6. package/packages/datadog-instrumentations/src/helpers/register.js +8 -1
  7. package/packages/datadog-instrumentations/src/mocha/main.js +90 -70
  8. package/packages/datadog-instrumentations/src/nyc.js +23 -0
  9. package/packages/datadog-instrumentations/src/vitest.js +18 -2
  10. package/packages/datadog-plugin-cucumber/src/index.js +12 -2
  11. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +16 -4
  12. package/packages/datadog-plugin-jest/src/index.js +17 -4
  13. package/packages/datadog-plugin-mocha/src/index.js +25 -6
  14. package/packages/datadog-plugin-nyc/src/index.js +35 -0
  15. package/packages/datadog-plugin-playwright/src/index.js +9 -4
  16. package/packages/datadog-plugin-vitest/src/index.js +30 -4
  17. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +40 -1
  18. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -4
  19. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -4
  20. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +8 -7
  21. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -4
  22. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -4
  23. package/packages/dd-trace/src/ci-visibility/telemetry.js +29 -2
  24. package/packages/dd-trace/src/config.js +118 -112
  25. package/packages/dd-trace/src/opentelemetry/context_manager.js +22 -39
  26. package/packages/dd-trace/src/opentelemetry/span_context.js +2 -2
  27. package/packages/dd-trace/src/opentelemetry/tracer.js +23 -14
  28. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +9 -1
  29. package/packages/dd-trace/src/opentracing/propagation/log.js +1 -1
  30. package/packages/dd-trace/src/opentracing/propagation/text_map.js +60 -0
  31. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  32. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -2
  33. package/packages/dd-trace/src/plugins/index.js +1 -0
  34. package/packages/dd-trace/src/plugins/util/git.js +14 -1
  35. package/packages/dd-trace/src/telemetry/index.js +1 -1
package/index.d.ts CHANGED
@@ -1837,9 +1837,10 @@ declare namespace tracer {
1837
1837
  /**
1838
1838
  * Construct a new TracerProvider to register with @opentelemetry/api
1839
1839
  *
1840
+ * @param config Configuration object for the TracerProvider
1840
1841
  * @returns TracerProvider A TracerProvider instance
1841
1842
  */
1842
- new(): TracerProvider;
1843
+ new(config?: Record<string, unknown>): TracerProvider;
1843
1844
 
1844
1845
  /**
1845
1846
  * Returns a Tracer, creating one if one with the given name and version is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "4.44.0",
3
+ "version": "4.45.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -73,8 +73,8 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@datadog/native-appsec": "8.0.1",
76
- "@datadog/native-iast-rewriter": "2.4.0",
77
- "@datadog/native-iast-taint-tracking": "3.0.0",
76
+ "@datadog/native-iast-rewriter": "2.4.1",
77
+ "@datadog/native-iast-taint-tracking": "3.1.0",
78
78
  "@datadog/native-metrics": "^2.0.0",
79
79
  "@datadog/pprof": "5.3.0",
80
80
  "@datadog/sketches-js": "^2.1.0",
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { channel, addHook } = require('./helpers/instrument')
4
+ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
5
5
 
6
6
  const bodyParserReadCh = channel('datadog:body-parser:read:finish')
7
7
 
@@ -23,7 +23,19 @@ function publishRequestBodyAndNext (req, res, next) {
23
23
  addHook({
24
24
  name: 'body-parser',
25
25
  file: 'lib/read.js',
26
- versions: ['>=1.4.0']
26
+ versions: ['>=1.4.0 <1.20.0']
27
+ }, read => {
28
+ return shimmer.wrap(read, function (req, res, next) {
29
+ const nextResource = new AsyncResource('bound-anonymous-fn')
30
+ arguments[2] = nextResource.bind(publishRequestBodyAndNext(req, res, next))
31
+ return read.apply(this, arguments)
32
+ })
33
+ })
34
+
35
+ addHook({
36
+ name: 'body-parser',
37
+ file: 'lib/read.js',
38
+ versions: ['>=1.20.0']
27
39
  }, read => {
28
40
  return shimmer.wrap(read, function (req, res, next) {
29
41
  arguments[2] = publishRequestBodyAndNext(req, res, next)
@@ -28,6 +28,8 @@ const workerReportTraceCh = channel('ci:cucumber:worker-report:trace')
28
28
 
29
29
  const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
30
30
 
31
+ const getCodeCoverageCh = channel('ci:nyc:get-coverage')
32
+
31
33
  const {
32
34
  getCoveredFilenamesFromCoverage,
33
35
  resetCoverage,
@@ -356,10 +358,18 @@ function getWrappedStart (start, frameworkVersion, isParallel = false) {
356
358
 
357
359
  const success = await start.apply(this, arguments)
358
360
 
361
+ let untestedCoverage
362
+ if (getCodeCoverageCh.hasSubscribers) {
363
+ untestedCoverage = await getChannelPromise(getCodeCoverageCh)
364
+ }
365
+
359
366
  let testCodeCoverageLinesTotal
360
367
 
361
368
  if (global.__coverage__) {
362
369
  try {
370
+ if (untestedCoverage) {
371
+ originalCoverageMap.merge(fromCoverageMapToCoverage(untestedCoverage))
372
+ }
363
373
  testCodeCoverageLinesTotal = originalCoverageMap.getCoverageSummary().lines.pct
364
374
  } catch (e) {
365
375
  // ignore errors
@@ -72,7 +72,6 @@ module.exports = {
72
72
  'microgateway-core': () => require('../microgateway-core'),
73
73
  mocha: () => require('../mocha'),
74
74
  'mocha-each': () => require('../mocha'),
75
- workerpool: () => require('../mocha'),
76
75
  moleculer: () => require('../moleculer'),
77
76
  mongodb: () => require('../mongodb'),
78
77
  'mongodb-core': () => require('../mongodb-core'),
@@ -89,6 +88,7 @@ module.exports = {
89
88
  'node:http2': () => require('../http2'),
90
89
  'node:https': () => require('../http'),
91
90
  'node:net': () => require('../net'),
91
+ nyc: () => require('../nyc'),
92
92
  oracledb: () => require('../oracledb'),
93
93
  openai: () => require('../openai'),
94
94
  paperplane: () => require('../paperplane'),
@@ -113,5 +113,6 @@ module.exports = {
113
113
  undici: () => require('../undici'),
114
114
  vitest: { esmFirst: true, fn: () => require('../vitest') },
115
115
  when: () => require('../when'),
116
- winston: () => require('../winston')
116
+ winston: () => require('../winston'),
117
+ workerpool: () => require('../mocha')
117
118
  }
@@ -90,7 +90,14 @@ for (const packageName of names) {
90
90
  }
91
91
 
92
92
  if (matchesFile) {
93
- const version = moduleVersion || getVersion(moduleBaseDir)
93
+ let version = moduleVersion
94
+ try {
95
+ version = version || getVersion(moduleBaseDir)
96
+ } catch (e) {
97
+ log.error(`Error getting version for "${name}": ${e.message}`)
98
+ log.error(e)
99
+ continue
100
+ }
94
101
  if (!Object.hasOwnProperty(namesAndSuccesses, name)) {
95
102
  namesAndSuccesses[`${name}@${version}`] = false
96
103
  }
@@ -47,6 +47,7 @@ const config = {}
47
47
 
48
48
  // We'll preserve the original coverage here
49
49
  const originalCoverageMap = createCoverageMap()
50
+ let untestedCoverage
50
51
 
51
52
  // test channels
52
53
  const testStartCh = channel('ci:mocha:test:start')
@@ -66,6 +67,8 @@ const testSessionStartCh = channel('ci:mocha:session:start')
66
67
  const testSessionFinishCh = channel('ci:mocha:session:finish')
67
68
  const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
68
69
 
70
+ const getCodeCoverageCh = channel('ci:nyc:get-coverage')
71
+
69
72
  function getFilteredSuites (originalSuites) {
70
73
  return originalSuites.reduce((acc, suite) => {
71
74
  const testPath = getTestSuitePath(suite.file, process.cwd())
@@ -131,6 +134,9 @@ function getOnEndHandler (isParallel) {
131
134
  let testCodeCoverageLinesTotal
132
135
  if (global.__coverage__) {
133
136
  try {
137
+ if (untestedCoverage) {
138
+ originalCoverageMap.merge(fromCoverageMapToCoverage(untestedCoverage))
139
+ }
134
140
  testCodeCoverageLinesTotal = originalCoverageMap.getCoverageSummary().lines.pct
135
141
  } catch (e) {
136
142
  // ignore errors
@@ -153,6 +159,83 @@ function getOnEndHandler (isParallel) {
153
159
  })
154
160
  }
155
161
 
162
+ function getExecutionConfiguration (runner, onFinishRequest) {
163
+ const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn')
164
+
165
+ const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
166
+ if (err) {
167
+ suitesToSkip = []
168
+ } else {
169
+ suitesToSkip = skippableSuites
170
+ itrCorrelationId = responseItrCorrelationId
171
+ }
172
+ // We remove the suites that we skip through ITR
173
+ const filteredSuites = getFilteredSuites(runner.suite.suites)
174
+ const { suitesToRun } = filteredSuites
175
+
176
+ isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length
177
+
178
+ log.debug(
179
+ () => `${suitesToRun.length} out of ${runner.suite.suites.length} suites are going to run.`
180
+ )
181
+
182
+ runner.suite.suites = suitesToRun
183
+
184
+ skippedSuites = Array.from(filteredSuites.skippedSuites)
185
+
186
+ onFinishRequest()
187
+ }
188
+
189
+ const onReceivedKnownTests = ({ err, knownTests: receivedKnownTests }) => {
190
+ if (err) {
191
+ knownTests = []
192
+ isEarlyFlakeDetectionEnabled = false
193
+ } else {
194
+ knownTests = receivedKnownTests
195
+ }
196
+
197
+ if (isSuitesSkippingEnabled) {
198
+ skippableSuitesCh.publish({
199
+ onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
200
+ })
201
+ } else {
202
+ onFinishRequest()
203
+ }
204
+ }
205
+
206
+ const onReceivedConfiguration = ({ err, libraryConfig }) => {
207
+ if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
208
+ return onFinishRequest()
209
+ }
210
+
211
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
212
+ isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
213
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
214
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
215
+
216
+ config.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
217
+ config.isSuitesSkippingEnabled = isSuitesSkippingEnabled
218
+ config.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
219
+ config.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
220
+
221
+ if (isEarlyFlakeDetectionEnabled) {
222
+ knownTestsCh.publish({
223
+ onDone: mochaRunAsyncResource.bind(onReceivedKnownTests)
224
+ })
225
+ } else if (isSuitesSkippingEnabled) {
226
+ skippableSuitesCh.publish({
227
+ onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
228
+ })
229
+ } else {
230
+ onFinishRequest()
231
+ }
232
+ }
233
+
234
+ libraryConfigurationCh.publish({
235
+ onDone: mochaRunAsyncResource.bind(onReceivedConfiguration)
236
+ })
237
+ }
238
+
156
239
  // In this hook we delay the execution with options.delay to grab library configuration,
157
240
  // skippable and known tests.
158
241
  // It is called but skipped in parallel mode.
@@ -161,7 +244,6 @@ addHook({
161
244
  versions: ['>=5.2.0'],
162
245
  file: 'lib/mocha.js'
163
246
  }, (Mocha) => {
164
- const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn')
165
247
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
166
248
  // Workers do not need to request any data, just run the tests
167
249
  if (!testStartCh.hasSubscribers || process.env.MOCHA_WORKER_ID || this.options.parallel) {
@@ -181,79 +263,17 @@ addHook({
181
263
  }
182
264
  })
183
265
 
184
- const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
185
- if (err) {
186
- suitesToSkip = []
187
- } else {
188
- suitesToSkip = skippableSuites
189
- itrCorrelationId = responseItrCorrelationId
190
- }
191
- // We remove the suites that we skip through ITR
192
- const filteredSuites = getFilteredSuites(runner.suite.suites)
193
- const { suitesToRun } = filteredSuites
194
-
195
- isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length
196
-
197
- log.debug(
198
- () => `${suitesToRun.length} out of ${runner.suite.suites.length} suites are going to run.`
199
- )
200
-
201
- runner.suite.suites = suitesToRun
202
-
203
- skippedSuites = Array.from(filteredSuites.skippedSuites)
204
-
205
- global.run()
206
- }
207
-
208
- const onReceivedKnownTests = ({ err, knownTests: receivedKnownTests }) => {
209
- if (err) {
210
- knownTests = []
211
- isEarlyFlakeDetectionEnabled = false
212
- } else {
213
- knownTests = receivedKnownTests
214
- }
215
-
216
- if (isSuitesSkippingEnabled) {
217
- skippableSuitesCh.publish({
218
- onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
266
+ getExecutionConfiguration(runner, () => {
267
+ if (getCodeCoverageCh.hasSubscribers) {
268
+ getCodeCoverageCh.publish({
269
+ onDone: (receivedCodeCoverage) => {
270
+ untestedCoverage = receivedCodeCoverage
271
+ global.run()
272
+ }
219
273
  })
220
274
  } else {
221
275
  global.run()
222
276
  }
223
- }
224
-
225
- const onReceivedConfiguration = ({ err, libraryConfig }) => {
226
- if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
227
- return global.run()
228
- }
229
-
230
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
231
- isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
232
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
233
- isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
234
-
235
- config.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
236
- config.isSuitesSkippingEnabled = isSuitesSkippingEnabled
237
- config.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
238
- config.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
239
-
240
- if (isEarlyFlakeDetectionEnabled) {
241
- knownTestsCh.publish({
242
- onDone: mochaRunAsyncResource.bind(onReceivedKnownTests)
243
- })
244
- } else if (isSuitesSkippingEnabled) {
245
- skippableSuitesCh.publish({
246
- onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
247
- })
248
- } else {
249
- global.run()
250
- }
251
- }
252
-
253
- mochaRunAsyncResource.runInAsyncScope(() => {
254
- libraryConfigurationCh.publish({
255
- onDone: mochaRunAsyncResource.bind(onReceivedConfiguration)
256
- })
257
277
  })
258
278
 
259
279
  return runner
@@ -0,0 +1,23 @@
1
+ const { addHook, channel } = require('./helpers/instrument')
2
+ const shimmer = require('../../datadog-shimmer')
3
+
4
+ const codeCoverageWrapCh = channel('ci:nyc:wrap')
5
+
6
+ addHook({
7
+ name: 'nyc',
8
+ versions: ['>=17']
9
+ }, (nycPackage) => {
10
+ shimmer.wrap(nycPackage.prototype, 'wrap', wrap => async function () {
11
+ // Only relevant if the config `all` is set to true
12
+ try {
13
+ if (JSON.parse(process.env.NYC_CONFIG).all) {
14
+ codeCoverageWrapCh.publish(this)
15
+ }
16
+ } catch (e) {
17
+ // ignore errors
18
+ }
19
+
20
+ return wrap.apply(this, arguments)
21
+ })
22
+ return nycPackage
23
+ })
@@ -121,6 +121,21 @@ function getSortWrapper (sort) {
121
121
  this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
122
122
  }
123
123
 
124
+ let testCodeCoverageLinesTotal
125
+
126
+ if (this.ctx.coverageProvider?.generateCoverage) {
127
+ shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () {
128
+ const totalCodeCoverage = await generateCoverage.apply(this, arguments)
129
+
130
+ try {
131
+ testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
132
+ } catch (e) {
133
+ // ignore errors
134
+ }
135
+ return totalCodeCoverage
136
+ })
137
+ }
138
+
124
139
  shimmer.wrap(this.ctx, 'exit', exit => async function () {
125
140
  let onFinish
126
141
 
@@ -136,8 +151,9 @@ function getSortWrapper (sort) {
136
151
  sessionAsyncResource.runInAsyncScope(() => {
137
152
  testSessionFinishCh.publish({
138
153
  status: getSessionStatus(this.state),
139
- onFinish,
140
- error
154
+ testCodeCoverageLinesTotal,
155
+ error,
156
+ onFinish
141
157
  })
142
158
  })
143
159
 
@@ -37,7 +37,10 @@ const {
37
37
  TELEMETRY_ITR_FORCED_TO_RUN,
38
38
  TELEMETRY_CODE_COVERAGE_EMPTY,
39
39
  TELEMETRY_ITR_UNSKIPPABLE,
40
- TELEMETRY_CODE_COVERAGE_NUM_FILES
40
+ TELEMETRY_CODE_COVERAGE_NUM_FILES,
41
+ TEST_IS_RUM_ACTIVE,
42
+ TEST_BROWSER_DRIVER,
43
+ TELEMETRY_TEST_SESSION
41
44
  } = require('../../dd-trace/src/ci-visibility/telemetry')
42
45
  const id = require('../../dd-trace/src/id')
43
46
 
@@ -107,6 +110,7 @@ class CucumberPlugin extends CiPlugin {
107
110
  this.testSessionSpan.finish()
108
111
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
109
112
  finishAllTraceSpans(this.testSessionSpan)
113
+ this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName })
110
114
 
111
115
  this.libraryConfig = null
112
116
  this.tracer._exporter.flush()
@@ -285,10 +289,16 @@ class CucumberPlugin extends CiPlugin {
285
289
 
286
290
  span.finish()
287
291
  if (!isStep) {
292
+ const spanTags = span.context()._tags
288
293
  this.telemetry.ciVisEvent(
289
294
  TELEMETRY_EVENT_FINISHED,
290
295
  'test',
291
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
296
+ {
297
+ hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
298
+ isNew,
299
+ isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
300
+ browserDriver: spanTags[TEST_BROWSER_DRIVER]
301
+ }
292
302
  )
293
303
  finishAllTraceSpans(span)
294
304
  // If it's a worker, flushing is cheap, as it's just sending data to the main process
@@ -44,7 +44,9 @@ const {
44
44
  TELEMETRY_ITR_UNSKIPPABLE,
45
45
  TELEMETRY_CODE_COVERAGE_NUM_FILES,
46
46
  incrementCountMetric,
47
- distributionMetric
47
+ distributionMetric,
48
+ TELEMETRY_ITR_SKIPPED,
49
+ TELEMETRY_TEST_SESSION
48
50
  } = require('../../dd-trace/src/ci-visibility/telemetry')
49
51
 
50
52
  const {
@@ -179,7 +181,7 @@ class CypressPlugin {
179
181
  } = this.testEnvironmentMetadata
180
182
 
181
183
  this.repositoryRoot = repositoryRoot
182
- this.isUnsupportedCIProvider = !ciProviderName
184
+ this.ciProviderName = ciProviderName
183
185
  this.codeOwnersEntries = getCodeOwnersFileEntries(repositoryRoot)
184
186
 
185
187
  this.testConfiguration = {
@@ -321,7 +323,7 @@ class CypressPlugin {
321
323
  incrementCountMetric(name, {
322
324
  testLevel,
323
325
  testFramework: 'cypress',
324
- isUnsupportedCIProvider: this.isUnsupportedCIProvider,
326
+ isUnsupportedCIProvider: !this.ciProviderName,
325
327
  ...tags
326
328
  })
327
329
  }
@@ -363,6 +365,7 @@ class CypressPlugin {
363
365
  const { skippableTests, correlationId } = skippableTestsResponse
364
366
  this.testsToSkip = skippableTests || []
365
367
  this.itrCorrelationId = correlationId
368
+ incrementCountMetric(TELEMETRY_ITR_SKIPPED, { testLevel: 'test' }, this.testsToSkip.length)
366
369
  }
367
370
  }
368
371
 
@@ -436,6 +439,9 @@ class CypressPlugin {
436
439
  this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
437
440
  this.testSessionSpan.finish()
438
441
  this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
442
+ incrementCountMetric(TELEMETRY_TEST_SESSION, {
443
+ provider: this.ciProviderName
444
+ })
439
445
 
440
446
  finishAllTraceSpans(this.testSessionSpan)
441
447
  }
@@ -668,8 +674,14 @@ class CypressPlugin {
668
674
  }
669
675
  // test spans are finished at after:spec
670
676
  }
677
+ this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
678
+ hasCodeOwners: !!this.activeTestSpan.context()._tags[TEST_CODE_OWNERS],
679
+ isNew,
680
+ isRum: isRUMActive,
681
+ browserDriver: 'cypress'
682
+ })
671
683
  this.activeTestSpan = null
672
- this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test')
684
+
673
685
  return null
674
686
  },
675
687
  'dd:addTags': (tags) => {
@@ -20,7 +20,9 @@ const {
20
20
  TEST_IS_RETRY,
21
21
  TEST_EARLY_FLAKE_ENABLED,
22
22
  TEST_EARLY_FLAKE_ABORT_REASON,
23
- JEST_DISPLAY_NAME
23
+ JEST_DISPLAY_NAME,
24
+ TEST_IS_RUM_ACTIVE,
25
+ TEST_BROWSER_DRIVER
24
26
  } = require('../../dd-trace/src/plugins/util/test')
25
27
  const { COMPONENT } = require('../../dd-trace/src/constants')
26
28
  const id = require('../../dd-trace/src/id')
@@ -32,7 +34,8 @@ const {
32
34
  TELEMETRY_ITR_FORCED_TO_RUN,
33
35
  TELEMETRY_CODE_COVERAGE_EMPTY,
34
36
  TELEMETRY_ITR_UNSKIPPABLE,
35
- TELEMETRY_CODE_COVERAGE_NUM_FILES
37
+ TELEMETRY_CODE_COVERAGE_NUM_FILES,
38
+ TELEMETRY_TEST_SESSION
36
39
  } = require('../../dd-trace/src/ci-visibility/telemetry')
37
40
 
38
41
  const isJestWorker = !!process.env.JEST_WORKER_ID
@@ -129,6 +132,8 @@ class JestPlugin extends CiPlugin {
129
132
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
130
133
  finishAllTraceSpans(this.testSessionSpan)
131
134
 
135
+ this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName })
136
+
132
137
  this.tracer._exporter.flush(() => {
133
138
  if (onDone) {
134
139
  onDone()
@@ -287,12 +292,20 @@ class JestPlugin extends CiPlugin {
287
292
  if (testStartLine) {
288
293
  span.setTag(TEST_SOURCE_START, testStartLine)
289
294
  }
290
- span.finish()
295
+
296
+ const spanTags = span.context()._tags
291
297
  this.telemetry.ciVisEvent(
292
298
  TELEMETRY_EVENT_FINISHED,
293
299
  'test',
294
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
300
+ {
301
+ hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
302
+ isNew: spanTags[TEST_IS_NEW] === 'true',
303
+ isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
304
+ browserDriver: spanTags[TEST_BROWSER_DRIVER]
305
+ }
295
306
  )
307
+
308
+ span.finish()
296
309
  finishAllTraceSpans(span)
297
310
  })
298
311
 
@@ -27,7 +27,9 @@ const {
27
27
  TEST_SUITE_ID,
28
28
  TEST_COMMAND,
29
29
  TEST_SUITE,
30
- MOCHA_IS_PARALLEL
30
+ MOCHA_IS_PARALLEL,
31
+ TEST_IS_RUM_ACTIVE,
32
+ TEST_BROWSER_DRIVER
31
33
  } = require('../../dd-trace/src/plugins/util/test')
32
34
  const { COMPONENT } = require('../../dd-trace/src/constants')
33
35
  const {
@@ -38,7 +40,8 @@ const {
38
40
  TELEMETRY_ITR_FORCED_TO_RUN,
39
41
  TELEMETRY_CODE_COVERAGE_EMPTY,
40
42
  TELEMETRY_ITR_UNSKIPPABLE,
41
- TELEMETRY_CODE_COVERAGE_NUM_FILES
43
+ TELEMETRY_CODE_COVERAGE_NUM_FILES,
44
+ TELEMETRY_TEST_SESSION
42
45
  } = require('../../dd-trace/src/ci-visibility/telemetry')
43
46
  const id = require('../../dd-trace/src/id')
44
47
  const log = require('../../dd-trace/src/log')
@@ -184,12 +187,20 @@ class MochaPlugin extends CiPlugin {
184
187
  if (hasBeenRetried) {
185
188
  span.setTag(TEST_IS_RETRY, 'true')
186
189
  }
187
- span.finish()
190
+
191
+ const spanTags = span.context()._tags
188
192
  this.telemetry.ciVisEvent(
189
193
  TELEMETRY_EVENT_FINISHED,
190
194
  'test',
191
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
195
+ {
196
+ hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
197
+ isNew: spanTags[TEST_IS_NEW] === 'true',
198
+ isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
199
+ browserDriver: spanTags[TEST_BROWSER_DRIVER]
200
+ }
192
201
  )
202
+
203
+ span.finish()
193
204
  finishAllTraceSpans(span)
194
205
  }
195
206
  })
@@ -226,12 +237,19 @@ class MochaPlugin extends CiPlugin {
226
237
  span.setTag(TEST_IS_RETRY, 'true')
227
238
  }
228
239
 
229
- span.finish()
240
+ const spanTags = span.context()._tags
230
241
  this.telemetry.ciVisEvent(
231
242
  TELEMETRY_EVENT_FINISHED,
232
243
  'test',
233
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
244
+ {
245
+ hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
246
+ isNew: spanTags[TEST_IS_NEW] === 'true',
247
+ isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
248
+ browserDriver: spanTags[TEST_BROWSER_DRIVER]
249
+ }
234
250
  )
251
+
252
+ span.finish()
235
253
  finishAllTraceSpans(span)
236
254
  }
237
255
  })
@@ -289,6 +307,7 @@ class MochaPlugin extends CiPlugin {
289
307
  this.testSessionSpan.finish()
290
308
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
291
309
  finishAllTraceSpans(this.testSessionSpan)
310
+ this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName })
292
311
  }
293
312
  this.libraryConfig = null
294
313
  this.tracer._exporter.flush()
@@ -0,0 +1,35 @@
1
+ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
2
+
3
+ class NycPlugin extends CiPlugin {
4
+ static get id () {
5
+ return 'nyc'
6
+ }
7
+
8
+ constructor (...args) {
9
+ super(...args)
10
+
11
+ this.addSub('ci:nyc:wrap', (nyc) => {
12
+ if (nyc?.config?.all) {
13
+ this.nyc = nyc
14
+ }
15
+ })
16
+
17
+ this.addSub('ci:nyc:get-coverage', ({ onDone }) => {
18
+ if (this.nyc?.getCoverageMapFromAllCoverageFiles) {
19
+ this.nyc.getCoverageMapFromAllCoverageFiles()
20
+ .then((untestedCoverageMap) => {
21
+ this.nyc = null
22
+ onDone(untestedCoverageMap)
23
+ }).catch((e) => {
24
+ this.nyc = null
25
+ onDone()
26
+ })
27
+ } else {
28
+ this.nyc = null
29
+ onDone()
30
+ }
31
+ })
32
+ }
33
+ }
34
+
35
+ module.exports = NycPlugin