dd-trace 5.89.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.
Files changed (52) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/index.d.ts +31 -0
  3. package/package.json +8 -8
  4. package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
  5. package/packages/datadog-instrumentations/src/cucumber.js +40 -1
  6. package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/jest.js +22 -0
  9. package/packages/datadog-instrumentations/src/mocha/main.js +10 -4
  10. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  11. package/packages/datadog-instrumentations/src/mocha/worker.js +10 -2
  12. package/packages/datadog-instrumentations/src/playwright.js +20 -2
  13. package/packages/datadog-instrumentations/src/prisma.js +4 -2
  14. package/packages/datadog-instrumentations/src/vitest.js +16 -0
  15. package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
  16. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
  17. package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
  18. package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
  19. package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
  20. package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
  21. package/packages/datadog-plugin-apollo/src/index.js +28 -0
  22. package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
  23. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +11 -1
  24. package/packages/datadog-plugin-jest/src/index.js +6 -0
  25. package/packages/datadog-plugin-playwright/src/index.js +35 -8
  26. package/packages/dd-trace/src/aiguard/noop.js +1 -1
  27. package/packages/dd-trace/src/aiguard/sdk.js +14 -5
  28. package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
  29. package/packages/dd-trace/src/appsec/index.js +11 -1
  30. package/packages/dd-trace/src/appsec/reporter.js +28 -11
  31. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  32. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  33. package/packages/dd-trace/src/config/index.js +1 -0
  34. package/packages/dd-trace/src/config/supported-configurations.json +7 -0
  35. package/packages/dd-trace/src/constants.js +1 -0
  36. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
  37. package/packages/dd-trace/src/encode/agentless-json.js +67 -22
  38. package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
  39. package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
  40. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  41. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
  42. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  43. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
  44. package/packages/dd-trace/src/plugins/apollo.js +7 -2
  45. package/packages/dd-trace/src/plugins/index.js +1 -0
  46. package/packages/dd-trace/src/plugins/util/ci.js +95 -3
  47. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
  48. package/packages/dd-trace/src/plugins/util/web.js +31 -11
  49. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
  50. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
  51. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
  52. package/packages/dd-trace/src/standalone/product.js +2 -1
@@ -61,7 +61,6 @@
61
61
  "crypto-randomuuid","npm:crypto-randomuuid","['MIT']","['Stephen Belanger']"
62
62
  "dc-polyfill","https://github.com/DataDog/dc-polyfill","['MIT']","['Thomas Hunter II']"
63
63
  "dd-trace","https://github.com/DataDog/dd-trace-js","['(Apache-2.0 OR BSD-3-Clause)']","['Datadog Inc. <info@datadoghq.com>']"
64
- "delay","https://github.com/sindresorhus/delay","['MIT']","['Sindre Sorhus']"
65
64
  "detect-newline","https://github.com/sindresorhus/detect-newline","['MIT']","['Sindre Sorhus']"
66
65
  "escape-string-regexp","https://github.com/sindresorhus/escape-string-regexp","['MIT']","['Sindre Sorhus']"
67
66
  "esquery","https://github.com/estools/esquery","['BSD-3-Clause']","['Joel Feenstra']"
@@ -85,7 +84,6 @@
85
84
  "node-gyp-build","https://github.com/prebuild/node-gyp-build","['MIT']","['Mathias Buus']"
86
85
  "opentracing","https://github.com/opentracing/opentracing-javascript","['Apache-2.0']","['opentracing']"
87
86
  "oxc-parser","https://github.com/oxc-project/oxc","['MIT']","['Boshen and oxc contributors']"
88
- "p-limit","https://github.com/sindresorhus/p-limit","['MIT']","['Sindre Sorhus']"
89
87
  "path-to-regexp","https://github.com/pillarjs/path-to-regexp","['MIT']","['pillarjs']"
90
88
  "pprof-format","https://github.com/DataDog/pprof-format","['MIT']","['Datadog Inc.']"
91
89
  "protobufjs","https://github.com/protobufjs/protobuf.js","['BSD-3-Clause']","['Daniel Wirtz']"
@@ -100,6 +98,5 @@
100
98
  "tslib","https://github.com/microsoft/tslib","['0BSD']","['Microsoft Corp.']"
