dd-trace 3.49.0 → 3.51.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 (58) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/README.md +8 -99
  3. package/ci/cypress/after-run.js +1 -0
  4. package/ci/cypress/after-spec.js +1 -0
  5. package/index.d.ts +1499 -1486
  6. package/package.json +3 -3
  7. package/packages/datadog-core/src/utils/src/get.js +11 -0
  8. package/packages/datadog-core/src/utils/src/has.js +14 -0
  9. package/packages/datadog-core/src/utils/src/set.js +16 -0
  10. package/packages/datadog-instrumentations/src/amqplib.js +1 -1
  11. package/packages/datadog-instrumentations/src/cucumber.js +157 -42
  12. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  13. package/packages/datadog-instrumentations/src/jest.js +80 -40
  14. package/packages/datadog-instrumentations/src/mocha.js +4 -1
  15. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  16. package/packages/datadog-instrumentations/src/playwright.js +78 -16
  17. package/packages/datadog-plugin-amqplib/src/consumer.js +8 -4
  18. package/packages/datadog-plugin-amqplib/src/producer.js +3 -4
  19. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  20. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +60 -57
  21. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +42 -22
  22. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +64 -30
  23. package/packages/datadog-plugin-cucumber/src/index.js +25 -9
  24. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  25. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  26. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  27. package/packages/datadog-plugin-cypress/src/plugin.js +6 -549
  28. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  29. package/packages/datadog-plugin-graphql/src/index.js +1 -1
  30. package/packages/datadog-plugin-graphql/src/resolve.js +10 -8
  31. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  32. package/packages/datadog-plugin-jest/src/index.js +11 -2
  33. package/packages/datadog-plugin-kafkajs/src/consumer.js +4 -3
  34. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -5
  35. package/packages/datadog-plugin-playwright/src/index.js +34 -3
  36. package/packages/datadog-plugin-rhea/src/consumer.js +8 -3
  37. package/packages/datadog-plugin-rhea/src/producer.js +3 -4
  38. package/packages/dd-trace/src/appsec/iast/index.js +10 -0
  39. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +18 -5
  40. package/packages/dd-trace/src/appsec/recommended.json +67 -27
  41. package/packages/dd-trace/src/appsec/remote_config/index.js +1 -1
  42. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -3
  43. package/packages/dd-trace/src/config.js +451 -459
  44. package/packages/dd-trace/src/data_streams_context.js +1 -1
  45. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  46. package/packages/dd-trace/src/datastreams/processor.js +3 -5
  47. package/packages/dd-trace/src/format.js +0 -1
  48. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
  49. package/packages/dd-trace/src/opentracing/span.js +4 -4
  50. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  51. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  52. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -32
  53. package/packages/dd-trace/src/telemetry/index.js +22 -34
  54. package/packages/dd-trace/src/tracer.js +3 -3
  55. package/register.js +4 -0
  56. /package/packages/{utils → datadog-core/src/utils}/src/kebabcase.js +0 -0
  57. /package/packages/{utils → datadog-core/src/utils}/src/pick.js +0 -0
  58. /package/packages/{utils → datadog-core/src/utils}/src/uniq.js +0 -0
@@ -1,156 +1,5 @@
1
- const {
2
- TEST_STATUS,
3
- TEST_IS_RUM_ACTIVE,
4
- TEST_CODE_OWNERS,
5
- getTestEnvironmentMetadata,
6
- CI_APP_ORIGIN,
7
- getTestParentSpan,
8
- getCodeOwnersFileEntries,
9
- getCodeOwnersForFilename,
10
- getTestCommonTags,
11
- getTestSessionCommonTags,
12
- getTestModuleCommonTags,
13
- getTestSuiteCommonTags,
14
- TEST_SUITE_ID,
15
- TEST_MODULE_ID,
16
- TEST_SESSION_ID,
17
- TEST_COMMAND,
18
- TEST_MODULE,
19
- TEST_SOURCE_START,
20
- finishAllTraceSpans,
21
- getCoveredFilenamesFromCoverage,
22
- getTestSuitePath,
23
- addIntelligentTestRunnerSpanTags,
24
- TEST_SKIPPED_BY_ITR,
25
- TEST_ITR_UNSKIPPABLE,
26
- TEST_ITR_FORCED_RUN,
27
- ITR_CORRELATION_ID,
28
- TEST_SOURCE_FILE
29
- } = require('../../dd-trace/src/plugins/util/test')
30
- const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
31
- const log = require('../../dd-trace/src/log')
32
1
  const NoopTracer = require('../../dd-trace/src/noop/tracer')
