dd-trace 5.18.0 → 5.19.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 +0 -2
- package/index.d.ts +61 -39
- package/package.json +7 -11
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +1 -1
- package/packages/datadog-instrumentations/src/hapi.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +17 -2
- package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +46 -32
- package/packages/datadog-instrumentations/src/restify.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +51 -5
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +79 -42
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +7 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/index.js +5 -5
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +17 -6
- package/packages/dd-trace/src/analytics_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
- package/packages/dd-trace/src/appsec/index.js +3 -3
- package/packages/dd-trace/src/appsec/passport.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +0 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/config.js +27 -24
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -46,7 +46,6 @@ dev,eslint-plugin-import,MIT,Copyright 2015 Ben Mosher
|
|
|
46
46
|
dev,eslint-plugin-mocha,MIT,Copyright 2014 Mathias Schreck
|
|
47
47
|
dev,eslint-plugin-n,MIT,Copyright 2015 Toru Nagashima
|
|
48
48
|
dev,eslint-plugin-promise,ISC,jden and other contributors
|
|
49
|
-
dev,eslint-plugin-standard,MIT,Copyright 2015 Jamund Ferguson
|
|
50
49
|
dev,express,MIT,Copyright 2009-2014 TJ Holowaychuk 2013-2014 Roman Shtylman 2014-2015 Douglas Christopher Wilson
|
|
51
50
|
dev,get-port,MIT,Copyright Sindre Sorhus
|
|
52
51
|
dev,glob,ISC,Copyright Isaac Z. Schlueter and Contributors
|
|
@@ -63,7 +62,6 @@ dev,rimraf,ISC,Copyright Isaac Z. Schlueter and Contributors
|
|
|
63
62
|
dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen
|
|
64
63
|
dev,sinon-chai,WTFPL and BSD-2-Clause,Copyright 2004 Sam Hocevar 2012–2017 Domenic Denicola
|
|
65
64
|
dev,tap,ISC,Copyright 2011-2022 Isaac Z. Schlueter and Contributors
|
|
66
|
-
dev,tape,MIT,Copyright James Halliday
|
|
67
65
|
dev,tiktoken,MIT,Copyright (c) 2022 OpenAI, Shantanu Jain
|
|
68
66
|
file,aws-lambda-nodejs-runtime-interface-client,Apache 2.0,Copyright 2019 Amazon.com Inc. or its affiliates. All Rights Reserved.
|
|
69
67
|
file,profile.proto,Apache license 2.0,Copyright 2016 Google Inc.
|
package/index.d.ts
CHANGED
|
@@ -518,45 +518,7 @@ declare namespace tracer {
|
|
|
518
518
|
/**
|
|
519
519
|
* Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
|
|
520
520
|
*/
|
|
521
|
-
iast?: boolean |
|
|
522
|
-
/**
|
|
523
|
-
* Whether to enable IAST.
|
|
524
|
-
* @default false
|
|
525
|
-
*/
|
|
526
|
-
enabled?: boolean,
|
|
527
|
-
/**
|
|
528
|
-
* Controls the percentage of requests that iast will analyze
|
|
529
|
-
* @default 30
|
|
530
|
-
*/
|
|
531
|
-
requestSampling?: number,
|
|
532
|
-
/**
|
|
533
|
-
* Controls how many request can be analyzing code vulnerabilities at the same time
|
|
534
|
-
* @default 2
|
|
535
|
-
*/
|
|
536
|
-
maxConcurrentRequests?: number,
|
|
537
|
-
/**
|
|
538
|
-
* Controls how many code vulnerabilities can be detected in the same request
|
|
539
|
-
* @default 2
|
|
540
|
-
*/
|
|
541
|
-
maxContextOperations?: number,
|
|
542
|
-
/**
|
|
543
|
-
* Whether to enable vulnerability deduplication
|
|
544
|
-
*/
|
|
545
|
-
deduplicationEnabled?: boolean,
|
|
546
|
-
/**
|
|
547
|
-
* Whether to enable vulnerability redaction
|
|
548
|
-
* @default true
|
|
549
|
-
*/
|
|
550
|
-
redactionEnabled?: boolean,
|
|
551
|
-
/**
|
|
552
|
-
* Specifies a regex that will redact sensitive source names in vulnerability reports.
|
|
553
|
-
*/
|
|
554
|
-
redactionNamePattern?: string,
|
|
555
|
-
/**
|
|
556
|
-
* Specifies a regex that will redact sensitive source values in vulnerability reports.
|
|
557
|
-
*/
|
|
558
|
-
redactionValuePattern?: string
|
|
559
|
-
}
|
|
521
|
+
iast?: boolean | IastOptions
|
|
560
522
|
|
|
561
523
|
appsec?: {
|
|
562
524
|
/**
|
|
@@ -736,6 +698,11 @@ declare namespace tracer {
|
|
|
736
698
|
}
|
|
737
699
|
};
|
|
738
700
|
|
|
701
|
+
/**
|
|
702
|
+
* Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
|
|
703
|
+
*/
|
|
704
|
+
iast?: boolean | IastOptions
|
|
705
|
+
|
|
739
706
|
/**
|
|
740
707
|
* Configuration of ASM Remote Configuration
|
|
741
708
|
*/
|
|
@@ -2137,6 +2104,61 @@ declare namespace tracer {
|
|
|
2137
2104
|
export type TimeInput = otel.TimeInput;
|
|
2138
2105
|
export type TraceState = otel.TraceState;
|
|
2139
2106
|
}
|
|
2107
|
+
|
|
2108
|
+
/**
|
|
2109
|
+
* Iast configuration used in `tracer` and `tracer.experimental` options
|
|
2110
|
+
*/
|
|
2111
|
+
interface IastOptions {
|
|
2112
|
+
/**
|
|
2113
|
+
* Whether to enable IAST.
|
|
2114
|
+
* @default false
|
|
2115
|
+
*/
|
|
2116
|
+
enabled?: boolean,
|
|
2117
|
+
|
|
2118
|
+
/**
|
|
2119
|
+
* Controls the percentage of requests that iast will analyze
|
|
2120
|
+
* @default 30
|
|
2121
|
+
*/
|
|
2122
|
+
requestSampling?: number,
|
|
2123
|
+
|
|
2124
|
+
/**
|
|
2125
|
+
* Controls how many request can be analyzing code vulnerabilities at the same time
|
|
2126
|
+
* @default 2
|
|
2127
|
+
*/
|
|
2128
|
+
maxConcurrentRequests?: number,
|
|
2129
|
+
|
|
2130
|
+
/**
|
|
2131
|
+
* Controls how many code vulnerabilities can be detected in the same request
|
|
2132
|
+
* @default 2
|
|
2133
|
+
*/
|
|
2134
|
+
maxContextOperations?: number,
|
|
2135
|
+
|
|
2136
|
+
/**
|
|
2137
|
+
* Whether to enable vulnerability deduplication
|
|
2138
|
+
*/
|
|
2139
|
+
deduplicationEnabled?: boolean,
|
|
2140
|
+
|
|
2141
|
+
/**
|
|
2142
|
+
* Whether to enable vulnerability redaction
|
|
2143
|
+
* @default true
|
|
2144
|
+
*/
|
|
2145
|
+
redactionEnabled?: boolean,
|
|
2146
|
+
|
|
2147
|
+
/**
|
|
2148
|
+
* Specifies a regex that will redact sensitive source names in vulnerability reports.
|
|
2149
|
+
*/
|
|
2150
|
+
redactionNamePattern?: string,
|
|
2151
|
+
|
|
2152
|
+
/**
|
|
2153
|
+
* Specifies a regex that will redact sensitive source values in vulnerability reports.
|
|
2154
|
+
*/
|
|
2155
|
+
redactionValuePattern?: string,
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* Specifies the verbosity of the sent telemetry. Default 'INFORMATION'
|
|
2159
|
+
*/
|
|
2160
|
+
telemetryVerbosity?: string
|
|
2161
|
+
}
|
|
2140
2162
|
}
|
|
2141
2163
|
|
|
2142
2164
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.19.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -45,9 +45,7 @@
|
|
|
45
45
|
"test:integration:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
|
|
46
46
|
"test:unit:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
|
|
47
47
|
"test:shimmer": "mocha 'packages/datadog-shimmer/test/**/*.spec.js'",
|
|
48
|
-
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer"
|
|
49
|
-
"leak:core": "node ./scripts/install_plugin_modules && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape 'packages/dd-trace/test/leak/**/*.js'",
|
|
50
|
-
"leak:plugins": "yarn services && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape \"packages/datadog-plugin-@($(echo $PLUGINS))/test/leak.js\""
|
|
48
|
+
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer"
|
|
51
49
|
},
|
|
52
50
|
"repository": {
|
|
53
51
|
"type": "git",
|
|
@@ -116,13 +114,12 @@
|
|
|
116
114
|
"cli-table3": "^0.6.3",
|
|
117
115
|
"dotenv": "16.3.1",
|
|
118
116
|
"esbuild": "0.16.12",
|
|
119
|
-
"eslint": "^8.
|
|
117
|
+
"eslint": "^8.57.0",
|
|
120
118
|
"eslint-config-standard": "^17.1.0",
|
|
121
|
-
"eslint-plugin-import": "^2.
|
|
122
|
-
"eslint-plugin-mocha": "^10.
|
|
123
|
-
"eslint-plugin-n": "^
|
|
124
|
-
"eslint-plugin-promise": "^
|
|
125
|
-
"eslint-plugin-standard": "^3.0.1",
|
|
119
|
+
"eslint-plugin-import": "^2.29.1",
|
|
120
|
+
"eslint-plugin-mocha": "^10.4.3",
|
|
121
|
+
"eslint-plugin-n": "^16.6.2",
|
|
122
|
+
"eslint-plugin-promise": "^6.4.0",
|
|
126
123
|
"express": "^4.18.2",
|
|
127
124
|
"get-port": "^3.2.0",
|
|
128
125
|
"glob": "^7.1.6",
|
|
@@ -139,7 +136,6 @@
|
|
|
139
136
|
"sinon": "^15.2.0",
|
|
140
137
|
"sinon-chai": "^3.7.0",
|
|
141
138
|
"tap": "^16.3.7",
|
|
142
|
-
"tape": "^5.6.5",
|
|
143
139
|
"tiktoken": "^1.0.15"
|
|
144
140
|
}
|
|
145
141
|
}
|
|
@@ -39,10 +39,10 @@ function normalizeArgs (args, shell) {
|
|
|
39
39
|
|
|
40
40
|
if (Array.isArray(args[1])) {
|
|
41
41
|
childProcessInfo.command = childProcessInfo.command + ' ' + args[1].join(' ')
|
|
42
|
-
if (args[2]
|
|
42
|
+
if (args[2] !== null && typeof args[2] === 'object') {
|
|
43
43
|
childProcessInfo.options = args[2]
|
|
44
44
|
}
|
|
45
|
-
} else if (args[1]
|
|
45
|
+
} else if (args[1] !== null && typeof args[1] === 'object') {
|
|
46
46
|
childProcessInfo.options = args[1]
|
|
47
47
|
}
|
|
48
48
|
childProcessInfo.shell = shell ||
|
|
@@ -272,7 +272,7 @@ function createWrapFunction (prefix = '', override = '') {
|
|
|
272
272
|
const outerResource = new AsyncResource('bound-anonymous-fn')
|
|
273
273
|
|
|
274
274
|
arguments[lastIndex] = innerResource.bind(function (e) {
|
|
275
|
-
if (typeof e === 'object') { // fs.exists receives a boolean
|
|
275
|
+
if (e !== null && typeof e === 'object') { // fs.exists receives a boolean
|
|
276
276
|
errorChannel.publish(e)
|
|
277
277
|
}
|
|
278
278
|
|
|
@@ -38,7 +38,7 @@ function wrapStart (start) {
|
|
|
38
38
|
|
|
39
39
|
function wrapExt (ext) {
|
|
40
40
|
return function (events, method, options) {
|
|
41
|
-
if (typeof events === 'object') {
|
|
41
|
+
if (events !== null && typeof events === 'object') {
|
|
42
42
|
arguments[0] = wrapEvents(events)
|
|
43
43
|
} else {
|
|
44
44
|
arguments[1] = wrapExtension(method)
|
|
@@ -132,7 +132,7 @@ function patch (http, methodName) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function combineOptions (inputURL, inputOptions) {
|
|
135
|
-
if (typeof inputOptions === 'object') {
|
|
135
|
+
if (inputOptions !== null && typeof inputOptions === 'object') {
|
|
136
136
|
return Object.assign(inputURL || {}, inputOptions)
|
|
137
137
|
} else {
|
|
138
138
|
return inputURL
|
|
@@ -12,7 +12,8 @@ const {
|
|
|
12
12
|
getTestParametersString,
|
|
13
13
|
addEfdStringToTestName,
|
|
14
14
|
removeEfdStringFromTestName,
|
|
15
|
-
getIsFaultyEarlyFlakeDetection
|
|
15
|
+
getIsFaultyEarlyFlakeDetection,
|
|
16
|
+
NUM_FAILED_TEST_RETRIES
|
|
16
17
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
18
|
const {
|
|
18
19
|
getFormattedJestTestParameters,
|
|
@@ -49,6 +50,9 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
|
|
|
49
50
|
const CHILD_MESSAGE_CALL = 1
|
|
50
51
|
// Maximum time we'll wait for the tracer to flush
|
|
51
52
|
const FLUSH_TIMEOUT = 10000
|
|
53
|
+
// eslint-disable-next-line
|
|
54
|
+
// https://github.com/jestjs/jest/blob/41f842a46bb2691f828c3a5f27fc1d6290495b82/packages/jest-circus/src/types.ts#L9C8-L9C54
|
|
55
|
+
const RETRY_TIMES = Symbol.for('RETRY_TIMES')
|
|
52
56
|
|
|
53
57
|
let skippableSuites = []
|
|
54
58
|
let knownTests = {}
|
|
@@ -127,6 +131,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
|
|
134
|
+
this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled
|
|
130
135
|
|
|
131
136
|
if (this.isEarlyFlakeDetectionEnabled) {
|
|
132
137
|
const hasKnownTests = !!knownTests.jest
|
|
@@ -140,6 +145,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
140
145
|
this.isEarlyFlakeDetectionEnabled = false
|
|
141
146
|
}
|
|
142
147
|
}
|
|
148
|
+
|
|
149
|
+
if (this.isFlakyTestRetriesEnabled) {
|
|
150
|
+
const currentNumRetries = this.global[RETRY_TIMES]
|
|
151
|
+
if (!currentNumRetries) {
|
|
152
|
+
this.global[RETRY_TIMES] = NUM_FAILED_TEST_RETRIES
|
|
153
|
+
}
|
|
154
|
+
}
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
getHasSnapshotTests () {
|
|
@@ -218,6 +230,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
218
230
|
retriedTestsToNumAttempts.set(originalTestName, numEfdRetry + 1)
|
|
219
231
|
}
|
|
220
232
|
}
|
|
233
|
+
const isJestRetry = event.test?.invocations > 1
|
|
221
234
|
asyncResource.runInAsyncScope(() => {
|
|
222
235
|
testStartCh.publish({
|
|
223
236
|
name: removeEfdStringFromTestName(testName),
|
|
@@ -228,7 +241,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
228
241
|
testParameters,
|
|
229
242
|
frameworkVersion: jestVersion,
|
|
230
243
|
isNew: isNewTest,
|
|
231
|
-
isEfdRetry: numEfdRetry > 0
|
|
244
|
+
isEfdRetry: numEfdRetry > 0,
|
|
245
|
+
isJestRetry
|
|
232
246
|
})
|
|
233
247
|
originalTestFns.set(event.test, event.test.fn)
|
|
234
248
|
event.test.fn = asyncResource.bind(event.test.fn)
|
|
@@ -758,6 +772,7 @@ addHook({
|
|
|
758
772
|
_ddIsEarlyFlakeDetectionEnabled,
|
|
759
773
|
_ddEarlyFlakeDetectionNumRetries,
|
|
760
774
|
_ddRepositoryRoot,
|
|
775
|
+
_ddIsFlakyTestRetriesEnabled,
|
|
761
776
|
...restOfTestEnvironmentOptions
|
|
762
777
|
} = testEnvironmentOptions
|
|
763
778
|
|
|
@@ -59,7 +59,7 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
|
|
|
59
59
|
try {
|
|
60
60
|
const { topic, messages = [] } = arguments[0]
|
|
61
61
|
for (const message of messages) {
|
|
62
|
-
if (typeof message === 'object') {
|
|
62
|
+
if (message !== null && typeof message === 'object') {
|
|
63
63
|
message.headers = message.headers || {}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -61,7 +61,7 @@ addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
|
|
|
61
61
|
let filter
|
|
62
62
|
if (isString(options)) {
|
|
63
63
|
filter = options
|
|
64
|
-
} else if (typeof options === 'object' && options.filter) {
|
|
64
|
+
} else if (options !== null && typeof options === 'object' && options.filter) {
|
|
65
65
|
if (isString(options.filter)) {
|
|
66
66
|
filter = options.filter
|
|
67
67
|
}
|
|
@@ -78,7 +78,7 @@ addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
|
|
|
78
78
|
const callback = arguments[callbackIndex]
|
|
79
79
|
// eslint-disable-next-line n/handle-callback-err
|
|
80
80
|
arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) {
|
|
81
|
-
if (typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
|
|
81
|
+
if (corkedEmitter !== null && typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
|
|
82
82
|
wrapEmitter(corkedEmitter)
|
|
83
83
|
}
|
|
84
84
|
callback.apply(this, arguments)
|
|
@@ -25,9 +25,9 @@ const methodsOptionalArgs = ['findOneAndUpdate']
|
|
|
25
25
|
function getFilters (args, methodName) {
|
|
26
26
|
const [arg0, arg1] = args
|
|
27
27
|
|
|
28
|
-
const filters = arg0 && typeof arg0 === 'object' ? [arg0] : []
|
|
28
|
+
const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : []
|
|
29
29
|
|
|
30
|
-
if (arg1 && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
|
|
30
|
+
if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
|
|
31
31
|
filters.push(arg1)
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -46,7 +46,7 @@ function wrapHandleApiRequest (handleApiRequest) {
|
|
|
46
46
|
function wrapHandleApiRequestWithMatch (handleApiRequest) {
|
|
47
47
|
return function (req, res, query, match) {
|
|
48
48
|
return instrument(req, res, () => {
|
|
49
|
-
const page = (typeof match === 'object' && typeof match.definition === 'object')
|
|
49
|
+
const page = (match !== null && typeof match === 'object' && typeof match.definition === 'object')
|
|
50
50
|
? match.definition.pathname
|
|
51
51
|
: undefined
|
|
52
52
|
|
|
@@ -35,7 +35,7 @@ function wrapQuery (query) {
|
|
|
35
35
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
36
36
|
const processId = this.processID
|
|
37
37
|
|
|
38
|
-
const pgQuery = arguments[0] && typeof arguments[0] === 'object'
|
|
38
|
+
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object'
|
|
39
39
|
? arguments[0]
|
|
40
40
|
: { text: arguments[0] }
|
|
41
41
|
|
|
@@ -109,7 +109,7 @@ function wrapPoolQuery (query) {
|
|
|
109
109
|
|
|
110
110
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
111
111
|
|
|
112
|
-
const pgQuery = arguments[0] && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }
|
|
112
|
+
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }
|
|
113
113
|
|
|
114
114
|
return asyncResource.runInAsyncScope(() => {
|
|
115
115
|
startPoolQueryCh.publish({
|
|
@@ -2,7 +2,7 @@ const semver = require('semver')
|
|
|
2
2
|
|
|
3
3
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
|
-
const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
|
|
5
|
+
const { parseAnnotations, getTestSuitePath, NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
|
|
6
6
|
const log = require('../../dd-trace/src/log')
|
|
7
7
|
|
|
8
8
|
const testStartCh = channel('ci:playwright:test:start')
|
|
@@ -21,6 +21,7 @@ const testToAr = new WeakMap()
|
|
|
21
21
|
const testSuiteToAr = new Map()
|
|
22
22
|
const testSuiteToTestStatuses = new Map()
|
|
23
23
|
const testSuiteToErrors = new Map()
|
|
24
|
+
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
24
25
|
|
|
25
26
|
let applyRepeatEachIndex = null
|
|
26
27
|
|
|
@@ -35,6 +36,7 @@ const STATUS_TO_TEST_STATUS = {
|
|
|
35
36
|
|
|
36
37
|
let remainingTestsByFile = {}
|
|
37
38
|
let isEarlyFlakeDetectionEnabled = false
|
|
39
|
+
let isFlakyTestRetriesEnabled = false
|
|
38
40
|
let earlyFlakeDetectionNumRetries = 0
|
|
39
41
|
let knownTests = {}
|
|
40
42
|
let rootDir = ''
|
|
@@ -196,6 +198,31 @@ function getTestSuiteError (testSuiteAbsolutePath) {
|
|
|
196
198
|
return new Error(`${errors.length} errors in this test suite:\n${errors.map(e => e.message).join('\n------\n')}`)
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
function getTestByTestId (dispatcher, testId) {
|
|
202
|
+
if (dispatcher._testById) {
|
|
203
|
+
return dispatcher._testById.get(testId)?.test
|
|
204
|
+
}
|
|
205
|
+
const allTests = dispatcher._allTests || dispatcher._ddAllTests
|
|
206
|
+
if (allTests) {
|
|
207
|
+
return allTests.find(({ id }) => id === testId)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getChannelPromise (channelToPublishTo) {
|
|
212
|
+
return new Promise(resolve => {
|
|
213
|
+
testSessionAsyncResource.runInAsyncScope(() => {
|
|
214
|
+
channelToPublishTo.publish({ onDone: resolve })
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
// eslint-disable-next-line
|
|
219
|
+
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
|
|
220
|
+
// We can't use test.outcome() directly because it's set on follow up handlers:
|
|
221
|
+
// our `testEndHandler` is called before the outcome is set.
|
|
222
|
+
function testWillRetry (test, testStatus) {
|
|
223
|
+
return testStatus === 'fail' && test.results.length <= test.retries
|
|
224
|
+
}
|
|
225
|
+
|
|
199
226
|
function testBeginHandler (test, browserName) {
|
|
200
227
|
const {
|
|
201
228
|
_requireFile: testSuiteAbsolutePath,
|
|
@@ -250,6 +277,7 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
250
277
|
testFinishCh.publish({
|
|
251
278
|
testStatus,
|
|
252
279
|
steps: testResult?.steps || [],
|
|
280
|
+
isRetry: testResult?.retry > 0,
|
|
253
281
|
error,
|
|
254
282
|
extraTags: annotationTags,
|
|
255
283
|
isNew: test._ddIsNew,
|
|
@@ -267,8 +295,10 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
267
295
|
addErrorToTestSuite(testSuiteAbsolutePath, error)
|
|
268
296
|
}
|
|
269
297
|
|
|
270
|
-
|
|
271
|
-
|
|
298
|
+
if (!testWillRetry(test, testStatus)) {
|
|
299
|
+
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
300
|
+
.filter(currentTest => currentTest !== test)
|
|
301
|
+
}
|
|
272
302
|
|
|
273
303
|
// Last test, we finish the suite
|
|
274
304
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
@@ -335,16 +365,6 @@ function dispatcherHook (dispatcherExport) {
|
|
|
335
365
|
return dispatcherExport
|
|
336
366
|
}
|
|
337
367
|
|
|
338
|
-
function getTestByTestId (dispatcher, testId) {
|
|
339
|
-
if (dispatcher._testById) {
|
|
340
|
-
return dispatcher._testById.get(testId)?.test
|
|
341
|
-
}
|
|
342
|
-
const allTests = dispatcher._allTests || dispatcher._ddAllTests
|
|
343
|
-
if (allTests) {
|
|
344
|
-
return allTests.find(({ id }) => id === testId)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
368
|
function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
349
369
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)
|
|
350
370
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
|
|
@@ -373,8 +393,6 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
373
393
|
shimmer.wrap(runnerExport.Runner.prototype, 'runAllTests', runAllTests => async function () {
|
|
374
394
|
let onDone
|
|
375
395
|
|
|
376
|
-
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
377
|
-
|
|
378
396
|
rootDir = getRootDir(this)
|
|
379
397
|
|
|
380
398
|
const processArgv = process.argv.slice(2).join(' ')
|
|
@@ -383,46 +401,42 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
383
401
|
testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
|
|
384
402
|
})
|
|
385
403
|
|
|
386
|
-
const configurationPromise = new Promise((resolve) => {
|
|
387
|
-
onDone = resolve
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
testSessionAsyncResource.runInAsyncScope(() => {
|
|
391
|
-
libraryConfigurationCh.publish({ onDone })
|
|
392
|
-
})
|
|
393
|
-
|
|
394
404
|
try {
|
|
395
|
-
const { err, libraryConfig } = await
|
|
405
|
+
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
|
|
396
406
|
if (!err) {
|
|
397
407
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
398
408
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
409
|
+
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
399
410
|
}
|
|
400
411
|
} catch (e) {
|
|
412
|
+
isEarlyFlakeDetectionEnabled = false
|
|
401
413
|
log.error(e)
|
|
402
414
|
}
|
|
403
415
|
|
|
404
416
|
if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
|
|
405
|
-
const knownTestsPromise = new Promise((resolve) => {
|
|
406
|
-
onDone = resolve
|
|
407
|
-
})
|
|
408
|
-
testSessionAsyncResource.runInAsyncScope(() => {
|
|
409
|
-
knownTestsCh.publish({ onDone })
|
|
410
|
-
})
|
|
411
|
-
|
|
412
417
|
try {
|
|
413
|
-
const { err, knownTests: receivedKnownTests } = await
|
|
418
|
+
const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh)
|
|
414
419
|
if (!err) {
|
|
415
420
|
knownTests = receivedKnownTests
|
|
416
421
|
} else {
|
|
417
422
|
isEarlyFlakeDetectionEnabled = false
|
|
418
423
|
}
|
|
419
424
|
} catch (err) {
|
|
425
|
+
isEarlyFlakeDetectionEnabled = false
|
|
420
426
|
log.error(err)
|
|
421
427
|
}
|
|
422
428
|
}
|
|
423
429
|
|
|
424
430
|
const projects = getProjectsFromRunner(this)
|
|
425
431
|
|
|
432
|
+
if (isFlakyTestRetriesEnabled) {
|
|
433
|
+
projects.forEach(project => {
|
|
434
|
+
if (project.retries === 0) { // Only if it hasn't been set by the user
|
|
435
|
+
project.retries = NUM_FAILED_TEST_RETRIES
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
|
|
426
440
|
const runAllTestsReturn = await runAllTests.apply(this, arguments)
|
|
427
441
|
|
|
428
442
|
Object.values(remainingTestsByFile).forEach(tests => {
|
|
@@ -51,7 +51,7 @@ function wrapFn (fn) {
|
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
53
|
const result = fn.apply(this, arguments)
|
|
54
|
-
if (result && typeof result === 'object' && typeof result.then === 'function') {
|
|
54
|
+
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
|
|
55
55
|
return result.then(function () {
|
|
56
56
|
nextChannel.publish({ req })
|
|
57
57
|
finishChannel.publish({ req })
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
2
2
|
const shimmer = require('../../datadog-shimmer')
|
|
3
|
+
const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
|
|
3
4
|
|
|
4
5
|
// test hooks
|
|
5
6
|
const testStartCh = channel('ci:vitest:test:start')
|
|
@@ -16,6 +17,7 @@ const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
|
|
|
16
17
|
// test session hooks
|
|
17
18
|
const testSessionStartCh = channel('ci:vitest:session:start')
|
|
18
19
|
const testSessionFinishCh = channel('ci:vitest:session:finish')
|
|
20
|
+
const libraryConfigurationCh = channel('ci:vitest:library-configuration')
|
|
19
21
|
|
|
20
22
|
const taskToAsync = new WeakMap()
|
|
21
23
|
|
|
@@ -30,6 +32,14 @@ function isReporterPackageNew (vitestPackage) {
|
|
|
30
32
|
return vitestPackage.e?.name === 'BaseSequencer'
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function getChannelPromise (channelToPublishTo) {
|
|
36
|
+
return new Promise(resolve => {
|
|
37
|
+
sessionAsyncResource.runInAsyncScope(() => {
|
|
38
|
+
channelToPublishTo.publish({ onDone: resolve })
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
function getSessionStatus (state) {
|
|
34
44
|
if (state.getCountOfFailedTests() > 0) {
|
|
35
45
|
return 'fail'
|
|
@@ -90,6 +100,23 @@ function getSortWrapper (sort) {
|
|
|
90
100
|
if (!testSessionFinishCh.hasSubscribers) {
|
|
91
101
|
return sort.apply(this, arguments)
|
|
92
102
|
}
|
|
103
|
+
// There isn't any other async function that we seem to be able to hook into
|
|
104
|
+
// So we will use the sort from BaseSequencer. This means that a custom sequencer
|
|
105
|
+
// will not work. This will be a known limitation.
|
|
106
|
+
let isFlakyTestRetriesEnabled = false
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
|
|
110
|
+
if (!err) {
|
|
111
|
+
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
isFlakyTestRetriesEnabled = false
|
|
115
|
+
}
|
|
116
|
+
if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) {
|
|
117
|
+
this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
shimmer.wrap(this.ctx, 'exit', exit => async function () {
|
|
94
121
|
let onFinish
|
|
95
122
|
|
|
@@ -126,15 +153,31 @@ addHook({
|
|
|
126
153
|
}, (vitestPackage) => {
|
|
127
154
|
const { VitestTestRunner } = vitestPackage
|
|
128
155
|
// test start (only tests that are not marked as skip or todo)
|
|
129
|
-
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task) {
|
|
156
|
+
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
|
|
130
157
|
if (!testStartCh.hasSubscribers) {
|
|
131
158
|
return onBeforeTryTask.apply(this, arguments)
|
|
132
159
|
}
|
|
160
|
+
const { retry: numAttempt } = retryInfo
|
|
161
|
+
// We finish the previous test here because we know it has failed already
|
|
162
|
+
if (numAttempt > 0) {
|
|
163
|
+
const asyncResource = taskToAsync.get(task)
|
|
164
|
+
const testError = task.result?.errors?.[0]
|
|
165
|
+
if (asyncResource) {
|
|
166
|
+
asyncResource.runInAsyncScope(() => {
|
|
167
|
+
testErrorCh.publish({ error: testError })
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
133
172
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
134
173
|
taskToAsync.set(task, asyncResource)
|
|
135
174
|
|
|
136
175
|
asyncResource.runInAsyncScope(() => {
|
|
137
|
-
testStartCh.publish({
|
|
176
|
+
testStartCh.publish({
|
|
177
|
+
testName: getTestName(task),
|
|
178
|
+
testSuiteAbsolutePath: task.file.filepath,
|
|
179
|
+
isRetry: numAttempt > 0
|
|
180
|
+
})
|
|
138
181
|
})
|
|
139
182
|
return onBeforeTryTask.apply(this, arguments)
|
|
140
183
|
})
|
|
@@ -235,6 +278,7 @@ addHook({
|
|
|
235
278
|
|
|
236
279
|
const testTasks = getTypeTasks(startTestsResponse[0].tasks)
|
|
237
280
|
|
|
281
|
+
// Only one test task per test, even if there are retries
|
|
238
282
|
testTasks.forEach(task => {
|
|
239
283
|
const testAsyncResource = taskToAsync.get(task)
|
|
240
284
|
const { result } = task
|
|
@@ -242,7 +286,7 @@ addHook({
|
|
|
242
286
|
if (result) {
|
|
243
287
|
const { state, duration, errors } = result
|
|
244
288
|
if (state === 'skip') { // programmatic skip
|
|
245
|
-
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.
|
|
289
|
+
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
|
|
246
290
|
} else if (state === 'pass') {
|
|
247
291
|
if (testAsyncResource) {
|
|
248
292
|
testAsyncResource.runInAsyncScope(() => {
|
|
@@ -258,8 +302,10 @@ addHook({
|
|
|
258
302
|
}
|
|
259
303
|
|
|
260
304
|
if (testAsyncResource) {
|
|
305
|
+
const isRetry = task.result?.retryCount > 0
|
|
306
|
+
// `duration` is the duration of all the retries, so it can't be used if there are retries
|
|
261
307
|
testAsyncResource.runInAsyncScope(() => {
|
|
262
|
-
testErrorCh.publish({ duration, error: testError })
|
|
308
|
+
testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError })
|
|
263
309
|
})
|
|
264
310
|
}
|
|
265
311
|
if (errors?.length) {
|
|
@@ -267,7 +313,7 @@ addHook({
|
|
|
267
313
|
}
|
|
268
314
|
}
|
|
269
315
|
} else { // test.skip or test.todo
|
|
270
|
-
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.
|
|
316
|
+
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
|
|
271
317
|
}
|
|
272
318
|
})
|
|
273
319
|
|
|
@@ -73,7 +73,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
73
73
|
if (!cbExists && this.serviceIdentifier === 'sqs') {
|
|
74
74
|
const params = response.request.params
|
|
75
75
|
const operation = response.request.operation
|
|
76
|
-
this.responseExtractDSMContext(operation, params, response.data, span)
|
|
76
|
+
this.responseExtractDSMContext(operation, params, response.data ?? response, span)
|
|
77
77
|
}
|
|
78
78
|
this.addResponseTags(span, response)
|
|
79
79
|
this.finish(span, response, response.error)
|