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.
- package/CONTRIBUTING.md +98 -0
- package/README.md +8 -99
- package/ci/cypress/after-run.js +1 -0
- package/ci/cypress/after-spec.js +1 -0
- package/index.d.ts +1499 -1486
- package/package.json +3 -3
- package/packages/datadog-core/src/utils/src/get.js +11 -0
- package/packages/datadog-core/src/utils/src/has.js +14 -0
- package/packages/datadog-core/src/utils/src/set.js +16 -0
- package/packages/datadog-instrumentations/src/amqplib.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +157 -42
- package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +80 -40
- package/packages/datadog-instrumentations/src/mocha.js +4 -1
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
- package/packages/datadog-instrumentations/src/playwright.js +78 -16
- package/packages/datadog-plugin-amqplib/src/consumer.js +8 -4
- package/packages/datadog-plugin-amqplib/src/producer.js +3 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +60 -57
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +42 -22
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +64 -30
- package/packages/datadog-plugin-cucumber/src/index.js +25 -9
- package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
- package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
- package/packages/datadog-plugin-cypress/src/plugin.js +6 -549
- package/packages/datadog-plugin-cypress/src/support.js +50 -3
- package/packages/datadog-plugin-graphql/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +10 -8
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +11 -2
- package/packages/datadog-plugin-kafkajs/src/consumer.js +4 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -5
- package/packages/datadog-plugin-playwright/src/index.js +34 -3
- package/packages/datadog-plugin-rhea/src/consumer.js +8 -3
- package/packages/datadog-plugin-rhea/src/producer.js +3 -4
- package/packages/dd-trace/src/appsec/iast/index.js +10 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +18 -5
- package/packages/dd-trace/src/appsec/recommended.json +67 -27
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -3
- package/packages/dd-trace/src/config.js +451 -459
- package/packages/dd-trace/src/data_streams_context.js +1 -1
- package/packages/dd-trace/src/datastreams/pathway.js +58 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -5
- package/packages/dd-trace/src/format.js +0 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/opentracing/span.js +4 -4
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +77 -32
- package/packages/dd-trace/src/telemetry/index.js +22 -34
- package/packages/dd-trace/src/tracer.js +3 -3
- package/register.js +4 -0
- /package/packages/{utils → datadog-core/src/utils}/src/kebabcase.js +0 -0
- /package/packages/{utils → datadog-core/src/utils}/src/pick.js +0 -0
- /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
|
|
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
|
-
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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]
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
133
|
+
if (Object.keys(argList).length) {
|
|
134
|
+
resolverVars[directive.name.value] = argList
|
|
135
|
+
}
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
|