dd-trace 5.88.0 → 5.90.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 -3
- package/ext/tags.js +2 -0
- package/index.d.ts +40 -0
- package/package.json +18 -14
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
- package/packages/datadog-instrumentations/src/cucumber.js +40 -1
- package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +26 -111
- package/packages/datadog-instrumentations/src/helpers/rewriter/{compiler.js → orchestrion/compiler.js} +5 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +6 -6
- package/packages/datadog-instrumentations/src/jest.js +123 -43
- package/packages/datadog-instrumentations/src/mocha/main.js +10 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +10 -2
- package/packages/datadog-instrumentations/src/playwright.js +20 -2
- package/packages/datadog-instrumentations/src/prisma.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +16 -0
- package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
- package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
- package/packages/datadog-plugin-apollo/src/index.js +28 -0
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +47 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
- package/packages/datadog-plugin-cypress/src/support.js +4 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-playwright/src/index.js +35 -8
- package/packages/dd-trace/src/aiguard/noop.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +18 -5
- package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
- package/packages/dd-trace/src/appsec/index.js +11 -1
- package/packages/dd-trace/src/appsec/reporter.js +28 -11
- package/packages/dd-trace/src/appsec/waf/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -0
- package/packages/dd-trace/src/config/index.js +3 -0
- package/packages/dd-trace/src/config/supported-configurations.json +17 -0
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
- package/packages/dd-trace/src/datastreams/index.js +3 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -0
- package/packages/dd-trace/src/datastreams/processor.js +126 -3
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
- package/packages/dd-trace/src/encode/agentless-json.js +82 -23
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -8
- package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
- package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
- package/packages/dd-trace/src/llmobs/tagger.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
- package/packages/dd-trace/src/pkg.js +1 -1
- package/packages/dd-trace/src/plugins/apollo.js +7 -2
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +95 -3
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
- package/packages/dd-trace/src/plugins/util/web.js +31 -11
- package/packages/dd-trace/src/proxy.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
- package/packages/dd-trace/src/standalone/product.js +2 -1
- package/packages/dd-trace/src/startup-log.js +52 -18
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/@datadog/source-map/index.js +1 -1
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/@opentelemetry/core/index.js +1 -1
- package/vendor/dist/@opentelemetry/resources/index.js +1 -1
- package/vendor/dist/astring/index.js +1 -1
- package/vendor/dist/crypto-randomuuid/index.js +1 -1
- package/vendor/dist/escape-string-regexp/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/ignore/index.js +1 -1
- package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
- package/vendor/dist/jest-docblock/index.js +1 -1
- package/vendor/dist/jsonpath-plus/index.js +1 -1
- package/vendor/dist/limiter/index.js +1 -1
- package/vendor/dist/lodash.sortby/index.js +1 -1
- package/vendor/dist/lru-cache/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/module-details-from-path/index.js +1 -1
- package/vendor/dist/mutexify/promise/index.js +1 -1
- package/vendor/dist/opentracing/index.js +1 -1
- package/vendor/dist/path-to-regexp/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/retry/index.js +1 -1
- package/vendor/dist/rfdc/index.js +1 -1
- package/vendor/dist/semifies/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/vendor/dist/source-map/lib/util/index.js +1 -1
- package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
- package/vendor/dist/ttl-set/index.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +0 -21
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This folder is basically a JavaScript version of Orchestrion-JS. The goal is
|
|
5
|
+
not to replace Orchestrion-JS, but rather to make it easier and faster to write
|
|
6
|
+
new integrations in the short-term, especially as many changes to the rewriter
|
|
7
|
+
will be needed as all the patterns we need have not been identified yet. This
|
|
8
|
+
will avoid the back and forth of having to make Rust changes to an external
|
|
9
|
+
library for every integration change or addition that requires something new.
|
|
10
|
+
|
|
11
|
+
In the meantime, we'll work concurrently on a change to Orchestrion-JS that
|
|
12
|
+
adds an "arbitrary transform" or "plugin" system that can be used from
|
|
13
|
+
JavaScript, in order to enable quick iteration while still using Orchestrion-JS.
|
|
14
|
+
Once that's done we'll use that, so that we can remove this JS approach and
|
|
15
|
+
return to using Orchestrion-JS.
|
|
16
|
+
|
|
17
|
+
The long term goal is to backport any additional features we add to the JS
|
|
18
|
+
rewriter (or using the plugin system in Orchestrion-JS once we're using that)
|
|
19
|
+
to Orchestrion-JS once we're confident that the implementation is fairly
|
|
20
|
+
complete and has all features we need.
|
|
21
|
+
|
|
22
|
+
Here is a list of the additions and changes in this rewriter compared to
|
|
23
|
+
Orchestrion-JS that will need to be backported:
|
|
24
|
+
|
|
25
|
+
(NOTE: Please keep this list up-to-date whenever new features are added)
|
|
26
|
+
|
|
27
|
+
- Supports an `astQuery` field to filter AST nodes with an esquery query. This
|
|
28
|
+
is mostly meant to be used when experimenting or if what needs to be queried
|
|
29
|
+
is not a function. We'll see over time if something like this is needed to be
|
|
30
|
+
backported or if it can be replaced by simpler queries.
|
|
31
|
+
- Supports replacing methods of child class instances in the base constructor.
|
|
32
|
+
- Supports tracing iterator (sync/async) returning functions (sync/async).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/* eslint-disable camelcase */
|
|
36
|
+
|
|
37
|
+
const { InstrumentationMatcher } = require('./matcher')
|
|
38
|
+
|
|
39
|
+
function create (configs, dc_module) {
|
|
40
|
+
return new InstrumentationMatcher(configs, dc_module)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { create }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-disable camelcase */
|
|
4
|
+
|
|
5
|
+
const semifies = require('../../../../../../vendor/dist/semifies')
|
|
6
|
+
const { Transformer } = require('./transformer')
|
|
7
|
+
|
|
8
|
+
// TODO: addTransform
|
|
9
|
+
|
|
10
|
+
class InstrumentationMatcher {
|
|
11
|
+
#configs = []
|
|
12
|
+
#dc_module = null
|
|
13
|
+
#transformers = {}
|
|
14
|
+
|
|
15
|
+
constructor (configs, dc_module) {
|
|
16
|
+
this.#configs = configs
|
|
17
|
+
this.#dc_module = dc_module || 'diagnostics_channel'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
free () {
|
|
21
|
+
this.#transformers = {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getTransformer (module_name, version, file_path) {
|
|
25
|
+
const id = `${module_name}/${file_path}@${version}`
|
|
26
|
+
|
|
27
|
+
if (this.#transformers[id]) return this.#transformers[id]
|
|
28
|
+
|
|
29
|
+
const configs = this.#configs.filter(({ module: { name, filePath, versionRange } }) =>
|
|
30
|
+
name === module_name &&
|
|
31
|
+
filePath === file_path &&
|
|
32
|
+
semifies(version, versionRange)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if (configs.length === 0) return
|
|
36
|
+
|
|
37
|
+
this.#transformers[id] = new Transformer(
|
|
38
|
+
module_name,
|
|
39
|
+
version,
|
|
40
|
+
file_path,
|
|
41
|
+
configs,
|
|
42
|
+
this.#dc_module
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return this.#transformers[id]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { InstrumentationMatcher }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-disable camelcase */
|
|
4
|
+
|
|
5
|
+
const { generate, parse, traverse } = require('./compiler')
|
|
6
|
+
const transforms = require('./transforms')
|
|
7
|
+
|
|
8
|
+
let SourceMapConsumer
|
|
9
|
+
let SourceMapGenerator
|
|
10
|
+
|
|
11
|
+
class Transformer {
|
|
12
|
+
#module_name = null
|
|
13
|
+
#file_path = null
|
|
14
|
+
#configs = []
|
|
15
|
+
#dc_module = null
|
|
16
|
+
|
|
17
|
+
// TODO: module_name false for user module
|
|
18
|
+
constructor (module_name, _version, file_path, configs, dc_module) {
|
|
19
|
+
this.#module_name = module_name
|
|
20
|
+
this.#file_path = file_path
|
|
21
|
+
this.#configs = configs
|
|
22
|
+
this.#dc_module = dc_module
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
free () {
|
|
26
|
+
// Freeing is not needed for a JavaScript implementation.
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
transform (code, module_type, sourcemap) {
|
|
30
|
+
if (!code) return { code }
|
|
31
|
+
|
|
32
|
+
const sourceType = module_type === 'esm' ? 'module' : 'script'
|
|
33
|
+
|
|
34
|
+
let ast
|
|
35
|
+
|
|
36
|
+
for (const config of this.#configs) {
|
|
37
|
+
const { astQuery, functionQuery = {} } = config
|
|
38
|
+
|
|
39
|
+
ast ??= parse(code.toString(), { range: true, sourceType })
|
|
40
|
+
|
|
41
|
+
const query = astQuery || this.#fromFunctionQuery(functionQuery)
|
|
42
|
+
const state = { ...config, dcModule: this.#dc_module, sourceType, functionQuery }
|
|
43
|
+
|
|
44
|
+
state.operator = this.#getOperator(state)
|
|
45
|
+
|
|
46
|
+
traverse(ast, query, (...args) => this.#visit(state, ...args))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (ast) {
|
|
50
|
+
SourceMapConsumer ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapConsumer
|
|
51
|
+
SourceMapGenerator ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapGenerator
|
|
52
|
+
|
|
53
|
+
const file = `${this.#module_name}/${this.#file_path}`
|
|
54
|
+
const sourceMapInput = sourcemap ? new SourceMapConsumer(sourcemap) : { file }
|
|
55
|
+
const sourceMap = new SourceMapGenerator(sourceMapInput)
|
|
56
|
+
const code = generate(ast, { sourceMap })
|
|
57
|
+
const map = sourceMap.toString()
|
|
58
|
+
|
|
59
|
+
return { code, map }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { code }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#visit (state, ...args) {
|
|
66
|
+
const transform = transforms[state.operator]
|
|
67
|
+
const { index } = state.functionQuery
|
|
68
|
+
|
|
69
|
+
if (index !== undefined) {
|
|
70
|
+
state.functionIndex = ++state.functionIndex || 0
|
|
71
|
+
|
|
72
|
+
if (index !== state.functionIndex) return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
transform(state, ...args)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#getOperator ({ functionQuery: { kind } }) {
|
|
79
|
+
switch (kind) {
|
|
80
|
+
case 'Async': return 'tracePromise'
|
|
81
|
+
case 'AsyncIterator': return 'traceAsyncIterator'
|
|
82
|
+
case 'Callback': return 'traceCallback'
|
|
83
|
+
case 'Iterator': return 'traceIterator'
|
|
84
|
+
case 'Sync': return 'traceSync'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#fromFunctionQuery (functionQuery) {
|
|
89
|
+
const { functionName, expressionName, className } = functionQuery
|
|
90
|
+
const method = functionQuery.methodName || functionQuery.privateMethodName
|
|
91
|
+
const type = functionQuery.privateMethodName ? 'PrivateIdentifier' : 'Identifier'
|
|
92
|
+
const queries = []
|
|
93
|
+
|
|
94
|
+
if (className) {
|
|
95
|
+
queries.push(
|
|
96
|
+
`[id.name="${className}"]`,
|
|
97
|
+
`[id.name="${className}"] > ClassExpression`,
|
|
98
|
+
`[id.name="${className}"] > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
|
|
99
|
+
`[id.name="${className}"] > ClassExpression > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`
|
|
100
|
+
)
|
|
101
|
+
} else if (method) {
|
|
102
|
+
queries.push(
|
|
103
|
+
`ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
|
|
104
|
+
`Property[key.name="${method}"][key.type=${type}] > [async]`
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (functionName) {
|
|
109
|
+
queries.push(`FunctionDeclaration[id.name="${functionName}"][async]`)
|
|
110
|
+
} else if (expressionName) {
|
|
111
|
+
queries.push(
|
|
112
|
+
`FunctionExpression[id.name="${expressionName}"][async]`,
|
|
113
|
+
`ArrowFunctionExpression[id.name="${expressionName}"][async]`
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return queries.join(', ')
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = { Transformer }
|
|
@@ -8,13 +8,13 @@ const tracingChannelPredicate = (node) => (
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
const transforms = module.exports = {
|
|
11
|
-
tracingChannelImport ({ sourceType }, node) {
|
|
11
|
+
tracingChannelImport ({ dcModule, sourceType }, node) {
|
|
12
12
|
if (node.body.some(tracingChannelPredicate)) return
|
|
13
13
|
|
|
14
14
|
const index = node.body.findIndex(child => child.directive === 'use strict')
|
|
15
15
|
const code = sourceType === 'module'
|
|
16
|
-
?
|
|
17
|
-
:
|
|
16
|
+
? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
|
|
17
|
+
: `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
|
|
18
18
|
|
|
19
19
|
node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
|
|
20
20
|
},
|
|
@@ -196,11 +196,11 @@ function wrapSuper (_state, node) {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
function wrapCallback (state, node) {
|
|
199
|
-
const { channelName, functionQuery: {
|
|
199
|
+
const { channelName, functionQuery: { callbackIndex = -1 } } = state
|
|
200
200
|
const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
|
|
201
201
|
const wrapper = parse(`
|
|
202
202
|
function wrapper () {
|
|
203
|
-
const __apm$cb = Array.prototype.at.call(arguments, ${
|
|
203
|
+
const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
|
|
204
204
|
const __apm$ctx = {
|
|
205
205
|
arguments,
|
|
206
206
|
self: this,
|
|
@@ -235,7 +235,7 @@ function wrapCallback (state, node) {
|
|
|
235
235
|
if (typeof __apm$cb !== 'function') {
|
|
236
236
|
return __apm$traced();
|
|
237
237
|
}
|
|
238
|
-
Array.prototype.splice.call(arguments, ${
|
|
238
|
+
Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
|
|
239
239
|
|
|
240
240
|
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
241
241
|
try {
|
|
@@ -47,6 +47,7 @@ const testSkippedCh = channel('ci:jest:test:skip')
|
|
|
47
47
|
const testFinishCh = channel('ci:jest:test:finish')
|
|
48
48
|
const testErrCh = channel('ci:jest:test:err')
|
|
49
49
|
const testFnCh = channel('ci:jest:test:fn')
|
|
50
|
+
const testSuiteHookFnCh = channel('ci:jest:test-suite:hook:fn')
|
|
50
51
|
|
|
51
52
|
const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
|
|
52
53
|
const libraryConfigurationCh = channel('ci:jest:library-configuration')
|
|
@@ -69,7 +70,9 @@ const RETRY_TIMES = Symbol.for('RETRY_TIMES')
|
|
|
69
70
|
let skippableSuites = []
|
|
70
71
|
let knownTests = {}
|
|
71
72
|
let isCodeCoverageEnabled = false
|
|
73
|
+
let isCodeCoverageEnabledBecauseOfUs = false
|
|
72
74
|
let isSuitesSkippingEnabled = false
|
|
75
|
+
let isKeepingCoverageConfiguration = false
|
|
73
76
|
let isUserCodeCoverageEnabled = false
|
|
74
77
|
let isSuitesSkipped = false
|
|
75
78
|
let numSkippedSuites = 0
|
|
@@ -503,6 +506,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
503
506
|
})
|
|
504
507
|
}
|
|
505
508
|
|
|
509
|
+
if (event.name === 'hook_start' && (event.hook.type === 'beforeAll' || event.hook.type === 'afterAll')) {
|
|
510
|
+
const ctx = { testSuiteAbsolutePath: this.testSuiteAbsolutePath }
|
|
511
|
+
let hookFn = event.hook.fn
|
|
512
|
+
if (originalHookFns.has(event.hook)) {
|
|
513
|
+
hookFn = originalHookFns.get(event.hook)
|
|
514
|
+
} else {
|
|
515
|
+
originalHookFns.set(event.hook, hookFn)
|
|
516
|
+
}
|
|
517
|
+
event.hook.fn = shimmer.wrapFunction(hookFn, hookFn => function () {
|
|
518
|
+
return testSuiteHookFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
506
522
|
if (event.name === 'add_test') {
|
|
507
523
|
if (event.failing) {
|
|
508
524
|
return
|
|
@@ -671,6 +687,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
671
687
|
}
|
|
672
688
|
}
|
|
673
689
|
|
|
690
|
+
// ATR: set failedAllTests when all auto test retries were exhausted and every attempt failed
|
|
691
|
+
if (this.isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry) {
|
|
692
|
+
const maxRetries = Number(this.global[RETRY_TIMES]) || 0
|
|
693
|
+
if (event.test?.invocations === maxRetries + 1 && status === 'fail') {
|
|
694
|
+
failedAllTests = true
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
674
698
|
const promises = {}
|
|
675
699
|
const numRetries = this.global[RETRY_TIMES]
|
|
676
700
|
const numTestExecutions = event.test?.invocations
|
|
@@ -994,6 +1018,8 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
994
1018
|
if (!err) {
|
|
995
1019
|
isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
|
|
996
1020
|
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
|
|
1021
|
+
isKeepingCoverageConfiguration =
|
|
1022
|
+
libraryConfig.isKeepingCoverageConfiguration ?? isKeepingCoverageConfiguration
|
|
997
1023
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
998
1024
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
999
1025
|
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
@@ -1327,9 +1353,10 @@ function coverageReporterWrapper (coverageReporter) {
|
|
|
1327
1353
|
*/
|
|
1328
1354
|
// `_addUntestedFiles` is an async function
|
|
1329
1355
|
shimmer.wrap(CoverageReporter.prototype, '_addUntestedFiles', addUntestedFiles => function () {
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1356
|
+
if (isKeepingCoverageConfiguration) {
|
|
1357
|
+
return addUntestedFiles.apply(this, arguments)
|
|
1358
|
+
}
|
|
1359
|
+
if (isCodeCoverageEnabledBecauseOfUs) {
|
|
1333
1360
|
return Promise.resolve()
|
|
1334
1361
|
}
|
|
1335
1362
|
return addUntestedFiles.apply(this, arguments)
|
|
@@ -1509,12 +1536,13 @@ function configureTestEnvironment (readConfigsResult) {
|
|
|
1509
1536
|
}
|
|
1510
1537
|
|
|
1511
1538
|
isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
|
|
1539
|
+
isCodeCoverageEnabledBecauseOfUs = isCodeCoverageEnabled && !isUserCodeCoverageEnabled
|
|
1512
1540
|
|
|
1513
1541
|
if (readConfigsResult.globalConfig.forceExit) {
|
|
1514
1542
|
log.warn("Jest's '--forceExit' flag has been passed. This may cause loss of data.")
|
|
1515
1543
|
}
|
|
1516
1544
|
|
|
1517
|
-
if (
|
|
1545
|
+
if (isCodeCoverageEnabledBecauseOfUs) {
|
|
1518
1546
|
const globalConfig = {
|
|
1519
1547
|
...readConfigsResult.globalConfig,
|
|
1520
1548
|
collectCoverage: true,
|
|
@@ -1522,14 +1550,18 @@ function configureTestEnvironment (readConfigsResult) {
|
|
|
1522
1550
|
readConfigsResult.globalConfig = globalConfig
|
|
1523
1551
|
}
|
|
1524
1552
|
if (isSuitesSkippingEnabled) {
|
|
1525
|
-
// If suite skipping is enabled,
|
|
1526
|
-
// so we do not show them.
|
|
1527
|
-
// Also, we might skip every test, so we need to pass `passWithNoTests`
|
|
1553
|
+
// If suite skipping is enabled, we pass `passWithNoTests` in case every test gets skipped.
|
|
1528
1554
|
const globalConfig = {
|
|
1529
1555
|
...readConfigsResult.globalConfig,
|
|
1530
|
-
coverageReporters: ['none'],
|
|
1531
1556
|
passWithNoTests: true,
|
|
1532
1557
|
}
|
|
1558
|
+
if (isCodeCoverageEnabledBecauseOfUs && !isKeepingCoverageConfiguration) {
|
|
1559
|
+
globalConfig.coverageReporters = ['none']
|
|
1560
|
+
readConfigsResult.configs = configs.map(config => ({
|
|
1561
|
+
...config,
|
|
1562
|
+
coverageReporters: ['none'],
|
|
1563
|
+
}))
|
|
1564
|
+
}
|
|
1533
1565
|
readConfigsResult.globalConfig = globalConfig
|
|
1534
1566
|
}
|
|
1535
1567
|
|
|
@@ -1552,49 +1584,97 @@ function jestConfigSyncWrapper (jestConfig) {
|
|
|
1552
1584
|
})
|
|
1553
1585
|
}
|
|
1554
1586
|
|
|
1587
|
+
const DD_TEST_ENVIRONMENT_OPTION_KEYS = [
|
|
1588
|
+
'_ddTestModuleId',
|
|
1589
|
+
'_ddTestSessionId',
|
|
1590
|
+
'_ddTestCommand',
|
|
1591
|
+
'_ddTestSessionName',
|
|
1592
|
+
'_ddForcedToRun',
|
|
1593
|
+
'_ddUnskippable',
|
|
1594
|
+
'_ddItrCorrelationId',
|
|
1595
|
+
'_ddKnownTests',
|
|
1596
|
+
'_ddIsEarlyFlakeDetectionEnabled',
|
|
1597
|
+
'_ddEarlyFlakeDetectionSlowTestRetries',
|
|
1598
|
+
'_ddRepositoryRoot',
|
|
1599
|
+
'_ddIsFlakyTestRetriesEnabled',
|
|
1600
|
+
'_ddFlakyTestRetriesCount',
|
|
1601
|
+
'_ddIsDiEnabled',
|
|
1602
|
+
'_ddIsKnownTestsEnabled',
|
|
1603
|
+
'_ddIsTestManagementTestsEnabled',
|
|
1604
|
+
'_ddTestManagementTests',
|
|
1605
|
+
'_ddTestManagementAttemptToFixRetries',
|
|
1606
|
+
'_ddModifiedFiles',
|
|
1607
|
+
]
|
|
1608
|
+
|
|
1609
|
+
function removeDatadogTestEnvironmentOptions (testEnvironmentOptions) {
|
|
1610
|
+
const removedEntries = []
|
|
1611
|
+
|
|
1612
|
+
for (const key of DD_TEST_ENVIRONMENT_OPTION_KEYS) {
|
|
1613
|
+
if (!Object.hasOwn(testEnvironmentOptions, key)) {
|
|
1614
|
+
continue
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
removedEntries.push([key, testEnvironmentOptions[key]])
|
|
1618
|
+
delete testEnvironmentOptions[key]
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
return function restoreDatadogTestEnvironmentOptions () {
|
|
1622
|
+
for (const [key, value] of removedEntries) {
|
|
1623
|
+
testEnvironmentOptions[key] = value
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Wrap `createScriptTransformer` to temporarily hide Datadog-specific
|
|
1630
|
+
* `testEnvironmentOptions` keys while Jest builds its transform config.
|
|
1631
|
+
*
|
|
1632
|
+
* @param {Function} createScriptTransformer
|
|
1633
|
+
* @returns {Function}
|
|
1634
|
+
*/
|
|
1635
|
+
function wrapCreateScriptTransformer (createScriptTransformer) {
|
|
1636
|
+
return function (config) {
|
|
1637
|
+
const testEnvironmentOptions = config?.testEnvironmentOptions
|
|
1638
|
+
|
|
1639
|
+
if (!testEnvironmentOptions) {
|
|
1640
|
+
return createScriptTransformer.apply(this, arguments)
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
const restoreTestEnvironmentOptions = removeDatadogTestEnvironmentOptions(testEnvironmentOptions)
|
|
1644
|
+
|
|
1645
|
+
try {
|
|
1646
|
+
const result = createScriptTransformer.apply(this, arguments)
|
|
1647
|
+
|
|
1648
|
+
if (result?.then) {
|
|
1649
|
+
return result.finally(restoreTestEnvironmentOptions)
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
restoreTestEnvironmentOptions()
|
|
1653
|
+
return result
|
|
1654
|
+
} catch (e) {
|
|
1655
|
+
restoreTestEnvironmentOptions()
|
|
1656
|
+
throw e
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1555
1661
|
addHook({
|
|
1556
1662
|
name: '@jest/transform',
|
|
1557
|
-
versions: ['>=24.8.0'],
|
|
1663
|
+
versions: ['>=24.8.0 <30.0.0'],
|
|
1558
1664
|
file: 'build/ScriptTransformer.js',
|
|
1559
1665
|
}, transformPackage => {
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
// `createScriptTransformer` is an async function
|
|
1563
|
-
transformPackage.createScriptTransformer = function (config) {
|
|
1564
|
-
const { testEnvironmentOptions, ...restOfConfig } = config
|
|
1565
|
-
const {
|
|
1566
|
-
_ddTestModuleId,
|
|
1567
|
-
_ddTestSessionId,
|
|
1568
|
-
_ddTestCommand,
|
|
1569
|
-
_ddTestSessionName,
|
|
1570
|
-
_ddForcedToRun,
|
|
1571
|
-
_ddUnskippable,
|
|
1572
|
-
_ddItrCorrelationId,
|
|
1573
|
-
_ddKnownTests,
|
|
1574
|
-
_ddIsEarlyFlakeDetectionEnabled,
|
|
1575
|
-
_ddEarlyFlakeDetectionSlowTestRetries,
|
|
1576
|
-
_ddRepositoryRoot,
|
|
1577
|
-
_ddIsFlakyTestRetriesEnabled,
|
|
1578
|
-
_ddFlakyTestRetriesCount,
|
|
1579
|
-
_ddIsDiEnabled,
|
|
1580
|
-
_ddIsKnownTestsEnabled,
|
|
1581
|
-
_ddIsTestManagementTestsEnabled,
|
|
1582
|
-
_ddTestManagementTests,
|
|
1583
|
-
_ddTestManagementAttemptToFixRetries,
|
|
1584
|
-
_ddModifiedFiles,
|
|
1585
|
-
...restOfTestEnvironmentOptions
|
|
1586
|
-
} = testEnvironmentOptions
|
|
1587
|
-
|
|
1588
|
-
restOfConfig.testEnvironmentOptions = restOfTestEnvironmentOptions
|
|
1589
|
-
|
|
1590
|
-
arguments[0] = restOfConfig
|
|
1591
|
-
|
|
1592
|
-
return originalCreateScriptTransformer.apply(this, arguments)
|
|
1593
|
-
}
|
|
1666
|
+
transformPackage.createScriptTransformer = wrapCreateScriptTransformer(transformPackage.createScriptTransformer)
|
|
1594
1667
|
|
|
1595
1668
|
return transformPackage
|
|
1596
1669
|
})
|
|
1597
1670
|
|
|
1671
|
+
addHook({
|
|
1672
|
+
name: '@jest/transform',
|
|
1673
|
+
versions: ['>=30.0.0'],
|
|
1674
|
+
}, transformPackage => {
|
|
1675
|
+
return shimmer.wrap(transformPackage, 'createScriptTransformer', wrapCreateScriptTransformer, { replaceGetter: true })
|
|
1676
|
+
})
|
|
1677
|
+
|
|
1598
1678
|
/**
|
|
1599
1679
|
* Hook to remove the test paths (test suite) that are part of `skippableSuites`
|
|
1600
1680
|
*/
|
|
@@ -315,10 +315,10 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
315
315
|
config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
|
|
316
316
|
config.testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
|
|
317
317
|
config.isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
|
|
318
|
-
// ITR
|
|
318
|
+
// ITR is not supported in parallel mode yet
|
|
319
319
|
config.isSuitesSkippingEnabled = !isParallel && libraryConfig.isSuitesSkippingEnabled
|
|
320
|
-
config.isFlakyTestRetriesEnabled =
|
|
321
|
-
config.flakyTestRetriesCount =
|
|
320
|
+
config.isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
321
|
+
config.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
322
322
|
|
|
323
323
|
if (config.isKnownTestsEnabled) {
|
|
324
324
|
ctx.onDone = onReceivedKnownTests
|
|
@@ -663,7 +663,8 @@ addHook({
|
|
|
663
663
|
if (!testFinishCh.hasSubscribers ||
|
|
664
664
|
(!config.isKnownTestsEnabled &&
|
|
665
665
|
!config.isTestManagementTestsEnabled &&
|
|
666
|
-
!config.isImpactedTestsEnabled
|
|
666
|
+
!config.isImpactedTestsEnabled &&
|
|
667
|
+
!config.isFlakyTestRetriesEnabled)) {
|
|
667
668
|
return run.apply(this, arguments)
|
|
668
669
|
}
|
|
669
670
|
|
|
@@ -709,6 +710,11 @@ addHook({
|
|
|
709
710
|
newWorkerArgs._ddModifiedFiles = config.modifiedFiles || {}
|
|
710
711
|
}
|
|
711
712
|
|
|
713
|
+
if (config.isFlakyTestRetriesEnabled) {
|
|
714
|
+
newWorkerArgs._ddIsFlakyTestRetriesEnabled = true
|
|
715
|
+
newWorkerArgs._ddFlakyTestRetriesCount = config.flakyTestRetriesCount
|
|
716
|
+
}
|
|
717
|
+
|
|
712
718
|
// We pass the known tests for the test file to the worker
|
|
713
719
|
const testFileResult = await run.apply(
|
|
714
720
|
this,
|
|
@@ -289,6 +289,12 @@ function getOnTestEndHandler (config) {
|
|
|
289
289
|
hasFailedAllRetries = true
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
// ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
|
|
293
|
+
if (config.isFlakyTestRetriesEnabled && !test._ddIsAttemptToFix && !test._ddIsEfdRetry &&
|
|
294
|
+
getIsLastRetry(test) && testStatuses.every(status => status === 'fail')) {
|
|
295
|
+
hasFailedAllRetries = true
|
|
296
|
+
}
|
|
297
|
+
|
|
292
298
|
const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
|
|
293
299
|
const isAtrRetry = config.isFlakyTestRetriesEnabled &&
|
|
294
300
|
!test._ddIsAttemptToFix &&
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
getOnHookEndHandler,
|
|
11
11
|
getOnFailHandler,
|
|
12
12
|
getOnPendingHandler,
|
|
13
|
+
getOnTestRetryHandler,
|
|
13
14
|
getRunTestsWrapper,
|
|
14
15
|
} = require('./utils')
|
|
15
16
|
require('./common')
|
|
@@ -48,6 +49,12 @@ addHook({
|
|
|
48
49
|
delete this.options._ddIsTestManagementTestsEnabled
|
|
49
50
|
delete this.options._ddTestManagementTests
|
|
50
51
|
}
|
|
52
|
+
if (this.options._ddIsFlakyTestRetriesEnabled) {
|
|
53
|
+
config.isFlakyTestRetriesEnabled = true
|
|
54
|
+
config.flakyTestRetriesCount = this.options._ddFlakyTestRetriesCount
|
|
55
|
+
delete this.options._ddIsFlakyTestRetriesEnabled
|
|
56
|
+
delete this.options._ddFlakyTestRetriesCount
|
|
57
|
+
}
|
|
51
58
|
return run.apply(this, arguments)
|
|
52
59
|
})
|
|
53
60
|
|
|
@@ -74,6 +81,8 @@ addHook({
|
|
|
74
81
|
|
|
75
82
|
this.on('test end', getOnTestEndHandler(config))
|
|
76
83
|
|
|
84
|
+
this.on('retry', getOnTestRetryHandler(config))
|
|
85
|
+
|
|
77
86
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
78
87
|
this.on('hook end', getOnHookEndHandler())
|
|
79
88
|
|
|
@@ -92,5 +101,4 @@ addHook({
|
|
|
92
101
|
name: 'mocha',
|
|
93
102
|
versions: ['>=5.2.0'],
|
|
94
103
|
file: 'lib/runnable.js',
|
|
95
|
-
}, runnableWrapper)
|
|
96
|
-
// TODO: parallel mode does not support flaky test retries, so no library config is passed.
|
|
104
|
+
}, (runnablePackage) => runnableWrapper(runnablePackage, config))
|
|
@@ -302,6 +302,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
302
302
|
const {
|
|
303
303
|
_requireFile: testSuiteAbsolutePath,
|
|
304
304
|
location: {
|
|
305
|
+
file: testSourceFileAbsolutePath,
|
|
305
306
|
line: testSourceLine,
|
|
306
307
|
},
|
|
307
308
|
_type,
|
|
@@ -319,7 +320,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
319
320
|
|
|
320
321
|
if (isNewTestSuite) {
|
|
321
322
|
startedSuites.push(testSuiteAbsolutePath)
|
|
322
|
-
const testSuiteCtx = { testSuiteAbsolutePath }
|
|
323
|
+
const testSuiteCtx = { testSuiteAbsolutePath, testSourceFileAbsolutePath }
|
|
323
324
|
testSuiteToCtx.set(testSuiteAbsolutePath, testSuiteCtx)
|
|
324
325
|
testSuiteStartCh.runStores(testSuiteCtx, () => {})
|
|
325
326
|
}
|
|
@@ -335,6 +336,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
335
336
|
const testCtx = {
|
|
336
337
|
testName,
|
|
337
338
|
testSuiteAbsolutePath,
|
|
339
|
+
testSourceFileAbsolutePath,
|
|
338
340
|
testSourceLine,
|
|
339
341
|
browserName,
|
|
340
342
|
isDisabled: test._ddIsDisabled,
|
|
@@ -404,6 +406,15 @@ function testEndHandler ({
|
|
|
404
406
|
test._ddHasFailedAllRetries = true
|
|
405
407
|
}
|
|
406
408
|
|
|
409
|
+
// ATR: set _ddHasFailedAllRetries when all auto test retries were exhausted and every attempt failed
|
|
410
|
+
if (isFlakyTestRetriesEnabled && !testProperties.attemptToFix && !test._ddIsEfdRetry &&
|
|
411
|
+
!(test._ddIsNew || test._ddIsModified) &&
|
|
412
|
+
flakyTestRetriesCount != null && flakyTestRetriesCount > 0 &&
|
|
413
|
+
testStatuses.length === flakyTestRetriesCount + 1 &&
|
|
414
|
+
testStatuses.every(status => status === 'fail')) {
|
|
415
|
+
test._ddHasFailedAllRetries = true
|
|
416
|
+
}
|
|
417
|
+
|
|
407
418
|
// this handles tests that do not go through the worker process (because they're skipped)
|
|
408
419
|
if (shouldCreateTestSpan) {
|
|
409
420
|
const testResult = results.at(-1)
|
|
@@ -459,6 +470,7 @@ function testEndHandler ({
|
|
|
459
470
|
testSkipCh.publish({
|
|
460
471
|
testName: getTestFullname(test),
|
|
461
472
|
testSuiteAbsolutePath,
|
|
473
|
+
testSourceFileAbsolutePath: test.location.file,
|
|
462
474
|
testSourceLine: test.location.line,
|
|
463
475
|
browserName,
|
|
464
476
|
isNew: test._ddIsNew,
|
|
@@ -1144,6 +1156,7 @@ addHook({
|
|
|
1144
1156
|
const {
|
|
1145
1157
|
_requireFile: testSuiteAbsolutePath,
|
|
1146
1158
|
location: {
|
|
1159
|
+
file: testSourceFileAbsolutePath,
|
|
1147
1160
|
line: testSourceLine,
|
|
1148
1161
|
},
|
|
1149
1162
|
} = test
|
|
@@ -1159,6 +1172,7 @@ addHook({
|
|
|
1159
1172
|
const testCtx = {
|
|
1160
1173
|
testName,
|
|
1161
1174
|
testSuiteAbsolutePath,
|
|
1175
|
+
testSourceFileAbsolutePath,
|
|
1162
1176
|
testSourceLine,
|
|
1163
1177
|
browserName,
|
|
1164
1178
|
}
|
|
@@ -1322,7 +1336,10 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
1322
1336
|
if (didNotRun) {
|
|
1323
1337
|
const {
|
|
1324
1338
|
_requireFile: testSuiteAbsolutePath,
|
|
1325
|
-
location: {
|
|
1339
|
+
location: {
|
|
1340
|
+
file: testSourceFileAbsolutePath,
|
|
1341
|
+
line: testSourceLine,
|
|
1342
|
+
},
|
|
1326
1343
|
_ddIsNew: isNew,
|
|
1327
1344
|
_ddIsDisabled: isDisabled,
|
|
1328
1345
|
_ddIsModified: isModified,
|
|
@@ -1333,6 +1350,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
1333
1350
|
testSkipCh.publish({
|
|
1334
1351
|
testName: getTestFullname(test),
|
|
1335
1352
|
testSuiteAbsolutePath,
|
|
1353
|
+
testSourceFileAbsolutePath,
|
|
1336
1354
|
testSourceLine,
|
|
1337
1355
|
browserName,
|
|
1338
1356
|
isNew,
|
|
@@ -172,8 +172,10 @@ const prismaHook = (runtime, versions, name, isIitm) => {
|
|
|
172
172
|
prismaHelperInit.publish(prismaHelperCtx)
|
|
173
173
|
|
|
174
174
|
const helper = prismaHelperCtx.helper
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
if (helper) {
|
|
176
|
+
this._tracingHelper = helper
|
|
177
|
+
this._engine.tracingHelper = helper
|
|
178
|
+
}
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
}
|