dd-trace 5.83.0 → 5.84.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 -1
- package/index.d.ts +61 -1
- package/package.json +3 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +6 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +73 -16
- package/packages/datadog-instrumentations/src/jest.js +89 -50
- package/packages/datadog-instrumentations/src/playwright.js +12 -8
- package/packages/datadog-plugin-cucumber/src/index.js +33 -32
- package/packages/datadog-plugin-playwright/src/index.js +23 -23
- package/packages/datadog-shimmer/src/shimmer.js +2 -5
- package/packages/dd-trace/src/agent/info.js +57 -0
- package/packages/dd-trace/src/agent/url.js +28 -0
- package/packages/dd-trace/src/appsec/index.js +47 -7
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -9
- package/packages/dd-trace/src/ci-visibility/telemetry.js +6 -2
- package/packages/dd-trace/src/config/index.js +15 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -0
- package/packages/dd-trace/src/config/supported-configurations.json +1 -1
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -5
- package/packages/dd-trace/src/datastreams/writer.js +2 -8
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -7
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +10 -11
- package/packages/dd-trace/src/dogstatsd.js +3 -9
- package/packages/dd-trace/src/exporters/agent/index.js +4 -8
- package/packages/dd-trace/src/exporters/agent/writer.js +3 -2
- package/packages/dd-trace/src/exporters/common/{agent-info-exporter.js → buffering-exporter.js} +10 -37
- package/packages/dd-trace/src/exporters/span-stats/index.js +3 -10
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -8
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -9
- package/packages/dd-trace/src/log/index.js +45 -30
- package/packages/dd-trace/src/log/writer.js +13 -78
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -8
- package/packages/dd-trace/src/openfeature/writers/util.js +3 -8
- package/packages/dd-trace/src/profiling/config.js +3 -6
- package/packages/dd-trace/src/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/remote_config/index.js +2 -7
- package/packages/dd-trace/src/startup-log.js +2 -2
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"@isaacs/ttlcache","https://github.com/isaacs/ttlcache","['BlueOak-1.0.0']","['Isaac Z. Schlueter']"
|
|
12
12
|
"@jsep-plugin/assignment","https://github.com/EricSmekens/jsep","['MIT']","['Shelly']"
|
|
13
13
|
"@jsep-plugin/regex","https://github.com/EricSmekens/jsep","['MIT']","['Shelly']"
|
|
14
|
-
"@openfeature/server-sdk","https://github.com/open-feature/js-sdk","['Apache-2.0']","['open-feature']"
|
|
15
14
|
"@opentelemetry/api","https://github.com/open-telemetry/opentelemetry-js","['Apache-2.0']","['OpenTelemetry Authors']"
|
|
16
15
|
"@opentelemetry/api-logs","https://github.com/open-telemetry/opentelemetry-js","['Apache-2.0']","['OpenTelemetry Authors']"
|
|
17
16
|
"@opentelemetry/core","https://github.com/open-telemetry/opentelemetry-js","['Apache-2.0']","['OpenTelemetry Authors']"
|
package/index.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ interface Tracer extends opentracing.Tracer {
|
|
|
148
148
|
* OpenFeature Provider with Remote Config integration.
|
|
149
149
|
*
|
|
150
150
|
* Extends DatadogNodeServerProvider with Remote Config integration for dynamic flag configuration.
|
|
151
|
-
* Enable with
|
|
151
|
+
* Enable with DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true.
|
|
152
152
|
*
|
|
153
153
|
* @beta This feature is in preview and not ready for production use
|
|
154
154
|
*/
|
|
@@ -241,6 +241,8 @@ interface Plugins {
|
|
|
241
241
|
"express": tracer.plugins.express;
|
|
242
242
|
"fastify": tracer.plugins.fastify;
|
|
243
243
|
"fetch": tracer.plugins.fetch;
|
|
244
|
+
"find-my-way": tracer.plugins.find_my_way;
|
|
245
|
+
"fs": tracer.plugins.fs;
|
|
244
246
|
"generic-pool": tracer.plugins.generic_pool;
|
|
245
247
|
"google-cloud-pubsub": tracer.plugins.google_cloud_pubsub;
|
|
246
248
|
"google-cloud-vertexai": tracer.plugins.google_cloud_vertexai;
|
|
@@ -269,6 +271,7 @@ interface Plugins {
|
|
|
269
271
|
"mysql2": tracer.plugins.mysql2;
|
|
270
272
|
"net": tracer.plugins.net;
|
|
271
273
|
"next": tracer.plugins.next;
|
|
274
|
+
"nyc": tracer.plugins.nyc;
|
|
272
275
|
"openai": tracer.plugins.openai;
|
|
273
276
|
"opensearch": tracer.plugins.opensearch;
|
|
274
277
|
"oracledb": tracer.plugins.oracledb;
|
|
@@ -286,7 +289,9 @@ interface Plugins {
|
|
|
286
289
|
"tedious": tracer.plugins.tedious;
|
|
287
290
|
"undici": tracer.plugins.undici;
|
|
288
291
|
"vitest": tracer.plugins.vitest;
|
|
292
|
+
"web": tracer.plugins.web;
|
|
289
293
|
"winston": tracer.plugins.winston;
|
|
294
|
+
"ws": tracer.plugins.ws;
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
declare namespace tracer {
|
|
@@ -687,6 +692,7 @@ declare namespace tracer {
|
|
|
687
692
|
/**
|
|
688
693
|
* Whether to enable the feature flagging provider.
|
|
689
694
|
* Requires Remote Config to be properly configured.
|
|
695
|
+
* Can be configured via DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED environment variable.
|
|
690
696
|
*
|
|
691
697
|
* @default false
|
|
692
698
|
*/
|
|
@@ -694,6 +700,7 @@ declare namespace tracer {
|
|
|
694
700
|
/**
|
|
695
701
|
* Timeout in milliseconds for OpenFeature provider initialization.
|
|
696
702
|
* If configuration is not received within this time, initialization fails.
|
|
703
|
+
* Can be configured via DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS environment variable.
|
|
697
704
|
*
|
|
698
705
|
* @default 30000
|
|
699
706
|
*/
|
|
@@ -1924,6 +1931,16 @@ declare namespace tracer {
|
|
|
1924
1931
|
*/
|
|
1925
1932
|
interface fetch extends HttpClient {}
|
|
1926
1933
|
|
|
1934
|
+
/**
|
|
1935
|
+
* This plugin patches the [find-my-way](https://github.com/delvedor/find-my-way) router.
|
|
1936
|
+
*/
|
|
1937
|
+
interface find_my_way extends Integration {}
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* This plugin automatically instruments Node.js core fs operations.
|
|
1941
|
+
*/
|
|
1942
|
+
interface fs extends Instrumentation {}
|
|
1943
|
+
|
|
1927
1944
|
/**
|
|
1928
1945
|
* This plugin patches the [generic-pool](https://github.com/coopernurse/node-pool)
|
|
1929
1946
|
* module to bind the callbacks the the caller context.
|
|
@@ -2355,6 +2372,11 @@ declare namespace tracer {
|
|
|
2355
2372
|
};
|
|
2356
2373
|
}
|
|
2357
2374
|
|
|
2375
|
+
/**
|
|
2376
|
+
* This plugin integrates with [nyc](https://github.com/istanbuljs/nyc) for CI visibility.
|
|
2377
|
+
*/
|
|
2378
|
+
interface nyc extends Integration {}
|
|
2379
|
+
|
|
2358
2380
|
/**
|
|
2359
2381
|
* This plugin automatically instruments the
|
|
2360
2382
|
* [openai](https://platform.openai.com/docs/api-reference?lang=node.js) module.
|
|
@@ -2541,6 +2563,32 @@ declare namespace tracer {
|
|
|
2541
2563
|
*/
|
|
2542
2564
|
interface vitest extends Integration {}
|
|
2543
2565
|
|
|
2566
|
+
/**
|
|
2567
|
+
* This plugin implements shared web request instrumentation helpers.
|
|
2568
|
+
*/
|
|
2569
|
+
interface web extends HttpServer {
|
|
2570
|
+
/**
|
|
2571
|
+
* Custom filter function used to decide whether a URL/path should be instrumented.
|
|
2572
|
+
* Takes precedence over allowlist/blocklist.
|
|
2573
|
+
*/
|
|
2574
|
+
filter?: (urlOrPath: string) => boolean;
|
|
2575
|
+
|
|
2576
|
+
/**
|
|
2577
|
+
* Whether (or how) to obfuscate querystring values in `http.url`.
|
|
2578
|
+
*
|
|
2579
|
+
* - `true`: obfuscate all values
|
|
2580
|
+
* - `false`: disable obfuscation
|
|
2581
|
+
* - `string`: regex string used to obfuscate matching values (empty string disables)
|
|
2582
|
+
* - `RegExp`: regex used to obfuscate matching values
|
|
2583
|
+
*/
|
|
2584
|
+
queryStringObfuscation?: boolean | string | RegExp;
|
|
2585
|
+
|
|
2586
|
+
/**
|
|
2587
|
+
* Whether to enable resource renaming when the framework route is unavailable.
|
|
2588
|
+
*/
|
|
2589
|
+
resourceRenamingEnabled?: boolean;
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2544
2592
|
/**
|
|
2545
2593
|
* This plugin patches the [winston](https://github.com/winstonjs/winston)
|
|
2546
2594
|
* to automatically inject trace identifiers in log records when the
|
|
@@ -2548,6 +2596,18 @@ declare namespace tracer {
|
|
|
2548
2596
|
* on the tracer.
|
|
2549
2597
|
*/
|
|
2550
2598
|
interface winston extends Integration {}
|
|
2599
|
+
|
|
2600
|
+
/**
|
|
2601
|
+
* This plugin automatically instruments the
|
|
2602
|
+
* [ws](https://github.com/websockets/ws) module.
|
|
2603
|
+
*/
|
|
2604
|
+
interface ws extends Instrumentation {
|
|
2605
|
+
/**
|
|
2606
|
+
* Controls whether websocket messages should be traced.
|
|
2607
|
+
* This is also configurable via `DD_TRACE_WEBSOCKET_MESSAGES_ENABLED`.
|
|
2608
|
+
*/
|
|
2609
|
+
traceWebsocketMessagesEnabled?: boolean;
|
|
2610
|
+
}
|
|
2551
2611
|
}
|
|
2552
2612
|
|
|
2553
2613
|
export namespace opentelemetry {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.84.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:debugger": "mocha packages/dd-trace/test/debugger/**/*.spec.js",
|
|
29
29
|
"test:debugger:ci": "nyc --no-clean --include \"packages/dd-trace/src/debugger/**/*.js\" -- npm run test:debugger",
|
|
30
30
|
"test:eslint-rules": "node eslint-rules/*.test.mjs",
|
|
31
|
-
"test:trace:core": "node scripts/mocha-parallel-files.js --expose-gc --timeout 30000 -- \"packages/dd-trace/test/*.spec.js\" \"packages/dd-trace/test/{ci-visibility,config,crashtracking,datastreams,encode,exporters,msgpack,opentelemetry,opentracing,payload-tagging,plugins,remote_config,service-naming,standalone,telemetry,external-logger}/**/*.spec.js\"",
|
|
31
|
+
"test:trace:core": "node scripts/mocha-parallel-files.js --expose-gc --timeout 30000 -- \"packages/dd-trace/test/*.spec.js\" \"packages/dd-trace/test/{agent,ci-visibility,config,crashtracking,datastreams,encode,exporters,msgpack,opentelemetry,opentracing,payload-tagging,plugins,remote_config,service-naming,standalone,telemetry,external-logger}/**/*.spec.js\"",
|
|
32
32
|
"test:trace:core:ci": "nyc --no-clean --include \"packages/dd-trace/src/**/*.js\" -- npm run test:trace:core",
|
|
33
33
|
"test:trace:guardrails": "mocha packages/dd-trace/test/guardrails/**/*.spec.js",
|
|
34
34
|
"test:trace:guardrails:ci": "nyc --no-clean --include \"packages/dd-trace/src/guardrails/**/*.js\" -- npm run test:trace:guardrails",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"@datadog/native-appsec": "10.3.0",
|
|
138
138
|
"@datadog/native-iast-taint-tracking": "4.1.0",
|
|
139
139
|
"@datadog/native-metrics": "3.1.1",
|
|
140
|
-
"@datadog/openfeature-node-server": "^0.
|
|
140
|
+
"@datadog/openfeature-node-server": "^0.3.3",
|
|
141
141
|
"@datadog/pprof": "5.13.2",
|
|
142
142
|
"@datadog/wasm-js-rewriter": "5.0.1",
|
|
143
143
|
"@opentelemetry/api": ">=1.0.0 <1.10.0",
|
|
@@ -127,6 +127,7 @@ function fromFunctionQuery (functionQuery) {
|
|
|
127
127
|
if (className) {
|
|
128
128
|
queries.push(
|
|
129
129
|
`[id.name="${className}"]`,
|
|
130
|
+
`[id.name="${className}"] > ClassExpression`,
|
|
130
131
|
`[id.name="${className}"] > ClassBody > [key.name="${methodName}"] > [async]`,
|
|
131
132
|
`[id.name="${className}"] > ClassExpression > ClassBody > [key.name="${methodName}"] > [async]`
|
|
132
133
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { parse } = require('./compiler')
|
|
3
|
+
const { parse, query } = require('./compiler')
|
|
4
4
|
|
|
5
5
|
const tracingChannelPredicate = (node) => (
|
|
6
6
|
node.specifiers?.[0]?.local?.name === 'tr_ch_apm_tracingChannel' ||
|
|
@@ -106,34 +106,91 @@ function traceInstanceMethod (state, node, program) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function wrap (state, node) {
|
|
109
|
-
const { channelName, operator
|
|
109
|
+
const { channelName, operator } = state
|
|
110
|
+
|
|
111
|
+
if (operator === 'traceCallback') return wrapCallback(state, node)
|
|
112
|
+
|
|
110
113
|
const async = operator === 'tracePromise' ? 'async' : ''
|
|
111
114
|
const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
|
|
112
|
-
const tracedArgs = operator === 'traceCallback'
|
|
113
|
-
? `__apm$original_args.splice(${index}, 1, arguments[${index >= 0 ? index : `arguments.length + ${index}`}])`
|
|
114
|
-
: '__apm$original_args'
|
|
115
|
-
const traceParams = operator === 'traceCallback'
|
|
116
|
-
? `__apm$traced, ${index}`
|
|
117
|
-
: '__apm$traced'
|
|
118
115
|
const wrapper = parse(`
|
|
119
116
|
function wrapper () {
|
|
120
|
-
const __apm$
|
|
121
|
-
const __apm$traced = ${async} function () {
|
|
117
|
+
const __apm$traced = ${async} () => {
|
|
122
118
|
const __apm$wrapped = () => {};
|
|
123
|
-
|
|
124
|
-
return __apm$wrapped.apply(this, __apm$traced_args);
|
|
119
|
+
return __apm$wrapped.apply(this, arguments);
|
|
125
120
|
};
|
|
126
|
-
if (!${channelVariable}.hasSubscribers) return __apm$traced
|
|
127
|
-
return ${channelVariable}.${operator}($
|
|
121
|
+
if (!${channelVariable}.hasSubscribers) return __apm$traced();
|
|
122
|
+
return ${channelVariable}.${operator}(__apm$traced, {
|
|
123
|
+
arguments,
|
|
124
|
+
self: this,
|
|
125
|
+
moduleVersion: "1.0.0"
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
`).body[0].body // Extract only block statement of function body.
|
|
129
|
+
|
|
130
|
+
// Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
|
|
131
|
+
query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
|
|
132
|
+
|
|
133
|
+
return wrapper
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function wrapCallback (state, node) {
|
|
137
|
+
const { channelName, functionQuery: { index = -1 } } = state
|
|
138
|
+
const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
|
|
139
|
+
const wrapper = parse(`
|
|
140
|
+
function wrapper () {
|
|
141
|
+
const __apm$cb = Array.prototype.at.call(arguments, ${index});
|
|
142
|
+
const __apm$ctx = {
|
|
128
143
|
arguments,
|
|
129
144
|
self: this,
|
|
130
145
|
moduleVersion: "1.0.0"
|
|
131
|
-
}
|
|
146
|
+
};
|
|
147
|
+
const __apm$traced = () => {
|
|
148
|
+
const __apm$wrapped = () => {};
|
|
149
|
+
return __apm$wrapped.apply(this, arguments);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (!${channelVariable}.start.hasSubscribers) return __apm$traced();
|
|
153
|
+
|
|
154
|
+
function __apm$wrappedCb(err, res) {
|
|
155
|
+
if (err) {
|
|
156
|
+
__apm$ctx.error = err;
|
|
157
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
158
|
+
} else {
|
|
159
|
+
__apm$ctx.result = res;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
${channelVariable}.asyncStart.runStores(__apm$ctx, () => {
|
|
163
|
+
try {
|
|
164
|
+
if (__apm$cb) {
|
|
165
|
+
return __apm$cb.apply(this, arguments);
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
${channelVariable}.asyncEnd.publish(__apm$ctx);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (typeof __apm$cb !== 'function') {
|
|
174
|
+
return __apm$traced();
|
|
175
|
+
}
|
|
176
|
+
Array.prototype.splice.call(arguments, ${index}, 1, __apm$wrappedCb);
|
|
177
|
+
|
|
178
|
+
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
179
|
+
try {
|
|
180
|
+
return __apm$traced();
|
|
181
|
+
} catch (err) {
|
|
182
|
+
__apm$ctx.error = err;
|
|
183
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
184
|
+
throw err;
|
|
185
|
+
} finally {
|
|
186
|
+
${channelVariable}.end.publish(__apm$ctx);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
132
189
|
}
|
|
133
190
|
`).body[0].body // Extract only block statement of function body.
|
|
134
191
|
|
|
135
192
|
// Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
|
|
136
|
-
wrapper
|
|
193
|
+
query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
|
|
137
194
|
|
|
138
195
|
return wrapper
|
|
139
196
|
}
|
|
@@ -91,6 +91,7 @@ const wrappedWorkers = new WeakSet()
|
|
|
91
91
|
const testSuiteMockedFiles = new Map()
|
|
92
92
|
const testsToBeRetried = new Set()
|
|
93
93
|
const testSuiteAbsolutePathsWithFastCheck = new Set()
|
|
94
|
+
const testSuiteJestObjects = new Map()
|
|
94
95
|
|
|
95
96
|
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
96
97
|
const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
|
|
@@ -237,6 +238,28 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Jest mock state issue during test retries
|
|
243
|
+
*
|
|
244
|
+
* Problem:
|
|
245
|
+
* - Jest tracks mock function calls using internal state (call count, call arguments, etc.)
|
|
246
|
+
* - When a test is retried, the mock state is not automatically reset
|
|
247
|
+
* - This causes assertions like `toHaveBeenCalledTimes(1)` to fail because the call count
|
|
248
|
+
* accumulates across retries
|
|
249
|
+
*
|
|
250
|
+
* The solution is to clear all mocks before each retry attempt.
|
|
251
|
+
*/
|
|
252
|
+
resetMockState () {
|
|
253
|
+
try {
|
|
254
|
+
const jestObject = testSuiteJestObjects.get(this.testSuiteAbsolutePath)
|
|
255
|
+
if (jestObject?.clearAllMocks) {
|
|
256
|
+
jestObject.clearAllMocks()
|
|
257
|
+
}
|
|
258
|
+
} catch (e) {
|
|
259
|
+
log.warn('Error resetting mock state', e)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
240
263
|
// This function returns an array if the known tests are valid and null otherwise.
|
|
241
264
|
getKnownTestsForSuite (suiteKnownTests) {
|
|
242
265
|
// `suiteKnownTests` is `this.testEnvironmentOptions._ddKnownTests`,
|
|
@@ -339,8 +362,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
339
362
|
if (event.name === 'test_start') {
|
|
340
363
|
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
|
|
341
364
|
if (testsToBeRetried.has(testName)) {
|
|
342
|
-
// This is needed because we're
|
|
365
|
+
// This is needed because we're retrying tests with the same name
|
|
343
366
|
this.resetSnapshotState()
|
|
367
|
+
this.resetMockState()
|
|
344
368
|
}
|
|
345
369
|
|
|
346
370
|
let isNewTest = false
|
|
@@ -864,7 +888,6 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
864
888
|
|
|
865
889
|
const {
|
|
866
890
|
results: {
|
|
867
|
-
success,
|
|
868
891
|
coverageMap,
|
|
869
892
|
numFailedTestSuites,
|
|
870
893
|
numFailedTests,
|
|
@@ -883,53 +906,6 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
883
906
|
// ignore errors
|
|
884
907
|
}
|
|
885
908
|
}
|
|
886
|
-
let status, error
|
|
887
|
-
|
|
888
|
-
if (success) {
|
|
889
|
-
status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
|
|
890
|
-
} else {
|
|
891
|
-
status = 'fail'
|
|
892
|
-
error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
|
|
893
|
-
}
|
|
894
|
-
let timeoutId
|
|
895
|
-
|
|
896
|
-
// Pass the resolve callback to defer it to DC listener
|
|
897
|
-
const flushPromise = new Promise((resolve) => {
|
|
898
|
-
onDone = () => {
|
|
899
|
-
clearTimeout(timeoutId)
|
|
900
|
-
resolve()
|
|
901
|
-
}
|
|
902
|
-
})
|
|
903
|
-
|
|
904
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
905
|
-
timeoutId = setTimeout(() => {
|
|
906
|
-
resolve('timeout')
|
|
907
|
-
}, FLUSH_TIMEOUT).unref()
|
|
908
|
-
})
|
|
909
|
-
|
|
910
|
-
testSessionFinishCh.publish({
|
|
911
|
-
status,
|
|
912
|
-
isSuitesSkipped,
|
|
913
|
-
isSuitesSkippingEnabled,
|
|
914
|
-
isCodeCoverageEnabled,
|
|
915
|
-
testCodeCoverageLinesTotal,
|
|
916
|
-
numSkippedSuites,
|
|
917
|
-
hasUnskippableSuites,
|
|
918
|
-
hasForcedToRunSuites,
|
|
919
|
-
error,
|
|
920
|
-
isEarlyFlakeDetectionEnabled,
|
|
921
|
-
isEarlyFlakeDetectionFaulty,
|
|
922
|
-
isTestManagementTestsEnabled,
|
|
923
|
-
onDone
|
|
924
|
-
})
|
|
925
|
-
|
|
926
|
-
const waitingResult = await Promise.race([flushPromise, timeoutPromise])
|
|
927
|
-
|
|
928
|
-
if (waitingResult === 'timeout') {
|
|
929
|
-
log.error('Timeout waiting for the tracer to flush')
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
numSkippedSuites = 0
|
|
933
909
|
|
|
934
910
|
/**
|
|
935
911
|
* If Early Flake Detection (EFD) is enabled the logic is as follows:
|
|
@@ -938,7 +914,6 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
938
914
|
* The rationale behind is the following: you may still be able to block your CI pipeline by gating
|
|
939
915
|
* on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
|
|
940
916
|
*/
|
|
941
|
-
|
|
942
917
|
if (isEarlyFlakeDetectionEnabled) {
|
|
943
918
|
let numFailedTestsToIgnore = 0
|
|
944
919
|
for (const testStatuses of newTestsTestStatuses.values()) {
|
|
@@ -996,6 +971,55 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
996
971
|
}
|
|
997
972
|
}
|
|
998
973
|
|
|
974
|
+
// Determine session status after EFD and quarantine checks have potentially modified success
|
|
975
|
+
let status, error
|
|
976
|
+
if (result.results.success) {
|
|
977
|
+
status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
|
|
978
|
+
} else {
|
|
979
|
+
status = 'fail'
|
|
980
|
+
error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
let timeoutId
|
|
984
|
+
|
|
985
|
+
// Pass the resolve callback to defer it to DC listener
|
|
986
|
+
const flushPromise = new Promise((resolve) => {
|
|
987
|
+
onDone = () => {
|
|
988
|
+
clearTimeout(timeoutId)
|
|
989
|
+
resolve()
|
|
990
|
+
}
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
994
|
+
timeoutId = setTimeout(() => {
|
|
995
|
+
resolve('timeout')
|
|
996
|
+
}, FLUSH_TIMEOUT).unref()
|
|
997
|
+
})
|
|
998
|
+
|
|
999
|
+
testSessionFinishCh.publish({
|
|
1000
|
+
status,
|
|
1001
|
+
isSuitesSkipped,
|
|
1002
|
+
isSuitesSkippingEnabled,
|
|
1003
|
+
isCodeCoverageEnabled,
|
|
1004
|
+
testCodeCoverageLinesTotal,
|
|
1005
|
+
numSkippedSuites,
|
|
1006
|
+
hasUnskippableSuites,
|
|
1007
|
+
hasForcedToRunSuites,
|
|
1008
|
+
error,
|
|
1009
|
+
isEarlyFlakeDetectionEnabled,
|
|
1010
|
+
isEarlyFlakeDetectionFaulty,
|
|
1011
|
+
isTestManagementTestsEnabled,
|
|
1012
|
+
onDone
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
const waitingResult = await Promise.race([flushPromise, timeoutPromise])
|
|
1016
|
+
|
|
1017
|
+
if (waitingResult === 'timeout') {
|
|
1018
|
+
log.error('Timeout waiting for the tracer to flush')
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
numSkippedSuites = 0
|
|
1022
|
+
|
|
999
1023
|
return result
|
|
1000
1024
|
}, {
|
|
1001
1025
|
replaceGetter: true
|
|
@@ -1148,9 +1172,19 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
|
|
|
1148
1172
|
})
|
|
1149
1173
|
}
|
|
1150
1174
|
testSuiteFinishCh.publish({ status, errorMessage, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
|
|
1175
|
+
|
|
1176
|
+
// Cleanup per-suite state to avoid memory leaks
|
|
1177
|
+
testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
|
|
1178
|
+
testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
|
|
1179
|
+
|
|
1151
1180
|
return suiteResults
|
|
1152
1181
|
}).catch(error => {
|
|
1153
1182
|
testSuiteFinishCh.publish({ status: 'fail', error, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
|
|
1183
|
+
|
|
1184
|
+
// Cleanup per-suite state to avoid memory leaks
|
|
1185
|
+
testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
|
|
1186
|
+
testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
|
|
1187
|
+
|
|
1154
1188
|
throw error
|
|
1155
1189
|
})
|
|
1156
1190
|
})
|
|
@@ -1312,6 +1346,11 @@ addHook({
|
|
|
1312
1346
|
const result = _createJestObjectFor.apply(this, arguments)
|
|
1313
1347
|
const suiteFilePath = this._testPath || from
|
|
1314
1348
|
|
|
1349
|
+
// Store the jest object so we can access it later for resetting mock state
|
|
1350
|
+
if (suiteFilePath) {
|
|
1351
|
+
testSuiteJestObjects.set(suiteFilePath, result)
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1315
1354
|
shimmer.wrap(result, 'mock', mock => function (moduleName) {
|
|
1316
1355
|
// If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
|
|
1317
1356
|
if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
|
|
@@ -484,10 +484,11 @@ function dispatcherRunWrapper (run) {
|
|
|
484
484
|
|
|
485
485
|
function dispatcherRunWrapperNew (run) {
|
|
486
486
|
return function (testGroups) {
|
|
487
|
-
// Filter out disabled tests from testGroups before they get scheduled
|
|
487
|
+
// Filter out disabled tests from testGroups before they get scheduled,
|
|
488
|
+
// unless they have attemptToFix (in which case they should still run and be retried)
|
|
488
489
|
if (isTestManagementTestsEnabled) {
|
|
489
490
|
testGroups.forEach(group => {
|
|
490
|
-
group.tests = group.tests.filter(test => !test._ddIsDisabled)
|
|
491
|
+
group.tests = group.tests.filter(test => !test._ddIsDisabled || test._ddIsAttemptToFix)
|
|
491
492
|
})
|
|
492
493
|
// Remove empty groups
|
|
493
494
|
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
@@ -911,14 +912,16 @@ addHook({
|
|
|
911
912
|
const fileSuitesWithManagedTestsToProjects = new Map()
|
|
912
913
|
for (const test of allTests) {
|
|
913
914
|
const testProperties = getTestProperties(test)
|
|
914
|
-
// Disabled tests are skipped
|
|
915
|
+
// Disabled tests are skipped unless they have attemptToFix
|
|
915
916
|
if (testProperties.disabled) {
|
|
916
917
|
test._ddIsDisabled = true
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
918
|
+
if (!testProperties.attemptToFix) {
|
|
919
|
+
test.expectedStatus = 'skipped'
|
|
920
|
+
// setting test.expectedStatus to 'skipped' does not work for every case,
|
|
921
|
+
// so we need to filter out disabled tests in dispatcherRunWrapperNew,
|
|
922
|
+
// so they don't get to the workers
|
|
923
|
+
continue
|
|
924
|
+
}
|
|
922
925
|
}
|
|
923
926
|
if (testProperties.quarantined) {
|
|
924
927
|
test._ddIsQuarantined = true
|
|
@@ -947,6 +950,7 @@ addHook({
|
|
|
947
950
|
(test) => test._ddIsAttemptToFix,
|
|
948
951
|
[
|
|
949
952
|
(test) => test._ddIsQuarantined && '_ddIsQuarantined',
|
|
953
|
+
(test) => test._ddIsDisabled && '_ddIsDisabled',
|
|
950
954
|
'_ddIsAttemptToFix',
|
|
951
955
|
'_ddIsAttemptToFixRetry'
|
|
952
956
|
],
|
|
@@ -5,48 +5,48 @@ const { storage } = require('../../datadog-core')
|
|
|
5
5
|
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
6
6
|
|
|
7
7
|
const {
|
|
8
|
-
|
|
9
|
-
TEST_STATUS,
|
|
10
|
-
TEST_SOURCE_START,
|
|
8
|
+
addIntelligentTestRunnerSpanTags,
|
|
11
9
|
finishAllTraceSpans,
|
|
12
|
-
|
|
10
|
+
getTestEndLine,
|
|
13
11
|
getTestSuiteCommonTags,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
TEST_CODE_OWNERS,
|
|
12
|
+
getTestSuitePath,
|
|
13
|
+
isModifiedTest,
|
|
14
|
+
CUCUMBER_IS_PARALLEL,
|
|
18
15
|
ITR_CORRELATION_ID,
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
TEST_BROWSER_DRIVER,
|
|
17
|
+
TEST_CODE_OWNERS,
|
|
21
18
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
19
|
+
TEST_EARLY_FLAKE_ENABLED,
|
|
20
|
+
TEST_HAS_FAILED_ALL_RETRIES,
|
|
21
|
+
TEST_IS_MODIFIED,
|
|
22
22
|
TEST_IS_NEW,
|
|
23
23
|
TEST_IS_RETRY,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
TEST_IS_RUM_ACTIVE,
|
|
25
|
+
TEST_ITR_FORCED_RUN,
|
|
26
|
+
TEST_ITR_UNSKIPPABLE,
|
|
27
|
+
TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
|
|
26
28
|
TEST_MANAGEMENT_ENABLED,
|
|
27
|
-
TEST_MANAGEMENT_IS_QUARANTINED,
|
|
28
|
-
TEST_MANAGEMENT_IS_DISABLED,
|
|
29
29
|
TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
TEST_MANAGEMENT_IS_DISABLED,
|
|
31
|
+
TEST_MANAGEMENT_IS_QUARANTINED,
|
|
32
32
|
TEST_RETRY_REASON_TYPES,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
TEST_RETRY_REASON,
|
|
34
|
+
TEST_SKIP_REASON,
|
|
35
|
+
TEST_SOURCE_FILE,
|
|
36
|
+
TEST_SOURCE_START,
|
|
37
|
+
TEST_STATUS,
|
|
36
38
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
37
39
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
38
40
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
39
41
|
const {
|
|
42
|
+
TELEMETRY_CODE_COVERAGE_EMPTY,
|
|
43
|
+
TELEMETRY_CODE_COVERAGE_FINISHED,
|
|
44
|
+
TELEMETRY_CODE_COVERAGE_NUM_FILES,
|
|
45
|
+
TELEMETRY_CODE_COVERAGE_STARTED,
|
|
40
46
|
TELEMETRY_EVENT_CREATED,
|
|
41
47
|
TELEMETRY_EVENT_FINISHED,
|
|
42
|
-
TELEMETRY_CODE_COVERAGE_STARTED,
|
|
43
|
-
TELEMETRY_CODE_COVERAGE_FINISHED,
|
|
44
48
|
TELEMETRY_ITR_FORCED_TO_RUN,
|
|
45
|
-
TELEMETRY_CODE_COVERAGE_EMPTY,
|
|
46
49
|
TELEMETRY_ITR_UNSKIPPABLE,
|
|
47
|
-
TELEMETRY_CODE_COVERAGE_NUM_FILES,
|
|
48
|
-
TEST_IS_RUM_ACTIVE,
|
|
49
|
-
TEST_BROWSER_DRIVER,
|
|
50
50
|
TELEMETRY_TEST_SESSION
|
|
51
51
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
52
52
|
|
|
@@ -362,18 +362,19 @@ class CucumberPlugin extends CiPlugin {
|
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
const spanTags = span.context()._tags
|
|
366
|
+
const telemetryTags = {
|
|
367
|
+
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
|
|
368
|
+
isNew,
|
|
369
|
+
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
|
|
370
|
+
browserDriver: spanTags[TEST_BROWSER_DRIVER]
|
|
371
|
+
}
|
|
365
372
|
span.finish()
|
|
366
373
|
if (!isStep) {
|
|
367
|
-
const spanTags = span.context()._tags
|
|
368
374
|
this.telemetry.ciVisEvent(
|
|
369
375
|
TELEMETRY_EVENT_FINISHED,
|
|
370
376
|
'test',
|
|
371
|
-
|
|
372
|
-
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
|
|
373
|
-
isNew,
|
|
374
|
-
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
|
|
375
|
-
browserDriver: spanTags[TEST_BROWSER_DRIVER]
|
|
376
|
-
}
|
|
377
|
+
telemetryTags
|
|
377
378
|
)
|
|
378
379
|
finishAllTraceSpans(span)
|
|
379
380
|
// If it's a worker, flushing is cheap, as it's just sending data to the main process
|