101
99
  "ttl-set","https://github.com/watson/ttl-set","['MIT']","['Thomas Watson']"
102
100
  "undici-types","https://github.com/nodejs/undici","['MIT']","['nodejs']"
103
- "yocto-queue","https://github.com/sindresorhus/yocto-queue","['MIT']","['Sindre Sorhus']"
104
101
  "aws-lambda-nodejs-runtime-interface-client","https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/v2.1.0/src/utils/UserFunction.ts","['Apache-2.0']","['Amazon.com Inc. or its affiliates']"
105
102
  "is-git-url","https://github.com/jonschlinkert/is-git-url/blob/396965ffabf2f46656c8af4c47bef1d69f09292e/index.js#L9C15-L9C87","['MIT']","['Jon Schlinkert']"
package/index.d.ts CHANGED
@@ -228,6 +228,7 @@ interface Plugins {
228
228
  "azure-event-hubs": tracer.plugins.azure_event_hubs;
229
229
  "azure-functions": tracer.plugins.azure_functions;
230
230
  "azure-service-bus": tracer.plugins.azure_service_bus;
231
+ "azure-durable-functions": tracer.plugins.azure_durable_functions
231
232
  "bullmq": tracer.plugins.bullmq;
232
233
  "bunyan": tracer.plugins.bunyan;
233
234
  "cassandra-driver": tracer.plugins.cassandra_driver;
@@ -1626,6 +1627,10 @@ declare namespace tracer {
1626
1627
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1627
1628
  */
1628
1629
  tags: string[];
1630
+ /**
1631
+ * Sensitive Data Scanner findings from the evaluation.
1632
+ */
1633
+ sds: Object[];
1629
1634
  }
1630
1635
 
1631
1636
  /**
@@ -1641,6 +1646,10 @@ declare namespace tracer {
1641
1646
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1642
1647
  */
1643
1648
  tags: string[];
1649
+ /**
1650
+ * Sensitive Data Scanner findings from the evaluation.
1651
+ */
1652
+ sds: Object[];
1644
1653
  }
1645
1654
 
1646
1655
  /**
@@ -2034,6 +2043,22 @@ declare namespace tracer {
2034
2043
  * @default true
2035
2044
  */
2036
2045
  signature?: boolean;
2046
+
2047
+ /**
2048
+ * An object of optional callbacks to be executed during the respective
2049
+ * phase of an Apollo Gateway operation. Undefined callbacks default to a
2050
+ * noop function.
2051
+ *
2052
+ * @default {}
2053
+ */
2054
+ hooks?: {
2055
+ request?: (span?: Span, ctx?: any) => void;
2056
+ validate?: (span?: Span, ctx?: any) => void;
2057
+ plan?: (span?: Span, ctx?: any) => void;
2058
+ execute?: (span?: Span, ctx?: any) => void;
2059
+ fetch?: (span?: Span, ctx?: any) => void;
2060
+ postprocessing?: (span?: Span, ctx?: any) => void;
2061
+ };
2037
2062
  }
2038
2063
 
2039
2064
  /**
@@ -2097,6 +2122,12 @@ declare namespace tracer {
2097
2122
  */
2098
2123
  interface azure_service_bus extends Integration {}
2099
2124
 
