dd-trace 5.19.0 → 5.21.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 (67) hide show
  1. package/ext/formats.d.ts +1 -0
  2. package/ext/formats.js +2 -1
  3. package/index.d.ts +2 -1
  4. package/init.js +3 -15
  5. package/package.json +5 -4
  6. package/packages/datadog-instrumentations/src/body-parser.js +14 -2
  7. package/packages/datadog-instrumentations/src/cucumber.js +10 -0
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -2
  9. package/packages/datadog-instrumentations/src/helpers/register.js +21 -12
  10. package/packages/datadog-instrumentations/src/http/client.js +7 -1
  11. package/packages/datadog-instrumentations/src/http/server.js +50 -13
  12. package/packages/datadog-instrumentations/src/mocha/main.js +111 -78
  13. package/packages/datadog-instrumentations/src/nyc.js +23 -0
  14. package/packages/datadog-instrumentations/src/process.js +29 -0
  15. package/packages/datadog-instrumentations/src/vitest.js +65 -25
  16. package/packages/datadog-plugin-aws-sdk/src/base.js +15 -1
  17. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  18. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -1
  19. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +3 -3
  20. package/packages/datadog-plugin-cucumber/src/index.js +12 -2
  21. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +53 -12
  22. package/packages/datadog-plugin-jest/src/index.js +17 -4
  23. package/packages/datadog-plugin-mocha/src/index.js +25 -6
  24. package/packages/datadog-plugin-nyc/src/index.js +35 -0
  25. package/packages/datadog-plugin-playwright/src/index.js +9 -4
  26. package/packages/datadog-plugin-vitest/src/index.js +32 -5
  27. package/packages/dd-trace/src/appsec/blocking.js +10 -1
  28. package/packages/dd-trace/src/appsec/channels.js +4 -1
  29. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  30. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +16 -0
  31. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -0
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -1
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +11 -0
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/code-injection-sensitive-analyzer.js +25 -0
  35. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  36. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
  37. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  38. package/packages/dd-trace/src/appsec/index.js +12 -7
  39. package/packages/dd-trace/src/appsec/rasp.js +121 -7
  40. package/packages/dd-trace/src/appsec/recommended.json +220 -2
  41. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +40 -1
  42. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -4
  43. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -4
  44. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +8 -7
  45. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -4
  46. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -4
  47. package/packages/dd-trace/src/ci-visibility/telemetry.js +29 -2
  48. package/packages/dd-trace/src/config.js +158 -153
  49. package/packages/dd-trace/src/data_streams.js +44 -0
  50. package/packages/dd-trace/src/datastreams/pathway.js +4 -2
  51. package/packages/dd-trace/src/log/index.js +32 -0
  52. package/packages/dd-trace/src/opentelemetry/context_manager.js +22 -39
  53. package/packages/dd-trace/src/opentelemetry/span_context.js +2 -2
  54. package/packages/dd-trace/src/opentelemetry/tracer.js +23 -14
  55. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +9 -1
  56. package/packages/dd-trace/src/opentracing/propagation/log.js +1 -1
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +60 -0
  58. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +43 -0
  59. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  60. package/packages/dd-trace/src/opentracing/tracer.js +10 -6
  61. package/packages/dd-trace/src/plugins/ci_plugin.js +11 -4
  62. package/packages/dd-trace/src/plugins/index.js +1 -0
  63. package/packages/dd-trace/src/plugins/plugin.js +12 -1
  64. package/packages/dd-trace/src/plugins/util/git.js +14 -1
  65. package/packages/dd-trace/src/proxy.js +1 -0
  66. package/packages/dd-trace/src/telemetry/index.js +1 -1
  67. package/packages/dd-trace/src/tracer.js +2 -0
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel } = require('dc-polyfill')
5
+
6
+ const startSetUncaughtExceptionCaptureCallback = channel('datadog:process:setUncaughtExceptionCaptureCallback:start')
7
+
8
+ if (process.setUncaughtExceptionCaptureCallback) {
9
+ let currentCallback
10
+
11
+ shimmer.wrap(process, 'setUncaughtExceptionCaptureCallback',
12
+ function wrapSetUncaughtExceptionCaptureCallback (originalSetUncaughtExceptionCaptureCallback) {
13
+ return function setUncaughtExceptionCaptureCallback (newCallback) {
14
+ if (startSetUncaughtExceptionCaptureCallback.hasSubscribers) {
15
+ const abortController = new AbortController()
16
+ startSetUncaughtExceptionCaptureCallback.publish({ newCallback, currentCallback, abortController })
17
+ if (abortController.signal.aborted) {
18
+ return
19
+ }
20
+ }
21
+
22
+ const result = originalSetUncaughtExceptionCaptureCallback.apply(this, arguments)
23
+
24
+ currentCallback = newCallback
25
+
26
+ return result
27
+ }
28
+ })
29
+ }
@@ -32,6 +32,10 @@ function isReporterPackageNew (vitestPackage) {
32
32
  return vitestPackage.e?.name === 'BaseSequencer'
33
33
  }
