dd-trace 5.104.0 → 5.105.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.
- package/LICENSE-3rdparty.csv +90 -102
- package/index.d.ts +82 -3
- package/package.json +15 -15
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +13 -0
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber.js +78 -5
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -62
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +360 -150
- package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
- package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +515 -292
- package/packages/datadog-instrumentations/src/router.js +76 -32
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -13
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +33 -40
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +5 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
- package/packages/dd-trace/src/config/supported-configurations.json +27 -8
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +0 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +43 -5
- package/packages/dd-trace/src/plugins/util/test.js +236 -13
- package/packages/dd-trace/src/plugins/util/web.js +79 -65
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const { performance } = require('perf_hooks')
|
|
5
5
|
const dateNow = Date.now
|
|
6
6
|
|
|
7
|
+
const { createCoverageMap } = require('../../../vendor/dist/istanbul-lib-coverage')
|
|
7
8
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
8
9
|
const {
|
|
9
10
|
TEST_STATUS,
|
|
@@ -25,7 +26,12 @@ const {
|
|
|
25
26
|
TEST_MODULE,
|
|
26
27
|
TEST_SOURCE_START,
|
|
27
28
|
finishAllTraceSpans,
|
|
28
|
-
|
|
29
|
+
getCoveredFilesFromCoverage,
|
|
30
|
+
getExecutableFilesFromCoverage,
|
|
31
|
+
getRelativeCoverageFiles,
|
|
32
|
+
getTestCoverageLinesPercentage,
|
|
33
|
+
applySkippedCoverageToCoverage,
|
|
34
|
+
mergeCoverage,
|
|
29
35
|
getTestSuitePath,
|
|
30
36
|
addIntelligentTestRunnerSpanTags,
|
|
31
37
|
TEST_SKIPPED_BY_ITR,
|
|
@@ -40,7 +46,6 @@ const {
|
|
|
40
46
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
41
47
|
getTestSessionName,
|
|
42
48
|
TEST_SESSION_NAME,
|
|
43
|
-
TEST_LEVEL_EVENT_TYPES,
|
|
44
49
|
TEST_RETRY_REASON,
|
|
45
50
|
DD_TEST_IS_USER_PROVIDED_SERVICE,
|
|
46
51
|
TEST_MANAGEMENT_IS_QUARANTINED,
|
|
@@ -74,6 +79,7 @@ const {
|
|
|
74
79
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
75
80
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
76
81
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
82
|
+
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
77
83
|
const getConfig = require('../../dd-trace/src/config')
|
|
78
84
|
const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
|
|
79
85
|
const log = require('../../dd-trace/src/log')
|
|
@@ -201,13 +207,17 @@ function getSkippableTests (tracer, testConfiguration) {
|
|
|
201
207
|
if (!tracer._tracer._exporter?.getSkippableSuites) {
|
|
202
208
|
return resolve({ err: new Error('Test Optimization was not initialized correctly') })
|
|
203
209
|
}
|
|
204
|
-
tracer._tracer._exporter.getSkippableSuites(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
tracer._tracer._exporter.getSkippableSuites(
|
|
211
|
+
testConfiguration,
|
|
212
|
+
(err, skippableTests, correlationId, skippableTestsCoverage) => {
|
|
213
|
+
resolve({
|
|
214
|
+
err,
|
|
215
|
+
skippableTests,
|
|
216
|
+
correlationId,
|
|
217
|
+
skippableTestsCoverage,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
)
|
|
211
221
|
})
|
|
212
222
|
}
|
|
213
223
|
|
|
@@ -361,9 +371,11 @@ class CypressPlugin {
|
|
|
361
371
|
finishedTestsByFile = {}
|
|
362
372
|
testStatuses = {}
|
|
363
373
|
hasLibraryConfiguration = false
|
|
374
|
+
isItrEnabled = false
|
|
364
375
|
isTestsSkipped = false
|
|
365
376
|
isSuitesSkippingEnabled = false
|
|
366
377
|
isCodeCoverageEnabled = false
|
|
378
|
+
isCoverageReportUploadEnabled = false
|
|
367
379
|
isFlakyTestRetriesEnabled = false
|
|
368
380
|
flakyTestRetriesCount = 0
|
|
369
381
|
isEarlyFlakeDetectionEnabled = false
|
|
@@ -376,6 +388,8 @@ class CypressPlugin {
|
|
|
376
388
|
earlyFlakeDetectionFaultyThreshold = 0
|
|
377
389
|
testsToSkip = []
|
|
378
390
|
skippedTests = []
|
|
391
|
+
skippableTestsCoverage = {}
|
|
392
|
+
testSessionCoverageMap = createCoverageMap()
|
|
379
393
|
hasForcedToRunSuites = false
|
|
380
394
|
hasUnskippableSuites = false
|
|
381
395
|
unskippableSuites = []
|
|
@@ -440,9 +454,11 @@ class CypressPlugin {
|
|
|
440
454
|
this.finishedTestsByFile = {}
|
|
441
455
|
this.testStatuses = {}
|
|
442
456
|
this.hasLibraryConfiguration = false
|
|
457
|
+
this.isItrEnabled = false
|
|
443
458
|
this.isTestsSkipped = false
|
|
444
459
|
this.isSuitesSkippingEnabled = false
|
|
445
460
|
this.isCodeCoverageEnabled = false
|
|
461
|
+
this.isCoverageReportUploadEnabled = false
|
|
446
462
|
this.isFlakyTestRetriesEnabled = false
|
|
447
463
|
this.flakyTestRetriesCount = 0
|
|
448
464
|
this.isEarlyFlakeDetectionEnabled = false
|
|
@@ -455,6 +471,8 @@ class CypressPlugin {
|
|
|
455
471
|
this.earlyFlakeDetectionFaultyThreshold = 0
|
|
456
472
|
this.testsToSkip = []
|
|
457
473
|
this.skippedTests = []
|
|
474
|
+
this.skippableTestsCoverage = {}
|
|
475
|
+
this.testSessionCoverageMap = createCoverageMap()
|
|
458
476
|
this.hasForcedToRunSuites = false
|
|
459
477
|
this.hasUnskippableSuites = false
|
|
460
478
|
this.unskippableSuites = []
|
|
@@ -494,6 +512,117 @@ class CypressPlugin {
|
|
|
494
512
|
return this._timeOrigin + performance.now() - this._perfOrigin
|
|
495
513
|
}
|
|
496
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Returns the directory used to normalize coverage file names.
|
|
517
|
+
*
|
|
518
|
+
* @returns {string}
|
|
519
|
+
*/
|
|
520
|
+
getCoverageRootDir () {
|
|
521
|
+
return this.repositoryRoot || this.rootDir || process.cwd()
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Returns whether the backend supplied skipped-test coverage data.
|
|
526
|
+
*
|
|
527
|
+
* @returns {boolean}
|
|
528
|
+
*/
|
|
529
|
+
hasSkippableTestsCoverage () {
|
|
530
|
+
return !!(this.skippableTestsCoverage &&
|
|
531
|
+
typeof this.skippableTestsCoverage === 'object' &&
|
|
532
|
+
Object.keys(this.skippableTestsCoverage).length > 0)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Returns whether skipped test coverage should be backfilled into the session coverage map.
|
|
537
|
+
*
|
|
538
|
+
* @returns {boolean}
|
|
539
|
+
*/
|
|
540
|
+
shouldBackfillSkippedCoverage () {
|
|
541
|
+
return this.isItrEnabled &&
|
|
542
|
+
this.isCoverageReportUploadEnabled &&
|
|
543
|
+
this.isTestsSkipped &&
|
|
544
|
+
this.hasSkippableTestsCoverage()
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Adds a test's Istanbul coverage to the aggregated session coverage map.
|
|
549
|
+
*
|
|
550
|
+
* @param {object} coverage
|
|
551
|
+
* @returns {void}
|
|
552
|
+
*/
|
|
553
|
+
addTestSessionCoverage (coverage) {
|
|
554
|
+
mergeCoverage(coverage, this.testSessionCoverageMap)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Applies backend skipped-test coverage to the aggregated session coverage map.
|
|
559
|
+
*
|
|
560
|
+
* @returns {boolean}
|
|
561
|
+
*/
|
|
562
|
+
applySkippedCoverageToTestSessionCoverage () {
|
|
563
|
+
if (!this.shouldBackfillSkippedCoverage()) {
|
|
564
|
+
return false
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return applySkippedCoverageToCoverage(
|
|
568
|
+
this.testSessionCoverageMap,
|
|
569
|
+
this.skippableTestsCoverage,
|
|
570
|
+
this.getCoverageRootDir()
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Calculates the total session code coverage percentage when product rules allow reporting it.
|
|
576
|
+
*
|
|
577
|
+
* @param {boolean} hasBackfilledCoverage
|
|
578
|
+
* @returns {number | undefined}
|
|
579
|
+
*/
|
|
580
|
+
getTestCodeCoverageLinesTotal (hasBackfilledCoverage) {
|
|
581
|
+
if (!this.testSessionCoverageMap.files().length || (this.isTestsSkipped && !hasBackfilledCoverage)) {
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return getTestCoverageLinesPercentage(this.testSessionCoverageMap, undefined, this.getCoverageRootDir())
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Returns repository-relative executable-line coverage files for the test session.
|
|
590
|
+
*
|
|
591
|
+
* @returns {Array<{ filename: string, bitmap: Buffer }>}
|
|
592
|
+
*/
|
|
593
|
+
getTestSessionCoverageFiles () {
|
|
594
|
+
return getRelativeCoverageFiles(
|
|
595
|
+
getExecutableFilesFromCoverage(this.testSessionCoverageMap),
|
|
596
|
+
this.getCoverageRootDir()
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Uploads executable-line coverage for the test session when backend configuration enables it.
|
|
602
|
+
*
|
|
603
|
+
* @returns {void}
|
|
604
|
+
*/
|
|
605
|
+
reportTestSessionCoverage () {
|
|
606
|
+
const exporter = this.tracer._tracer._exporter
|
|
607
|
+
if (
|
|
608
|
+
!this.testSessionSpan ||
|
|
609
|
+
!this.isCoverageReportUploadEnabled ||
|
|
610
|
+
!exporter?.exportCoverage
|
|
611
|
+
) {
|
|
612
|
+
return
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const files = this.getTestSessionCoverageFiles()
|
|
616
|
+
if (!files.length) {
|
|
617
|
+
return
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
exporter.exportCoverage({
|
|
621
|
+
sessionId: this.testSessionSpan.context()._traceId,
|
|
622
|
+
files,
|
|
623
|
+
})
|
|
624
|
+
}
|
|
625
|
+
|
|
497
626
|
// Init function returns a promise that resolves with the Cypress configuration
|
|
498
627
|
// Depending on the received configuration, the Cypress configuration can be modified:
|
|
499
628
|
// for example, to enable retries for failed tests.
|
|
@@ -529,8 +658,10 @@ class CypressPlugin {
|
|
|
529
658
|
this.hasLibraryConfiguration = true
|
|
530
659
|
const {
|
|
531
660
|
libraryConfig: {
|
|
661
|
+
isItrEnabled,
|
|
532
662
|
isSuitesSkippingEnabled,
|
|
533
663
|
isCodeCoverageEnabled,
|
|
664
|
+
isCoverageReportUploadEnabled,
|
|
534
665
|
isEarlyFlakeDetectionEnabled,
|
|
535
666
|
earlyFlakeDetectionNumRetries,
|
|
536
667
|
earlyFlakeDetectionSlowTestRetries,
|
|
@@ -543,8 +674,10 @@ class CypressPlugin {
|
|
|
543
674
|
isImpactedTestsEnabled,
|
|
544
675
|
},
|
|
545
676
|
} = libraryConfigurationResponse
|
|
677
|
+
this.isItrEnabled = isItrEnabled
|
|
546
678
|
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
|
|
547
679
|
this.isCodeCoverageEnabled = isCodeCoverageEnabled
|
|
680
|
+
this.isCoverageReportUploadEnabled = isCoverageReportUploadEnabled
|
|
548
681
|
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
|
|
549
682
|
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
|
|
550
683
|
this.earlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries ?? {}
|
|
@@ -686,7 +819,6 @@ class CypressPlugin {
|
|
|
686
819
|
|
|
687
820
|
getTestSpan ({ testName, testSuite, isUnskippable, isForcedToRun, testSourceFile, isDisabled, isQuarantined }) {
|
|
688
821
|
const testSuiteTags = {
|
|
689
|
-
[TEST_COMMAND]: this.command,
|
|
690
822
|
[TEST_MODULE]: TEST_FRAMEWORK_NAME,
|
|
691
823
|
}
|
|
692
824
|
if (this.testSuiteSpan) {
|
|
@@ -798,7 +930,10 @@ class CypressPlugin {
|
|
|
798
930
|
isSuitesSkippingEnabled: this.isSuitesSkippingEnabled,
|
|
799
931
|
getKnownTests: () => getKnownTests(this.tracer, this.testConfiguration),
|
|
800
932
|
getTestManagementTests: () => getTestManagementTests(this.tracer, this.testConfiguration),
|
|
801
|
-
getSkippableSuites: () => getSkippableTests(this.tracer,
|
|
933
|
+
getSkippableSuites: () => getSkippableTests(this.tracer, {
|
|
934
|
+
...this.testConfiguration,
|
|
935
|
+
isCoverageReportUploadEnabled: this.isCoverageReportUploadEnabled,
|
|
936
|
+
}),
|
|
802
937
|
})
|
|
803
938
|
|
|
804
939
|
if (this.isKnownTestsEnabled) {
|
|
@@ -835,13 +970,17 @@ class CypressPlugin {
|
|
|
835
970
|
|
|
836
971
|
if (this.isSuitesSkippingEnabled) {
|
|
837
972
|
const skippableTestsResponse =
|
|
838
|
-
skippableTestsRequestResponse || await getSkippableTests(this.tracer,
|
|
973
|
+
skippableTestsRequestResponse || await getSkippableTests(this.tracer, {
|
|
974
|
+
...this.testConfiguration,
|
|
975
|
+
isCoverageReportUploadEnabled: this.isCoverageReportUploadEnabled,
|
|
976
|
+
})
|
|
839
977
|
if (skippableTestsResponse.err) {
|
|
840
978
|
log.error('Cypress skippable tests response error', skippableTestsResponse.err)
|
|
841
979
|
this._pendingRequestErrorTags.push({ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, value: 'true' })
|
|
842
980
|
} else {
|
|
843
|
-
const { skippableTests, correlationId } = skippableTestsResponse
|
|
981
|
+
const { skippableTests, correlationId, skippableTestsCoverage } = skippableTestsResponse
|
|
844
982
|
this.testsToSkip = skippableTests || []
|
|
983
|
+
this.skippableTestsCoverage = skippableTestsCoverage || {}
|
|
845
984
|
this.itrCorrelationId = correlationId
|
|
846
985
|
incrementCountMetric(TELEMETRY_ITR_SKIPPED, { testLevel: 'test' }, this.testsToSkip.length)
|
|
847
986
|
}
|
|
@@ -904,15 +1043,9 @@ class CypressPlugin {
|
|
|
904
1043
|
)
|
|
905
1044
|
|
|
906
1045
|
if (this.tracer._tracer._exporter?.addMetadataTags) {
|
|
907
|
-
const metadataTags = {}
|
|
908
|
-
for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
|
|
909
|
-
metadataTags[testLevel] = {
|
|
910
|
-
[TEST_SESSION_NAME]: testSessionName,
|
|
911
|
-
}
|
|
912
|
-
}
|
|
1046
|
+
const metadataTags = { '*': { [TEST_COMMAND]: this.command, [TEST_SESSION_NAME]: testSessionName } }
|
|
913
1047
|
const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id, this.frameworkVersion)
|
|
914
1048
|
metadataTags.test = {
|
|
915
|
-
...metadataTags.test,
|
|
916
1049
|
...libraryCapabilitiesTags,
|
|
917
1050
|
}
|
|
918
1051
|
|
|
@@ -969,6 +1102,9 @@ class CypressPlugin {
|
|
|
969
1102
|
}
|
|
970
1103
|
if (this.testSessionSpan && this.testModuleSpan) {
|
|
971
1104
|
const testStatus = getSessionStatus(suiteStats)
|
|
1105
|
+
const hasBackfilledCoverage = this.applySkippedCoverageToTestSessionCoverage()
|
|
1106
|
+
const testCodeCoverageLinesTotal = this.getTestCodeCoverageLinesTotal(hasBackfilledCoverage)
|
|
1107
|
+
|
|
972
1108
|
this.testModuleSpan.setTag(TEST_STATUS, testStatus)
|
|
973
1109
|
this.testSessionSpan.setTag(TEST_STATUS, testStatus)
|
|
974
1110
|
|
|
@@ -979,6 +1115,7 @@ class CypressPlugin {
|
|
|
979
1115
|
isSuitesSkipped: this.isTestsSkipped,
|
|
980
1116
|
isSuitesSkippingEnabled: this.isSuitesSkippingEnabled,
|
|
981
1117
|
isCodeCoverageEnabled: this.isCodeCoverageEnabled,
|
|
1118
|
+
testCodeCoverageLinesTotal,
|
|
982
1119
|
skippingType: 'test',
|
|
983
1120
|
skippingCount: this.skippedTests.length,
|
|
984
1121
|
hasForcedToRunSuites: this.hasForcedToRunSuites,
|
|
@@ -986,6 +1123,8 @@ class CypressPlugin {
|
|
|
986
1123
|
}
|
|
987
1124
|
)
|
|
988
1125
|
|
|
1126
|
+
this.reportTestSessionCoverage()
|
|
1127
|
+
|
|
989
1128
|
if (this.isTestManagementTestsEnabled) {
|
|
990
1129
|
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
991
1130
|
}
|
|
@@ -1145,7 +1284,7 @@ class CypressPlugin {
|
|
|
1145
1284
|
}
|
|
1146
1285
|
// Update test status - but NOT for non-ATF quarantined tests where we intentionally
|
|
1147
1286
|
// report 'fail' to Datadog even though Cypress sees it as 'pass'
|
|
1148
|
-
const isQuarantinedTest = finishedTest.testSpan?.context()?.
|
|
1287
|
+
const isQuarantinedTest = finishedTest.testSpan?.context()?.getTag(TEST_MANAGEMENT_IS_QUARANTINED) === 'true'
|
|
1149
1288
|
if (cypressTestStatus !== finishedTest.testStatus && (!isQuarantinedTest || finishedTest.isAttemptToFix)) {
|
|
1150
1289
|
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
|
|
1151
1290
|
finishedTest.testSpan.setTag('error', latestError)
|
|
@@ -1172,7 +1311,7 @@ class CypressPlugin {
|
|
|
1172
1311
|
}
|
|
1173
1312
|
|
|
1174
1313
|
if (isLastAttempt) {
|
|
1175
|
-
const testSpanTags = finishedTest.testSpan.context().
|
|
1314
|
+
const testSpanTags = finishedTest.testSpan.context().getTags()
|
|
1176
1315
|
const retryKind = getFinalStatusRetryKind({
|
|
1177
1316
|
finishedTest,
|
|
1178
1317
|
finishedTestAttempts,
|
|
@@ -1280,7 +1419,7 @@ class CypressPlugin {
|
|
|
1280
1419
|
|
|
1281
1420
|
return this.activeTestSpan ? { traceId: this.activeTestSpan.context().toTraceId() } : {}
|
|
1282
1421
|
},
|
|
1283
|
-
'dd:afterEach': ({ test, coverage }) => {
|
|
1422
|
+
'dd:afterEach': ({ test, coverage, commands }) => {
|
|
1284
1423
|
if (!this.activeTestSpan) {
|
|
1285
1424
|
log.warn('There is no active test span in dd:afterEach handler')
|
|
1286
1425
|
return null
|
|
@@ -1303,11 +1442,18 @@ class CypressPlugin {
|
|
|
1303
1442
|
isQuarantined: isQuarantinedFromSupport,
|
|
1304
1443
|
isDisabled: isDisabledFromSupport,
|
|
1305
1444
|
} = test
|
|
1445
|
+
if (coverage && (this.isCodeCoverageEnabled || this.isCoverageReportUploadEnabled)) {
|
|
1446
|
+
this.addTestSessionCoverage(coverage)
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1306
1449
|
if (coverage && this.isCodeCoverageEnabled && this.tracer._tracer._exporter?.exportCoverage) {
|
|
1307
|
-
const coverageFiles =
|
|
1308
|
-
const relativeCoverageFiles =
|
|
1309
|
-
|
|
1310
|
-
|
|
1450
|
+
const coverageFiles = getCoveredFilesFromCoverage(coverage)
|
|
1451
|
+
const relativeCoverageFiles = getRelativeCoverageFiles(coverageFiles, this.getCoverageRootDir())
|
|
1452
|
+
if (testSuiteAbsolutePath) {
|
|
1453
|
+
relativeCoverageFiles.push({
|
|
1454
|
+
filename: getTestSuitePath(testSuiteAbsolutePath, this.getCoverageRootDir()),
|
|
1455
|
+
})
|
|
1456
|
+
}
|
|
1311
1457
|
if (!relativeCoverageFiles.length) {
|
|
1312
1458
|
incrementCountMetric(TELEMETRY_CODE_COVERAGE_EMPTY)
|
|
1313
1459
|
}
|
|
@@ -1343,7 +1489,7 @@ class CypressPlugin {
|
|
|
1343
1489
|
this.testStatuses[testName] = [testStatus]
|
|
1344
1490
|
}
|
|
1345
1491
|
const testStatuses = this.testStatuses[testName]
|
|
1346
|
-
const activeSpanTags = this.activeTestSpan.context().
|
|
1492
|
+
const activeSpanTags = this.activeTestSpan.context().getTags()
|
|
1347
1493
|
|
|
1348
1494
|
if (error) {
|
|
1349
1495
|
this.activeTestSpan.setTag('error', error)
|
|
@@ -1444,6 +1590,31 @@ class CypressPlugin {
|
|
|
1444
1590
|
this.activeTestSpan.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
|
|
1445
1591
|
}
|
|
1446
1592
|
|
|
1593
|
+
if (Array.isArray(commands) && commands.length > 0) {
|
|
1594
|
+
for (const command of commands) {
|
|
1595
|
+
const { startTime, endTime } = command
|
|
1596
|
+
if (typeof startTime !== 'number' || typeof endTime !== 'number' || endTime < startTime) {
|
|
1597
|
+
continue
|
|
1598
|
+
}
|
|
1599
|
+
const stepSpan = this.tracer.startSpan('cypress.step', {
|
|
1600
|
+
childOf: this.activeTestSpan,
|
|
1601
|
+
startTime,
|
|
1602
|
+
tags: {
|
|
1603
|
+
[COMPONENT]: 'cypress',
|
|
1604
|
+
'cypress.command': command.name,
|
|
1605
|
+
[RESOURCE_NAME]: command.name,
|
|
1606
|
+
},
|
|
1607
|
+
})
|
|
1608
|
+
if (command.error) {
|
|
1609
|
+
const errorObj = new Error(command.error.message || String(command.error))
|
|
1610
|
+
if (command.error.name) errorObj.name = command.error.name
|
|
1611
|
+
if (command.error.stack) errorObj.stack = command.error.stack
|
|
1612
|
+
stepSpan.setTag('error', errorObj)
|
|
1613
|
+
}
|
|
1614
|
+
stepSpan.finish(endTime)
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1447
1618
|
const finishedTest = {
|
|
1448
1619
|
testName,
|
|
1449
1620
|
testStatus,
|
|
@@ -24,6 +24,40 @@ const suppressedTestFailures = new Map()
|
|
|
24
24
|
// to a cross-origin URL, safeGetRum() handles the access error.
|
|
25
25
|
let originalWindow
|
|
26
26
|
|
|
27
|
+
let currentTestCommands = []
|
|
28
|
+
const commandStartTimes = new Map()
|
|
29
|
+
const INTERNAL_CYPRESS_COMMANDS = new Set(['wrap', 'then', 'noop'])
|
|
30
|
+
|
|
31
|
+
Cypress.on('command:start', (command) => {
|
|
32
|
+
commandStartTimes.set(command.get('id'), { startTime: Date.now(), name: command.get('name') })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
Cypress.on('command:end', (command) => {
|
|
36
|
+
const id = command.get('id')
|
|
37
|
+
const entry = commandStartTimes.get(id)
|
|
38
|
+
commandStartTimes.delete(id)
|
|
39
|
+
|
|
40
|
+
const name = command.get('name')
|
|
41
|
+
const args = command.get('args')
|
|
42
|
+
if (name === 'task' && args && typeof args[0] === 'string' && args[0].startsWith('dd:')) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
if (INTERNAL_CYPRESS_COMMANDS.has(name)) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
if (entry == null) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const err = command.get('err')
|
|
52
|
+
currentTestCommands.push({
|
|
53
|
+
name,
|
|
54
|
+
startTime: entry.startTime,
|
|
55
|
+
endTime: Date.now(),
|
|
56
|
+
// Serialize the error to a plain object so it survives cy.task JSON transport.
|
|
57
|
+
error: err ? { message: err.message, stack: err.stack, name: err.name } : null,
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
27
61
|
// If the test is using multi domain with cy.origin, trying to access
|
|
28
62
|
// window properties will result in a cross origin error.
|
|
29
63
|
function safeGetRum (window) {
|
|
@@ -56,6 +90,29 @@ function getTestProperties (testName) {
|
|
|
56
90
|
// By not re-throwing the error, Cypress marks the test as passed
|
|
57
91
|
// This allows quarantined tests to run but not affect the exit code
|
|
58
92
|
Cypress.on('fail', (err, runnable) => {
|
|
93
|
+
// For commands that time out, command:end may never fire.
|
|
94
|
+
// Finalize any in-flight commands so their step spans carry the error.
|
|
95
|
+
const hadInFlightCommands = commandStartTimes.size > 0
|
|
96
|
+
for (const [, { startTime, name }] of commandStartTimes) {
|
|
97
|
+
if (INTERNAL_CYPRESS_COMMANDS.has(name)) continue
|
|
98
|
+
currentTestCommands.push({
|
|
99
|
+
name,
|
|
100
|
+
startTime,
|
|
101
|
+
endTime: Date.now(),
|
|
102
|
+
error: { message: err.message, stack: err.stack, name: err.name },
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
commandStartTimes.clear()
|
|
106
|
+
|
|
107
|
+
// If command:end fired for all commands (none in-flight) but the last command
|
|
108
|
+
// has no error, it means command:end fired before the error was attached to it.
|
|
109
|
+
if (!hadInFlightCommands && currentTestCommands.length > 0) {
|
|
110
|
+
const lastCommand = currentTestCommands[currentTestCommands.length - 1]
|
|
111
|
+
if (!lastCommand.error) {
|
|
112
|
+
lastCommand.error = { message: err.message, stack: err.stack, name: err.name }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
59
116
|
if (!isTestManagementEnabled) {
|
|
60
117
|
throw err
|
|
61
118
|
}
|
|
@@ -169,6 +226,9 @@ beforeEach(function () {
|
|
|
169
226
|
retryReasonsByTestName.delete(testName)
|
|
170
227
|
}
|
|
171
228
|
|
|
229
|
+
currentTestCommands = []
|
|
230
|
+
commandStartTimes.clear()
|
|
231
|
+
|
|
172
232
|
cy.on('window:load', (win) => {
|
|
173
233
|
originalWindow = win
|
|
174
234
|
})
|
|
@@ -212,6 +272,11 @@ beforeEach(function () {
|
|
|
212
272
|
if (shouldSkip) {
|
|
213
273
|
this.skip()
|
|
214
274
|
}
|
|
275
|
+
}).then(() => {
|
|
276
|
+
// Clear any commands accumulated during DD-owned setup (e.g. setCookie, RUM restart)
|
|
277
|
+
// so they are not reported as user test steps.
|
|
278
|
+
currentTestCommands = []
|
|
279
|
+
commandStartTimes.clear()
|
|
215
280
|
})
|
|
216
281
|
})
|
|
217
282
|
|
|
@@ -289,6 +354,9 @@ afterEach(function () {
|
|
|
289
354
|
testInfo.testSourceStack = invocationDetails.stack
|
|
290
355
|
} catch {}
|
|
291
356
|
|
|
357
|
+
// Snapshot before any DD-owned Cypress commands so they are not reported as test steps.
|
|
358
|
+
const commandsToReport = [...currentTestCommands]
|
|
359
|
+
|
|
292
360
|
const rum = safeGetRum(originalWindow)
|
|
293
361
|
if (rum) {
|
|
294
362
|
testInfo.isRUMActive = true
|
|
@@ -310,5 +378,5 @@ afterEach(function () {
|
|
|
310
378
|
suppressedTestFailures.delete(testName)
|
|
311
379
|
}
|
|
312
380
|
|
|
313
|
-
cy.task('dd:afterEach', { test: testInfo, coverage })
|
|
381
|
+
cy.task('dd:afterEach', { test: testInfo, coverage, commands: commandsToReport })
|
|
314
382
|
})
|
|
@@ -23,22 +23,24 @@ class DNSLookupPlugin extends ClientPlugin {
|
|
|
23
23
|
return ctx.currentStore
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
finish (ctx) {
|
|
27
27
|
const span = ctx.currentStore.span
|
|
28
28
|
const result = ctx.result
|
|
29
29
|
|
|
30
30
|
if (Array.isArray(result)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
: [result]
|
|
34
|
-
|
|
31
|
+
// `lookup(..., { all: true })` or `dns.promises.lookup(..., { all: true })`.
|
|
32
|
+
const addresses = result.map(entry => entry.address).sort()
|
|
35
33
|
span.setTag('dns.address', addresses[0])
|
|
36
34
|
span.setTag('dns.addresses', addresses.join(','))
|
|
35
|
+
} else if (result && typeof result === 'object') {
|
|
36
|
+
// `dns.promises.lookup(...)` resolves to `{ address, family }`; the callback variant
|
|
37
|
+
// passes the address as a string.
|
|
38
|
+
span.setTag('dns.address', result.address)
|
|
37
39
|
} else {
|
|
38
40
|
span.setTag('dns.address', result)
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
super.finish(ctx)
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -181,7 +181,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
181
181
|
|
|
182
182
|
if (linkContext) {
|
|
183
183
|
if (span.addLink) {
|
|
184
|
-
span.addLink(linkContext, {})
|
|
184
|
+
span.addLink({ context: linkContext, attributes: {} })
|
|
185
185
|
} else {
|
|
186
186
|
span._links ??= []
|
|
187
187
|
span._links.push({ context: linkContext, attributes: {} })
|
|
@@ -17,6 +17,8 @@ class GraphQLExecutePlugin extends TracingPlugin {
|
|
|
17
17
|
const document = args.document
|
|
18
18
|
const source = this.config.source && document && docSource
|
|
19
19
|
|
|
20
|
+
ctx.collapse = this.config.collapse
|
|
21
|
+
|
|
20
22
|
const span = this.startSpan(this.operationName(), {
|
|
21
23
|
service: this.config.service || this.serviceName(),
|
|
22
24
|
resource: getSignature(document, name, type, this.config.signature),
|