33
- const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
34
- const {
35
- TELEMETRY_EVENT_CREATED,
36
- TELEMETRY_EVENT_FINISHED,
37
- TELEMETRY_ITR_FORCED_TO_RUN,
38
- TELEMETRY_CODE_COVERAGE_EMPTY,
39
- TELEMETRY_ITR_UNSKIPPABLE,
40
- TELEMETRY_CODE_COVERAGE_NUM_FILES,
41
- incrementCountMetric,
42
- distributionMetric
43
- } = require('../../dd-trace/src/ci-visibility/telemetry')
44
- const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
45
- const {
46
- GIT_REPOSITORY_URL,
47
- GIT_COMMIT_SHA,
48
- GIT_BRANCH,
49
- CI_PROVIDER_NAME,
50
- CI_WORKSPACE_PATH
51
- } = require('../../dd-trace/src/plugins/util/tags')
52
- const {
53
- OS_VERSION,
54
- OS_PLATFORM,
55
- OS_ARCHITECTURE,
56
- RUNTIME_NAME,
57
- RUNTIME_VERSION
58
- } = require('../../dd-trace/src/plugins/util/env')
59
-
60
- const TEST_FRAMEWORK_NAME = 'cypress'
61
-
62
- const CYPRESS_STATUS_TO_TEST_STATUS = {
63
- passed: 'pass',
64
- failed: 'fail',
65
- pending: 'skip',
66
- skipped: 'skip'
67
- }
68
-
69
- function getTestSpanMetadata (tracer, testName, testSuite, cypressConfig) {
70
- const childOf = getTestParentSpan(tracer)
71
-
72
- const commonTags = getTestCommonTags(testName, testSuite, cypressConfig.version, TEST_FRAMEWORK_NAME)
73
-
74
- return {
75
- childOf,
76
- ...commonTags
77
- }
78
- }
79
-
80
- function getCypressVersion (details) {
81
- if (details && details.cypressVersion) {
82
- return details.cypressVersion
83
- }
84
- if (details && details.config && details.config.version) {
85
- return details.config.version
86
- }
87
- return ''
88
- }
89
-
90
- function getRootDir (details) {
91
- if (details && details.config) {
92
- return details.config.projectRoot || details.config.repoRoot || process.cwd()
93
- }
94
- return process.cwd()
95
- }
96
-
97
- function getCypressCommand (details) {
98
- if (!details) {
99
- return TEST_FRAMEWORK_NAME
100
- }
101
- return `${TEST_FRAMEWORK_NAME} ${details.specPattern || ''}`
102
- }
103
-
104
- function getSessionStatus (summary) {
105
- if (summary.totalFailed !== undefined && summary.totalFailed > 0) {
106
- return 'fail'
107
- }
108
- if (summary.totalSkipped !== undefined && summary.totalSkipped === summary.totalTests) {
109
- return 'skip'
110
- }
111
- return 'pass'
112
- }
113
-
114
- function getSuiteStatus (suiteStats) {
115
- if (suiteStats.failures !== undefined && suiteStats.failures > 0) {
116
- return 'fail'
117
- }
118
- if (suiteStats.tests !== undefined &&
119
- (suiteStats.tests === suiteStats.pending || suiteStats.tests === suiteStats.skipped)) {
120
- return 'skip'
121
- }
122
- return 'pass'
123
- }
124
-
125
- function getLibraryConfiguration (tracer, testConfiguration) {
126
- return new Promise(resolve => {
127
- if (!tracer._tracer._exporter?.getLibraryConfiguration) {
128
- return resolve({ err: new Error('CI Visibility was not initialized correctly') })
129
- }
130
-
131
- tracer._tracer._exporter.getLibraryConfiguration(testConfiguration, (err, libraryConfig) => {
132
- resolve({ err, libraryConfig })
133
- })
134
- })
135
- }
136
-
137
- function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration) {
138
- if (!isSuitesSkippingEnabled) {
139
- return Promise.resolve({ skippableTests: [] })
140
- }
141
- return new Promise(resolve => {
142
- if (!tracer._tracer._exporter?.getLibraryConfiguration) {
143
- return resolve({ err: new Error('CI Visibility was not initialized correctly') })
144
- }
145
- tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
146
- resolve({
147
- err,
148
- skippableTests,
149
- correlationId
150
- })
151
- })
152
- })
153
- }
2
+ const cypressPlugin = require('./cypress-plugin')
154
3
 