2125
+ /**
2126
+ * This plugin automatically instruments the
2127
+ * durable-functions module
2128
+ */
2129
+ interface azure_durable_functions extends Integration {}
2130
+
2100
2131
  /**
2101
2132
  * This plugin patches the [bunyan](https://github.com/trentm/node-bunyan)
2102
2133
  * to automatically inject trace identifiers in log records when the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.89.0",
3
+ "version": "5.90.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -142,11 +142,11 @@
142
142
  "@datadog/native-iast-taint-tracking": "4.1.0",
143
143
  "@datadog/native-metrics": "3.1.1",
144
144
  "@datadog/openfeature-node-server": "^1.1.0",
145
- "@datadog/pprof": "5.13.4",
145
+ "@datadog/pprof": "5.13.5",
146
146
  "@datadog/wasm-js-rewriter": "5.0.1",
147
147
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
148
148
  "@opentelemetry/api-logs": "<1.0.0",
149
- "oxc-parser": "^0.115.0"
149
+ "oxc-parser": "^0.116.0"
150
150
  },
151
151
  "devDependencies": {
152
152
  "@actions/core": "^3.0.0",
@@ -157,7 +157,7 @@
157
157
  "@msgpack/msgpack": "^3.1.3",
158
158
  "@openfeature/core": "^1.8.1",
159
159
  "@openfeature/server-sdk": "~1.20.0",
160
- "@stylistic/eslint-plugin": "^5.7.1",
160
+ "@stylistic/eslint-plugin": "^5.10.0",
161
161
  "@types/mocha": "^10.0.10",
162
162
  "@types/node": "^18.19.106",
163
163
  "@types/sinon": "^21.0.0",
@@ -165,7 +165,7 @@
165
165
  "benchmark": "^2.1.4",
166
166
  "body-parser": "^2.2.2",
167
167
  "bun": "1.3.10",
168
- "codeowners-audit": "^2.0.0",
168
+ "codeowners-audit": "^2.7.1",
169
169
  "eslint": "^9.39.2",
170
170
  "eslint-plugin-cypress": "^6.1.0",
171
171
  "eslint-plugin-import": "^2.32.0",
@@ -183,9 +183,9 @@
183
183
  "mocha": "^11.6.0",
184
184
  "mocha-junit-reporter": "^2.2.1",
185
185
  "mocha-multi-reporters": "^1.5.1",
186
- "multer": "^2.0.2",
186
+ "multer": "^2.1.1",
187
187
  "nock": "^13.5.6",
188
- "nyc": "^17.1.0",
188
+ "nyc": "^18.0.0",
189
189
  "octokit": "^5.0.3",
190
190
  "opentracing": ">=0.14.7",
191
191
  "p-limit": "^7.2.0",
@@ -193,7 +193,7 @@
193
193
  "retry": "^0.13.1",
194
194
  "semifies": "^1.0.0",
195
195
  "semver": "^7.7.2",
196
- "sinon": "^21.0.1",
196
+ "sinon": "^21.0.2",
197
197
  "tiktoken": "^1.0.21",
198
198
  "typescript": "^5.9.2",
199
199
  "workerpool": "^10.0.0",
@@ -0,0 +1,75 @@
1
+ 'use strict'
2
+
3
+ const dc = require('dc-polyfill')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const {
7
+ addHook,
8
+ } = require('./helpers/instrument')
9
+
10
+ /**
11
+ * @type {import('diagnostics_channel').TracingChannel}
12
+ */
13
+ const azureDurableFunctionsChannel = dc.tracingChannel('datadog:azure:durable-functions:invoke')
14
+
15
+ addHook({ name: 'durable-functions', versions: ['>=3'], patchDefault: false }, (df) => {
16
+ const { app } = df
17
+
18
+ shimmer.wrap(app, 'entity', entityWrapper)
19
+ shimmer.wrap(app, 'activity', activityHandler)
20
+
21
+ return df
22
+ })
23
+
24
+ function entityWrapper (method) {
25
+ return function (entityName, arg) {
26
+ // because this method is overloaded, the second argument can either be an object
27
+ // with the handler or the handler itself, so first we figure which type it is
28
+ if (typeof arg === 'function') {
29
+ // if a function, this is the handler we want to wrap and trace
30
+ arguments[1] = shimmer.wrapFunction(arg, handler => entityHandler(handler, entityName))
31
+ } else {
32
+ // if an object, access the handler then trace it
33
+ shimmer.wrap(arg, 'handler', handler => entityHandler(handler, entityName))
34
+ }
35
+
36
+ return method.apply(this, arguments)
37
+ }
38
+ }
39
+
40
+ function entityHandler (handler, entityName) {
41
+ return function () {
42
+ if (!azureDurableFunctionsChannel.hasSubscribers) return handler.apply(this, arguments)
43
+
44
+ const entityContext = arguments[0]
45
+ return azureDurableFunctionsChannel.traceSync(
46
+ handler,
47
+ { trigger: 'Entity', functionName: entityName, operationName: entityContext?.df?.operationName },
48
+ this, ...arguments)
49
+ }
50
+ }
51
+
52
+ function activityHandler (method) {
53
+ return function (activityName, activityOptions) {
54
+ shimmer.wrap(activityOptions, 'handler', handler => {
55
+ const isAsync =
56
+ handler && handler.constructor && handler.constructor.name === 'AsyncFunction'
57
+
58
+ return function () {
59
+ if (!azureDurableFunctionsChannel.hasSubscribers) return handler.apply(this, arguments)
60
+
61
+ // use tracePromise if this is an async handler. otherwise, use traceSync
62
+ return isAsync
63
+ ? azureDurableFunctionsChannel.tracePromise(
64
+ handler,
65
+ { trigger: 'Activity', functionName: activityName },
66
+ this, ...arguments)
67
+ : azureDurableFunctionsChannel.traceSync(
68
+ handler,
69
+ { trigger: 'Activity', functionName: activityName },
70
+ this, ...arguments)
71
+ }
72
+ })
73
+ return method.apply(this, arguments)
74
+ }
75
+ }
@@ -55,6 +55,8 @@ const originalCoverageMap = createCoverageMap()
55
55
  const patched = new WeakSet()