34
34
 
35
+ function isReporterPackageNewest (vitestPackage) {
36
+ return vitestPackage.h?.name === 'BaseSequencer'
37
+ }
38
+
35
39
  function getChannelPromise (channelToPublishTo) {
36
40
  return new Promise(resolve => {
37
41
  sessionAsyncResource.runInAsyncScope(() => {
@@ -117,6 +121,21 @@ function getSortWrapper (sort) {
117
121
  this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
118
122
  }
119
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
+
120
139
  shimmer.wrap(this.ctx, 'exit', exit => async function () {
121
140
  let onFinish
122
141
 
@@ -132,8 +151,9 @@ function getSortWrapper (sort) {
132
151
  sessionAsyncResource.runInAsyncScope(() => {
133
152
  testSessionFinishCh.publish({
134
153
  status: getSessionStatus(this.state),
135
- onFinish,
136
- error
154
+ testCodeCoverageLinesTotal,
155
+ error,
156
+ onFinish
137
157
  })
138
158
  })
139
159
 
@@ -146,6 +166,21 @@ function getSortWrapper (sort) {
146
166
  }
147
167
  }
148
168
 
169
+ function getCreateCliWrapper (vitestPackage, frameworkVersion) {
170
+ shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
171
+ if (!testSessionStartCh.hasSubscribers) {
172
+ return oldCreateCli.apply(this, arguments)
173
+ }
174
+ sessionAsyncResource.runInAsyncScope(() => {
175
+ const processArgv = process.argv.slice(2).join(' ')
176
+ testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
177
+ })
178
+ return oldCreateCli.apply(this, arguments)
179
+ })
180
+
181
+ return vitestPackage
182
+ }
183
+
149
184
  addHook({
150
185
  name: 'vitest',
151
186
  versions: ['>=1.6.0'],
@@ -206,12 +241,25 @@ addHook({
206
241
  return vitestPackage
207
242
  })
208
243
 
244
+ // There are multiple index* files across different versions of vitest,
245
+ // so we check for the existence of BaseSequencer to determine if we are in the right file
246
+ addHook({
247
+ name: 'vitest',
248
+ versions: ['>=1.6.0 <2.0.0'],
249
+ filePattern: 'dist/vendor/index.*'
250
+ }, (vitestPackage) => {
251
+ if (isReporterPackage(vitestPackage)) {
252
+ shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
253
+ }
254
+
255
+ return vitestPackage
256
+ })
257
+
209
258
  addHook({
210
259
  name: 'vitest',
211
- versions: ['>=2.0.0'],
260
+ versions: ['>=2.0.0 <2.0.5'],
212
261
  filePattern: 'dist/vendor/index.*'
213
262
  }, (vitestPackage) => {
214
- // there are multiple index* files so we have to check the exported values
215
263
  if (isReporterPackageNew(vitestPackage)) {
216
264
  shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
217
265
  }
@@ -221,12 +269,11 @@ addHook({
221
269
 
222
270
  addHook({
223
271
  name: 'vitest',
224
- versions: ['>=1.6.0'],
225
- filePattern: 'dist/vendor/index.*'
272
+ versions: ['>=2.0.5'],
273
+ filePattern: 'dist/chunks/index.*'
226
274
  }, (vitestPackage) => {
227
- // there are multiple index* files so we have to check the exported values
228
- if (isReporterPackage(vitestPackage)) {
229
- shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
275
+ if (isReporterPackageNewest(vitestPackage)) {
276
+ shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
230
277
  }
231
278
 
232
279
  return vitestPackage
@@ -235,22 +282,15 @@ addHook({
235
282
  // Can't specify file because compiled vitest includes hashes in their files
236
283
  addHook({
237
284
  name: 'vitest',
238
- versions: ['>=1.6.0'],
285
+ versions: ['>=1.6.0 <2.0.5'],
239
286
  filePattern: 'dist/vendor/cac.*'
240
- }, (vitestPackage, frameworkVersion) => {
241
- shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
242
- if (!testSessionStartCh.hasSubscribers) {
243
- return oldCreateCli.apply(this, arguments)
244
- }
245
- sessionAsyncResource.runInAsyncScope(() => {
246
- const processArgv = process.argv.slice(2).join(' ')
247
- testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
248
- })
249
- return oldCreateCli.apply(this, arguments)
250
- })
287
+ }, getCreateCliWrapper)
251
288
 
252
- return vitestPackage
253
- })
289
+ addHook({
290
+ name: 'vitest',
291
+ versions: ['>=2.0.5'],
292
+ filePattern: 'dist/chunks/cac.*'
293
+ }, getCreateCliWrapper)
254
294
 
255
295
  // test suite start and finish
256
296
  // only relevant for workers
@@ -258,7 +298,7 @@ addHook({
258
298
  name: '@vitest/runner',
259
299
  versions: ['>=1.6.0'],
260
300
  file: 'dist/index.js'
261
- }, vitestPackage => {
301
+ }, (vitestPackage, frameworkVersion) => {
262
302
  shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPath) {
263
303
  let testSuiteError = null
264
304
  if (!testSuiteStartCh.hasSubscribers) {
@@ -267,7 +307,7 @@ addHook({
267
307
 
268
308
  const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
269
309
  testSuiteAsyncResource.runInAsyncScope(() => {
270
- testSuiteStartCh.publish(testPath[0])
310
+ testSuiteStartCh.publish({ testSuiteAbsolutePath: testPath[0], frameworkVersion })
271
311
  })
272
312
  const startTestsResponse = await startTests.apply(this, arguments)
273
313
 
@@ -4,6 +4,7 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
4
4
  const ClientPlugin = require('../../dd-trace/src/plugins/client')
5
5
  const { storage } = require('../../datadog-core')
6
6
  const { isTrue } = require('../../dd-trace/src/util')
7
+ const coalesce = require('koalas')
7
8
 
8
9
  class BaseAwsSdkPlugin extends ClientPlugin {
9
10
  static get id () { return 'aws' }
@@ -163,9 +164,22 @@ function normalizeConfig (config, serviceIdentifier) {
163
164
  break
164
165
  }
165
166
 
167
+ // check if AWS batch propagation or AWS_[SERVICE] batch propagation is enabled via env variable
168
+ const serviceId = serviceIdentifier.toUpperCase()
169
+ const batchPropagationEnabled = isTrue(
170
+ coalesce(
171
+ specificConfig.batchPropagationEnabled,
172
+ process.env[`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`],
173
+ config.batchPropagationEnabled,
174
+ process.env.DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED,
175
+ false
176
+ )
177
+ )
178
+
179
+ // Merge the specific config back into the main config
166
180
  return Object.assign({}, config, specificConfig, {
167
181
  splitByAwsService: config.splitByAwsService !== false,
168
- batchPropagationEnabled: config.batchPropagationEnabled !== false,
182
+ batchPropagationEnabled,
169
183
  hooks
170
184
  })
171
185
  }
@@ -156,7 +156,7 @@ class Kinesis extends BaseAwsSdkPlugin {
156
156
  span,
157
157
  params.Records[i],
158
158
  stream,
159
- i === 0 || (this.config.kinesis && this.config.kinesis.batchPropagationEnabled)
159
+ i === 0 || (this.config.batchPropagationEnabled)
160
160
  )
161
161
  }
162
162
  }
@@ -63,7 +63,7 @@ class Sns extends BaseAwsSdkPlugin {
63
63
  span,
64
64
  params.PublishBatchRequestEntries[i],
65
65
  params.TopicArn,
66
- i === 0 || (this.config.sns && this.config.sns.batchPropagationEnabled)
66
+ i === 0 || (this.config.batchPropagationEnabled)
67
67
  )
68
68
  }
69
69
  break
@@ -157,8 +157,8 @@ class Sqs extends BaseAwsSdkPlugin {
157
157
  if (attributes.StringValue) {
158
158
  const textMap = attributes.StringValue
159
159
  return JSON.parse(textMap)
160
- } else if (attributes.Type === 'Binary') {
161
- const buffer = Buffer.from(attributes.Value, 'base64')
160
+ } else if (attributes.Type === 'Binary' || attributes.DataType === 'Binary') {
161
+ const buffer = Buffer.from(attributes.Value ?? attributes.BinaryValue, 'base64')
162
162
  return JSON.parse(buffer)
163
163
  }
164
164
  } catch (e) {
@@ -222,7 +222,7 @@ class Sqs extends BaseAwsSdkPlugin {
222
222
  span,
223
223
  params.Entries[i],
224
224
  params.QueueUrl,
225
- i === 0 || (this.config.sqs && this.config.sqs.batchPropagationEnabled)
225
+ i === 0 || (this.config.batchPropagationEnabled)
226
226
  )
227
227
  }
228
228
  break
@@ -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 = {
@@ -258,7 +260,7 @@ class CypressPlugin {
258
260
  })
259
261
  }
260
262
 
261
- getTestSpan (testName, testSuite, isUnskippable, isForcedToRun) {
263
+ getTestSpan ({ testName, testSuite, isUnskippable, isForcedToRun, testSourceFile }) {
262
264
  const testSuiteTags = {
263
265
  [TEST_COMMAND]: this.command,
264
266
  [TEST_COMMAND]: this.command,
@@ -282,8 +284,11 @@ class CypressPlugin {
282
284
  ...testSpanMetadata
283
285
  } = getTestCommonTags(testName, testSuite, this.cypressConfig.version, TEST_FRAMEWORK_NAME)
284
286
 
285
- const codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
287
+ if (testSourceFile) {
288
+ testSpanMetadata[TEST_SOURCE_FILE] = testSourceFile
289
+ }
286
290
 
291
+ const codeOwners = this.getTestCodeOwners({ testSuite, testSourceFile })
287
292
  if (codeOwners) {
288
293
  testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
289
294
  }
@@ -318,7 +323,7 @@ class CypressPlugin {
318
323
  incrementCountMetric(name, {
319
324
  testLevel,
320
325
  testFramework: 'cypress',
321
- isUnsupportedCIProvider: this.isUnsupportedCIProvider,
326
+ isUnsupportedCIProvider: !this.ciProviderName,
322
327
  ...tags
323
328
  })
324
329
  }
@@ -360,6 +365,7 @@ class CypressPlugin {
360
365
  const { skippableTests, correlationId } = skippableTestsResponse
361
366
  this.testsToSkip = skippableTests || []
362
367
  this.itrCorrelationId = correlationId
368
+ incrementCountMetric(TELEMETRY_ITR_SKIPPED, { testLevel: 'test' }, this.testsToSkip.length)
363
369
  }
364
370
  }
365
371
 
@@ -433,6 +439,9 @@ class CypressPlugin {
433
439
  this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
434
440
  this.testSessionSpan.finish()
435
441
  this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
442
+ incrementCountMetric(TELEMETRY_TEST_SESSION, {
443
+ provider: this.ciProviderName
444
+ })
436
445
 
437
446
  finishAllTraceSpans(this.testSessionSpan)
438
447
  }
@@ -480,12 +489,16 @@ class CypressPlugin {
480
489
  const isSkippedByItr = this.testsToSkip.find(test =>
481
490
  cypressTestName === test.name && spec.relative === test.suite
482
491
  )
483
- const skippedTestSpan = this.getTestSpan(cypressTestName, spec.relative)
492
+ let testSourceFile
493
+
484
494
  if (spec.absolute && this.repositoryRoot) {
485
- skippedTestSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot))
495
+ testSourceFile = getTestSuitePath(spec.absolute, this.repositoryRoot)
486
496
  } else {
487
- skippedTestSpan.setTag(TEST_SOURCE_FILE, spec.relative)
497
+ testSourceFile = spec.relative
488
498
  }
499
+
500
+ const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
501
+
489
502
  skippedTestSpan.setTag(TEST_STATUS, 'skip')
490
503
  if (isSkippedByItr) {
491
504
  skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
@@ -538,11 +551,21 @@ class CypressPlugin {
538
551
  if (this.itrCorrelationId) {
539
552
  finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
540
553
  }
554
+ let testSourceFile
541
555
  if (spec.absolute && this.repositoryRoot) {
542
- finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot))
556
+ testSourceFile = getTestSuitePath(spec.absolute, this.repositoryRoot)
543
557
  } else {
544
- finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative)
558
+ testSourceFile = spec.relative
559
+ }
560
+ if (testSourceFile) {
561
+ finishedTest.testSpan.setTag(TEST_SOURCE_FILE, testSourceFile)
562
+ }
563
+ const codeOwners = this.getTestCodeOwners({ testSuite: spec.relative, testSourceFile })
564
+
565
+ if (codeOwners) {
566
+ finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
545
567
  }
568
+
546
569
  finishedTest.testSpan.finish(finishedTest.finishTime)
547
570
  })
548
571
  })
@@ -591,7 +614,12 @@ class CypressPlugin {
591
614
  }
592
615
 
593
616
  if (!this.activeTestSpan) {
594
- this.activeTestSpan = this.getTestSpan(testName, testSuite, isUnskippable, isForcedToRun)
617
+ this.activeTestSpan = this.getTestSpan({
618
+ testName,
619
+ testSuite,
620
+ isUnskippable,
621
+ isForcedToRun
622
+ })
595
623
  }
596
624
 
597
625
  return this.activeTestSpan ? { traceId: this.activeTestSpan.context().toTraceId() } : {}
@@ -646,8 +674,14 @@ class CypressPlugin {
646
674
  }
647
675
  // test spans are finished at after:spec
648
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
+ })
649
683
  this.activeTestSpan = null
650
- this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test')
684
+
651
685
  return null
652
686
  },
653
687
  'dd:addTags': (tags) => {
@@ -658,6 +692,13 @@ class CypressPlugin {
658
692
  }
659
693
  }
660
694
  }
695
+
696
+ getTestCodeOwners ({ testSuite, testSourceFile }) {
697
+ if (testSourceFile) {
698
+ return getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries)
699
+ }
700
+ return getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
701
+ }
661
702
  }
662
703
 
663
704
  module.exports = new CypressPlugin()
@@ -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
@@ -14,7 +14,8 @@ const {
14
14
  TEST_CONFIGURATION_BROWSER_NAME,
15
15
  TEST_IS_NEW,
16
16
  TEST_IS_RETRY,
17
- TEST_EARLY_FLAKE_ENABLED
17
+ TEST_EARLY_FLAKE_ENABLED,
18
+ TELEMETRY_TEST_SESSION
18
19
  } = require('../../dd-trace/src/plugins/util/test')
19
20
  const { RESOURCE_NAME } = require('../../../ext/tags')
20
21
  const { COMPONENT } = require('../../dd-trace/src/constants')
@@ -59,6 +60,7 @@ class PlaywrightPlugin extends CiPlugin {
59
60
  this.testSessionSpan.finish()
60
61
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
61
62
  finishAllTraceSpans(this.testSessionSpan)
63
+ this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName })
62
64
  appClosingTelemetry()
63
65
  this.tracer._exporter.flush(onDone)
64
66
  this.numFailedTests = 0
@@ -160,8 +162,6 @@ class PlaywrightPlugin extends CiPlugin {
160
162
  stepSpan.finish(stepStartTime + stepDuration)
161
163
  })
162
164
 
163
- span.finish()
164
-
165
165
  if (testStatus === 'fail') {
166
166
  this.numFailedTests++
167
167
  }
@@ -169,8 +169,13 @@ class PlaywrightPlugin extends CiPlugin {
169
169
  this.telemetry.ciVisEvent(
170
170
  TELEMETRY_EVENT_FINISHED,
171
171
  'test',
172
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
172
+ {
173
+ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS],
174
+ isNew,
175
+ browserDriver: 'playwright'
176
+ }
173
177
  )
178
+ span.finish()
174
179
 
175
180
  finishAllTraceSpans(span)
176
181
  })