155
4
  const noopTask = {
156
5
  'dd:testSuiteStart': () => {
@@ -168,8 +17,6 @@ const noopTask = {
168
17
  }
169
18
 
170
19
  module.exports = (on, config) => {
171
- let isTestsSkipped = false
172
- const skippedTests = []
173
20
  const tracer = require('../../dd-trace')
174
21
 
175
22
  // The tracer was not init correctly for whatever reason (such as invalid DD_SITE)
@@ -178,400 +25,10 @@ module.exports = (on, config) => {
178
25
  return on('task', noopTask)
179
26
  }
180
27
 
181
- const testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
182
-
183
- const {
184
- [GIT_REPOSITORY_URL]: repositoryUrl,
185
- [GIT_COMMIT_SHA]: sha,
186
- [OS_VERSION]: osVersion,
187
- [OS_PLATFORM]: osPlatform,
188
- [OS_ARCHITECTURE]: osArchitecture,
189
- [RUNTIME_NAME]: runtimeName,
190
- [RUNTIME_VERSION]: runtimeVersion,
191
- [GIT_BRANCH]: branch,
192
- [CI_PROVIDER_NAME]: ciProviderName,
193
- [CI_WORKSPACE_PATH]: repositoryRoot
194
- } = testEnvironmentMetadata
195
-
196
- const isUnsupportedCIProvider = !ciProviderName
197
-
198
- const finishedTestsByFile = {}
199
-
200
- const testConfiguration = {
201
- repositoryUrl,
202
- sha,
203
- osVersion,
204
- osPlatform,
205
- osArchitecture,
206
- runtimeName,
207
- runtimeVersion,
208
- branch,
209
- testLevel: 'test'
210
- }
211
-
212
- const codeOwnersEntries = getCodeOwnersFileEntries(repositoryRoot)
213
-
214
- let activeSpan = null
215
- let testSessionSpan = null
216
- let testModuleSpan = null
217
- let testSuiteSpan = null
218
- let command = null
219
- let frameworkVersion
220
- let rootDir
221
- let isSuitesSkippingEnabled = false
222
- let isCodeCoverageEnabled = false
223
- let testsToSkip = []
224
- let itrCorrelationId = ''
225
- const unskippableSuites = []
226
- let hasForcedToRunSuites = false
227
- let hasUnskippableSuites = false
228
-
229
- function ciVisEvent (name, testLevel, tags = {}) {
230
- incrementCountMetric(name, {
231
- testLevel,
232
- testFramework: 'cypress',
233
- isUnsupportedCIProvider,
234
- ...tags
235
- })
236
- }
237
-
238
- function getTestSpan (testName, testSuite, isUnskippable, isForcedToRun) {
239
- const testSuiteTags = {
240
- [TEST_COMMAND]: command,
241
- [TEST_COMMAND]: command,
242
- [TEST_MODULE]: TEST_FRAMEWORK_NAME
243
- }
244
- if (testSuiteSpan) {
245
- testSuiteTags[TEST_SUITE_ID] = testSuiteSpan.context().toSpanId()
246
- }
247
- if (testSessionSpan && testModuleSpan) {
248
- testSuiteTags[TEST_SESSION_ID] = testSessionSpan.context().toTraceId()
249
- testSuiteTags[TEST_MODULE_ID] = testModuleSpan.context().toSpanId()
250
- // If testSuiteSpan couldn't be created, we'll use the testModuleSpan as the parent
251
- if (!testSuiteSpan) {
252
- testSuiteTags[TEST_SUITE_ID] = testModuleSpan.context().toSpanId()
253
- }
254
- }
255
-
256
- const {
257
- childOf,
258
- resource,
259
- ...testSpanMetadata
260
- } = getTestSpanMetadata(tracer, testName, testSuite, config)
261
-
262
- const codeOwners = getCodeOwnersForFilename(testSuite, codeOwnersEntries)
263
-
264
- if (codeOwners) {
265
- testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
266
- }
267
-
268
- if (isUnskippable) {
269
- hasUnskippableSuites = true
270
- incrementCountMetric(TELEMETRY_ITR_UNSKIPPABLE, { testLevel: 'suite' })
271
- testSpanMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
272
- }
273
-
274
- if (isForcedToRun) {
275
- hasForcedToRunSuites = true
276
- incrementCountMetric(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
277
- testSpanMetadata[TEST_ITR_FORCED_RUN] = 'true'
278
- }
279
-
280
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'test', { hasCodeOwners: !!codeOwners })
281
-
282
- return tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test`, {
283
- childOf,
284
- tags: {
285
- [COMPONENT]: TEST_FRAMEWORK_NAME,
286
- [ORIGIN_KEY]: CI_APP_ORIGIN,
287
- ...testSpanMetadata,
288
- ...testEnvironmentMetadata,
289
- ...testSuiteTags
290
- }
291
- })
292
- }
293
-
294
- function getTestSuiteSpan (suite) {
295
- const testSuiteSpanMetadata = getTestSuiteCommonTags(command, frameworkVersion, suite, TEST_FRAMEWORK_NAME)
296
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
297
- return tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
298
- childOf: testModuleSpan,
299
- tags: {
300
- [COMPONENT]: TEST_FRAMEWORK_NAME,
301
- ...testEnvironmentMetadata,
302
- ...testSuiteSpanMetadata
303
- }
304
- })
305
- }
306
-
307
- on('before:run', (details) => {
308
- return getLibraryConfiguration(tracer, testConfiguration).then(({ err, libraryConfig }) => {
309
- if (err) {
310
- log.error(err)
311
- } else {
312
- isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
313
- isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
314
- }
315
-
316
- return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration)
317
- .then(({ err, skippableTests, correlationId }) => {
318
- if (err) {
319
- log.error(err)
320
- } else {
321
- testsToSkip = skippableTests || []
322
- itrCorrelationId = correlationId
323
- }
324
-
325
- // `details.specs` are test files
326
- details.specs.forEach(({ absolute, relative }) => {
327
- const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
328
- if (isUnskippableSuite) {
329
- unskippableSuites.push(relative)
330
- }
331
- })
332
-
333
- const childOf = getTestParentSpan(tracer)
334
- rootDir = getRootDir(details)
335
-
336
- command = getCypressCommand(details)
337
- frameworkVersion = getCypressVersion(details)
338
-
339
- const testSessionSpanMetadata = getTestSessionCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
340
- const testModuleSpanMetadata = getTestModuleCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
341
-
342
- testSessionSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
343
- childOf,
344
- tags: {
345
- [COMPONENT]: TEST_FRAMEWORK_NAME,
346
- ...testEnvironmentMetadata,
347
- ...testSessionSpanMetadata
348
- }
349
- })
350
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
351
-
352
- testModuleSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_module`, {
353
- childOf: testSessionSpan,
354
- tags: {
355
- [COMPONENT]: TEST_FRAMEWORK_NAME,
356
- ...testEnvironmentMetadata,
357
- ...testModuleSpanMetadata
358
- }
359
- })
360
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
361
-
362
- return details
363
- })
364
- })
365
- })
366
- on('after:spec', (spec, { tests, stats }) => {
367
- const cypressTests = tests || []
368
- const finishedTests = finishedTestsByFile[spec.relative] || []
369
-
370
- if (!testSuiteSpan) {
371
- // dd:testSuiteStart hasn't been triggered for whatever reason
372
- // We will create the test suite span on the spot if that's the case
373
- log.warn('There was an error creating the test suite event.')
374
- testSuiteSpan = getTestSuiteSpan(spec.relative)
375
- }
376
-
377
- // Get tests that didn't go through `dd:afterEach`
378
- // and create a skipped test span for each of them
379
- cypressTests.filter(({ title }) => {
380
- const cypressTestName = title.join(' ')
381
- const isTestFinished = finishedTests.find(({ testName }) => cypressTestName === testName)
382
-
383
- return !isTestFinished
384
- }).forEach(({ title }) => {
385
- const cypressTestName = title.join(' ')
386
- const isSkippedByItr = testsToSkip.find(test =>
387
- cypressTestName === test.name && spec.relative === test.suite
388
- )
389
- const skippedTestSpan = getTestSpan(cypressTestName, spec.relative)
390
- if (spec.absolute && repositoryRoot) {
391
- skippedTestSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
392
- } else {
393
- skippedTestSpan.setTag(TEST_SOURCE_FILE, spec.relative)
394
- }
395
- skippedTestSpan.setTag(TEST_STATUS, 'skip')
396
- if (isSkippedByItr) {
397
- skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
398
- }
399
- if (itrCorrelationId) {
400
- skippedTestSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
401
- }
402
- skippedTestSpan.finish()
403
- })
404
-
405
- // Make sure that reported test statuses are the same as Cypress reports.
406
- // This is not always the case, such as when an `after` hook fails:
407
- // Cypress will report the last run test as failed, but we don't know that yet at `dd:afterEach`
408
- let latestError
409
- finishedTests.forEach((finishedTest) => {
410
- const cypressTest = cypressTests.find(test => test.title.join(' ') === finishedTest.testName)
411
- if (!cypressTest) {
412
- return
413
- }
414
- if (cypressTest.displayError) {
415
- latestError = new Error(cypressTest.displayError)
416
- }
417
- const cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
418
- // update test status
419
- if (cypressTestStatus !== finishedTest.testStatus) {
420
- finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
421
- finishedTest.testSpan.setTag('error', latestError)
422
- }
423
- if (itrCorrelationId) {
424
- finishedTest.testSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
425
- }
426
- if (spec.absolute && repositoryRoot) {
427
- finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
428
- } else {
429
- finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative)
430
- }
431
- finishedTest.testSpan.finish(finishedTest.finishTime)
432
- })
433
-
434
- if (testSuiteSpan) {
435
- const status = getSuiteStatus(stats)
436
- testSuiteSpan.setTag(TEST_STATUS, status)
437
-
438
- if (latestError) {
439
- testSuiteSpan.setTag('error', latestError)
440
- }
441
- testSuiteSpan.finish()
442
- testSuiteSpan = null
443
- ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
444
- }
445
- })
446
-
447
- on('after:run', (suiteStats) => {
448
- if (testSessionSpan && testModuleSpan) {
449
- const testStatus = getSessionStatus(suiteStats)
450
- testModuleSpan.setTag(TEST_STATUS, testStatus)
451
- testSessionSpan.setTag(TEST_STATUS, testStatus)
452
-
453
- addIntelligentTestRunnerSpanTags(
454
- testSessionSpan,
455
- testModuleSpan,
456
- {
457
- isSuitesSkipped: isTestsSkipped,
458
- isSuitesSkippingEnabled,
459
- isCodeCoverageEnabled,
460
- skippingType: 'test',
461
- skippingCount: skippedTests.length,
462
- hasForcedToRunSuites,
463
- hasUnskippableSuites
464
- }
465
- )
466
-
467
- testModuleSpan.finish()
468
- ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
469
- testSessionSpan.finish()
470
- ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
471
-
472
- finishAllTraceSpans(testSessionSpan)
473
- }
474
-
475
- return new Promise(resolve => {
476
- const exporter = tracer._tracer._exporter
477
- if (!exporter) {
478
- return resolve(null)
479
- }
480
- if (exporter.flush) {
481
- exporter.flush(() => {
482
- appClosingTelemetry()
483
- resolve(null)
484
- })
485
- } else if (exporter._writer) {
486
- exporter._writer.flush(() => {
487
- appClosingTelemetry()
488
- resolve(null)
489
- })
490
- }
491
- })
492
- })
493
- on('task', {
494
- 'dd:testSuiteStart': (suite) => {
495
- if (testSuiteSpan) {
496
- return null
497
- }
498
- testSuiteSpan = getTestSuiteSpan(suite)
499
- return null
500
- },
501
- 'dd:beforeEach': (test) => {
502
- const { testName, testSuite } = test
503
- const shouldSkip = !!testsToSkip.find(test => {
504
- return testName === test.name && testSuite === test.suite
505
- })
506
- const isUnskippable = unskippableSuites.includes(testSuite)
507
- const isForcedToRun = shouldSkip && isUnskippable
508
-
509
- // skip test
510
- if (shouldSkip && !isUnskippable) {
511
- skippedTests.push(test)
512
- isTestsSkipped = true
513
- return { shouldSkip: true }
514
- }
515
-
516
- if (!activeSpan) {
517
- activeSpan = getTestSpan(testName, testSuite, isUnskippable, isForcedToRun)
518
- }
519
-
520
- return activeSpan ? { traceId: activeSpan.context().toTraceId() } : {}
521
- },
522
- 'dd:afterEach': ({ test, coverage }) => {
523
- const { state, error, isRUMActive, testSourceLine, testSuite, testName } = test
524
- if (activeSpan) {
525
- if (coverage && isCodeCoverageEnabled && tracer._tracer._exporter && tracer._tracer._exporter.exportCoverage) {
526
- const coverageFiles = getCoveredFilenamesFromCoverage(coverage)
527
- const relativeCoverageFiles = coverageFiles.map(file => getTestSuitePath(file, rootDir))
528
- if (!relativeCoverageFiles.length) {
529
- incrementCountMetric(TELEMETRY_CODE_COVERAGE_EMPTY)
530
- }
531
- distributionMetric(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
532
- const { _traceId, _spanId } = testSuiteSpan.context()
533
- const formattedCoverage = {
534
- sessionId: _traceId,
535
- suiteId: _spanId,
536
- testId: activeSpan.context()._spanId,
537
- files: relativeCoverageFiles
538
- }
539
- tracer._tracer._exporter.exportCoverage(formattedCoverage)
540
- }
541
- const testStatus = CYPRESS_STATUS_TO_TEST_STATUS[state]
542
- activeSpan.setTag(TEST_STATUS, testStatus)
28
+ cypressPlugin.init(tracer, config)
543
29
 
544
- if (error) {
545
- activeSpan.setTag('error', error)
546
- }
547
- if (isRUMActive) {
548
- activeSpan.setTag(TEST_IS_RUM_ACTIVE, 'true')
549
- }
550
- if (testSourceLine) {
551
- activeSpan.setTag(TEST_SOURCE_START, testSourceLine)
552
- }
553
- const finishedTest = {
554
- testName,
555
- testStatus,
556
- finishTime: activeSpan._getTime(), // we store the finish time here
557
- testSpan: activeSpan
558
- }
559
- if (finishedTestsByFile[testSuite]) {
560
- finishedTestsByFile[testSuite].push(finishedTest)
561
- } else {
562
- finishedTestsByFile[testSuite] = [finishedTest]
563
- }
564
- // test spans are finished at after:spec
565
- }
566
- activeSpan = null
567
- ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test')
568
- return null
569
- },
570
- 'dd:addTags': (tags) => {
571
- if (activeSpan) {
572
- activeSpan.addTags(tags)
573
- }
574
- return null
575
- }
576
- })
30
+ on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))
31
+ on('after:spec', cypressPlugin.afterSpec.bind(cypressPlugin))
32
+ on('after:run', cypressPlugin.afterRun.bind(cypressPlugin))
33
+ on('task', cypressPlugin.getTasks())
577
34
  }