56
56
 
57
57
  const lastStatusByPickleId = new Map()
58
+ /** For ATR: statuses keyed by stable scenario id (uri:name) so retries accumulate correctly */
59
+ const atrStatusesByScenarioKey = new Map()
58
60
  const numRetriesByPickleId = new Map()
59
61
  const numAttemptToCtx = new Map()
60
62
  const newTestsByTestFullname = new Map()
@@ -275,6 +277,17 @@ function wrapRun (pl, isLatestVersion, version) {
275
277
  const isFirstAttempt = numAttempt++ === 0
276
278
  const isAtrRetry = !isFirstAttempt && isFlakyTestRetriesEnabled
277
279
 
280
+ // ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
281
+ if (isFlakyTestRetriesEnabled && isAtrRetry === false) {
282
+ const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
283
+ const atrKey = `${this.pickle.uri}:${nameForKey}`
284
+ if (atrStatusesByScenarioKey.has(atrKey)) {
285
+ atrStatusesByScenarioKey.get(atrKey).push('fail')
286
+ } else {
287
+ atrStatusesByScenarioKey.set(atrKey, ['fail'])
288
+ }
289
+ }
290
+
278
291
  if (promises.hitBreakpointPromise) {
279
292
  await promises.hitBreakpointPromise
280
293
  }
@@ -367,6 +380,25 @@ function wrapRun (pl, isLatestVersion, version) {
367
380
  }
368
381
  }
369
382
 
383
+ // ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
384
+ // Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
385
+ if (isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
386
+ const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
387
+ const atrKey = `${this.pickle.uri}:${nameForKey}`
388
+ if (atrStatusesByScenarioKey.has(atrKey)) {
389
+ atrStatusesByScenarioKey.get(atrKey).push(status)
390
+ } else {
391
+ atrStatusesByScenarioKey.set(atrKey, [status])
392
+ }
393
+ const atrStatuses = atrStatusesByScenarioKey.get(atrKey)
394
+ const pickleStatuses = lastStatusByPickleId.get(this.pickle.id)
395
+ const statusesToCheck = atrStatuses?.length >= (numTestRetries + 1) ? atrStatuses : pickleStatuses
396
+ if (statusesToCheck && statusesToCheck.length === numTestRetries + 1 &&
397
+ statusesToCheck.every(s => s === 'fail')) {
398
+ hasFailedAllRetries = true
399
+ }
400
+ }
401
+
370
402
  const attemptCtx = numAttemptToCtx.get(numAttempt)
371
403
 
372
404
  const error = getErrorFromCucumberResult(result)
@@ -480,7 +512,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
480
512
  earlyFlakeDetectionFaultyThreshold = configurationResponse.libraryConfig?.earlyFlakeDetectionFaultyThreshold
481
513
  isSuitesSkippingEnabled = configurationResponse.libraryConfig?.isSuitesSkippingEnabled
482
514
  isFlakyTestRetriesEnabled = configurationResponse.libraryConfig?.isFlakyTestRetriesEnabled
483
- numTestRetries = configurationResponse.libraryConfig?.flakyTestRetriesCount
515
+ const configRetryCount = configurationResponse.libraryConfig?.flakyTestRetriesCount
516
+ numTestRetries = (typeof configRetryCount === 'number' && configRetryCount > 0) ? configRetryCount : 0
484
517
  isKnownTestsEnabled = configurationResponse.libraryConfig?.isKnownTestsEnabled
485
518
  isTestManagementTestsEnabled = configurationResponse.libraryConfig?.isTestManagementEnabled
486
519
  testManagementAttemptToFixRetries = configurationResponse.libraryConfig?.testManagementAttemptToFixRetries
@@ -563,6 +596,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
563
596
  options.retry = numTestRetries
564
597
  }
565
598
 
599
+ atrStatusesByScenarioKey.clear()
566
600
  sessionStartCh.publish({ command, frameworkVersion })
567
601
 
568
602
  if (!errorSkippableRequest && skippedSuites.length) {
@@ -1016,6 +1050,9 @@ addHook({
1016
1050
  this.options.worldParameters._ddModifiedFiles = modifiedFiles
1017
1051
  }
1018
1052
 
1053
+ this.options.worldParameters._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
1054
+ this.options.worldParameters._ddNumTestRetries = numTestRetries
1055
+
1019
1056
  return startWorker.apply(this, arguments)
1020
1057
  })
1021
1058
  return adapterPackage
@@ -1051,6 +1088,8 @@ addHook({
1051
1088
  if (isImpactedTestsEnabled) {
1052
1089
  modifiedFiles = this.options.worldParameters._ddModifiedFiles
1053
1090
  }
1091
+ isFlakyTestRetriesEnabled = !!this.options.worldParameters._ddIsFlakyTestRetriesEnabled
1092
+ numTestRetries = this.options.worldParameters._ddNumTestRetries ?? 0
1054
1093
  }
1055
1094
  )
1056
1095
  return workerPackage
@@ -6,12 +6,21 @@ const {
6
6
  addHook,
7
7
  } = require('./helpers/instrument')
8
8
 
9
- addHook({ name: '@elastic/transport', file: 'lib/Transport.js', versions: ['>=8'] }, (exports) => {
10
- shimmer.wrap(exports.default.prototype, 'request', createWrapRequest('elasticsearch'))
11
- shimmer.wrap(exports.default.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
9
+ addHook({ name: '@elastic/transport', file: 'lib/Transport.js', versions: ['>=8 <9'] }, (exports) => {
10
+ wrapTransportPrototype(exports.default)
12
11
  return exports
13
12
  })
14
13
 
14
+ addHook({ name: '@elastic/transport', versions: ['>=9'] }, (exports) => {
15
+ wrapTransportPrototype(exports.Transport)
16
+ return exports
17
+ })
18
+
19
+ function wrapTransportPrototype (Transport) {
20
+ shimmer.wrap(Transport.prototype, 'request', createWrapRequest('elasticsearch'))
21
+ shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
22
+ }
23
+
15
24
  addHook({ name: '@elastic/elasticsearch', file: 'lib/Transport.js', versions: ['>=5.6.16 <8', '>=8'] }, Transport => {
16
25
  shimmer.wrap(Transport.prototype, 'request', createWrapRequest('elasticsearch'))
17
26
  shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
@@ -8,6 +8,7 @@ module.exports = {
8
8
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
9
9
  '@azure/event-hubs': () => require('../azure-event-hubs'),
10
10
  '@azure/functions': () => require('../azure-functions'),
11
+ 'durable-functions': () => require('../azure-durable-functions'),
11
12
  '@azure/service-bus': () => require('../azure-service-bus'),
12
13
  '@cucumber/cucumber': () => require('../cucumber'),
13
14
  '@playwright/test': () => require('../playwright'),
@@ -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')
@@ -505,6 +506,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
505
506
  })
506
507
  }
507
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
+
508
522
  if (event.name === 'add_test') {
509
523
  if (event.failing) {
510
524
  return
@@ -673,6 +687,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
673
687
  }
674
688
  }
675
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
+
676
698
  const promises = {}
677
699
  const numRetries = this.global[RETRY_TIMES]
678
700
  const numTestExecutions = event.test?.invocations
@@ -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 and auto test retries are not supported in parallel mode yet
318
+ // ITR is not supported in parallel mode yet
319
319
  config.isSuitesSkippingEnabled = !isParallel && libraryConfig.isSuitesSkippingEnabled
320
- config.isFlakyTestRetriesEnabled = !isParallel && libraryConfig.isFlakyTestRetriesEnabled
321
- config.flakyTestRetriesCount = !isParallel && libraryConfig.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: { line: testSourceLine },
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
- this._tracingHelper = helper
176
- this._engine.tracingHelper = helper
175
+ if (helper) {
176
+ this._tracingHelper = helper
177
+ this._engine.tracingHelper = helper
178
+ }
177
179
  }
178
180
  }
179
181
  }