@@ -1,4 +1,43 @@
1
1
  /* eslint-disable */
2
+ let isEarlyFlakeDetectionEnabled = false
3
+ let knownTestsForSuite = []
4
+ let suiteTests = []
5
+ let earlyFlakeDetectionNumRetries = 0
6
+
7
+ function isNewTest (test) {
8
+ return !knownTestsForSuite.includes(test.fullTitle())
9
+ }
10
+
11
+ function retryTest (test, suiteTests) {
12
+ for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
13
+ const clonedTest = test.clone()
14
+ // TODO: signal in framework logs that this is a retry.
15
+ // TODO: Change it so these tests are allowed to fail.
16
+ // TODO: figure out if reported duration is skewed.
17
+ suiteTests.unshift(clonedTest)
18
+ clonedTest._ddIsNew = true
19
+ clonedTest._ddIsEfdRetry = true
20
+ }
21
+ }
22
+
23
+
24
+ const oldRunTests = Cypress.mocha.getRunner().runTests
25
+ Cypress.mocha.getRunner().runTests = function (suite, fn) {
26
+ if (!isEarlyFlakeDetectionEnabled) {
27
+ return oldRunTests.apply(this, arguments)
28
+ }
29
+ // We copy the new tests at the beginning of the suite run (runTests), so that they're run
30
+ // multiple times.
31
+ suite.tests.forEach(test => {
32
+ if (!test._ddIsNew && !test.isPending() && isNewTest(test)) {
33
+ test._ddIsNew = true
34
+ retryTest(test, suite.tests)
35
+ }
36
+ })
37
+
38
+ return oldRunTests.apply(this, [suite, fn])
39
+ }
40
+
2
41
  beforeEach(function () {
3
42
  cy.task('dd:beforeEach', {
4
43
  testName: Cypress.mocha.getRunner().suite.ctx.currentTest.fullTitle(),
@@ -11,8 +50,14 @@ beforeEach(function () {
11
50
  })
12
51
  })
13
52
 
14
- before(() => {
15
- cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file)
53
+ before(function () {
54
+ cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file).then((suiteConfig) => {
55
+ if (suiteConfig) {
56
+ isEarlyFlakeDetectionEnabled = suiteConfig.isEarlyFlakeDetectionEnabled
57
+ knownTestsForSuite = suiteConfig.knownTestsForSuite
58
+ earlyFlakeDetectionNumRetries = suiteConfig.earlyFlakeDetectionNumRetries
59
+ }
60
+ })
16
61
  })
17
62
 
18
63
  after(() => {
@@ -24,7 +69,7 @@ after(() => {
24
69
  })
25
70
 
26
71
 
27
- afterEach(() => {
72
+ afterEach(function () {
28
73
  cy.window().then(win => {
29
74
  const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
30
75
  const testInfo = {
@@ -32,6 +77,8 @@ afterEach(() => {
32
77
  testSuite: Cypress.mocha.getRootSuite().file,
33
78
  state: currentTest.state,
34
79
  error: currentTest.err,
80
+ isNew: currentTest._ddIsNew,
81
+ isEfdRetry: currentTest._ddIsEfdRetry
35
82
  }
36
83
  try {
37
84
  testInfo.testSourceLine = Cypress.mocha.getRunner().currentRunnable.invocationDetails.line
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('../../utils/src/pick')
3
+ const pick = require('../../datadog-core/src/utils/src/pick')
4
4
  const CompositePlugin = require('../../dd-trace/src/plugins/composite')
5
5
  const log = require('../../dd-trace/src/log')
6
6
  const GraphQLExecutePlugin = require('./execute')
@@ -122,15 +122,17 @@ function getResolverInfo (info, args) {
122
122
  Object.assign(resolverVars, args)
123
123
  }
124
124
 
125
- const directives = info.fieldNodes[0].directives
126
- for (const directive of directives) {
127
- const argList = {}
128
- for (const argument of directive['arguments']) {
129
- argList[argument.name.value] = argument.value.value
130
- }
125
+ const directives = info.fieldNodes?.[0]?.directives
126
+ if (Array.isArray(directives)) {
127
+ for (const directive of directives) {
128
+ const argList = {}
129
+ for (const argument of directive['arguments']) {
130
+ argList[argument.name.value] = argument.value.value
131
+ }
131
132
 
132
- if (Object.keys(argList).length) {
133
- resolverVars[directive.name.value] = argList
133
+ if (Object.keys(argList).length) {
134
+ resolverVars[directive.name.value] = argList
135
+ }
134
136
  }
135
137
  }
136
138
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('../../utils/src/pick')
3
+ const pick = require('../../datadog-core/src/utils/src/pick')
4
4
  const log = require('../../dd-trace/src/log')
5
5
 
6
6
  module.exports = {