@@ -95,6 +95,7 @@ function getProvidedContext () {
95
95
  _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
96
96
  _ddTestManagementTests: testManagementTests,
97
97
  _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
98
+ _ddFlakyTestRetriesCount: flakyTestRetriesCount,
98
99
  _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
99
100
  _ddModifiedFiles: modifiedFiles,
100
101
  } = globalThis.__vitest_worker__.providedContext
@@ -109,6 +110,7 @@ function getProvidedContext () {
109
110
  testManagementAttemptToFixRetries,
110
111
  testManagementTests,
111
112
  isFlakyTestRetriesEnabled,
113
+ flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
112
114
  isImpactedTestsEnabled,
113
115
  modifiedFiles,
114
116
  }
@@ -124,6 +126,7 @@ function getProvidedContext () {
124
126
  testManagementAttemptToFixRetries: 0,
125
127
  testManagementTests: {},
126
128
  isFlakyTestRetriesEnabled: false,
129
+ flakyTestRetriesCount: 0,
127
130
  isImpactedTestsEnabled: false,
128
131
  modifiedFiles: {},
129
132
  }
@@ -260,6 +263,7 @@ function getSortWrapper (sort, frameworkVersion) {
260
263
  ? this.ctx.getCoreWorkspaceProject()
261
264
  : this.ctx.getRootProject()
262
265
  workspaceProject._provided._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
266
+ workspaceProject._provided._ddFlakyTestRetriesCount = flakyTestRetriesCount
263
267
  } catch {
264
268
  log.warn('Could not send library configuration to workers.')
265
269
  }
@@ -657,6 +661,9 @@ function wrapVitestTestRunner (VitestTestRunner) {
657
661
  }
658
662
  task.result.state = 'pass'
659
663
  } else if (isQuarantined) {
664
+ if (task.result.state === 'fail') {
665
+ switchedStatuses.add(task)
666
+ }
660
667
  task.result.state = 'pass'
661
668
  }
662
669
  }
@@ -1047,6 +1054,15 @@ addHook({
1047
1054
  }
1048
1055
  }
1049
1056
 
1057
+ // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
1058
+ if (providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
1059
+ !newTasks.has(task) && !modifiedTasks.has(task)) {
1060
+ const maxRetries = providedContext.flakyTestRetriesCount ?? 0
1061
+ if (maxRetries > 0 && task.result?.retryCount === maxRetries) {
1062
+ hasFailedAllRetries = true
1063
+ }
1064
+ }
1065
+
1050
1066
  if (testCtx) {
1051
1067
  const isRetry = task.result?.retryCount > 0
1052
1068
  // `duration` is the duration of all the retries, so it can't be used if there are retries
@@ -5,6 +5,14 @@ const ApolloBasePlugin = require('../../../dd-trace/src/plugins/apollo')
5
5
  class ApolloGatewayExecutePlugin extends ApolloBasePlugin {
6
6
  static operation = 'execute'
7
7
  static prefix = 'tracing:apm:apollo:gateway:execute'
8
+
9
+ onAsyncStart (ctx) {
10
+ const span = ctx?.currentStore?.span
11
+
12
+ if (!span) return
13
+
14
+ this.config.hooks.execute(span, ctx)
15
+ }
8
16
  }
9
17
 
10
18
  module.exports = ApolloGatewayExecutePlugin
@@ -29,6 +29,11 @@ class ApolloGatewayFetchPlugin extends ApolloBasePlugin {
29
29
 
30
30
  return ctx.currentStore
31
31
  }
32
+
33
+ onAsyncStart (ctx) {
34
+ const span = ctx?.currentStore?.span
35
+ this.config.hooks.fetch(span, ctx)
36
+ }
32
37
  }
33
38
 
34
39
  module.exports = ApolloGatewayFetchPlugin