dd-trace 5.52.0 → 5.53.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 (55) hide show
  1. package/README.md +5 -0
  2. package/index.d.ts +54 -6
  3. package/package.json +1 -1
  4. package/packages/datadog-instrumentations/src/amqplib.js +8 -5
  5. package/packages/datadog-instrumentations/src/child_process.js +2 -1
  6. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +16 -1
  7. package/packages/datadog-instrumentations/src/couchbase.js +2 -1
  8. package/packages/datadog-instrumentations/src/cucumber.js +41 -46
  9. package/packages/datadog-instrumentations/src/express.js +2 -6
  10. package/packages/datadog-instrumentations/src/fs.js +6 -5
  11. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  12. package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
  13. package/packages/datadog-instrumentations/src/http/client.js +2 -1
  14. package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
  15. package/packages/datadog-instrumentations/src/jest.js +49 -41
  16. package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
  17. package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +72 -75
  19. package/packages/datadog-instrumentations/src/mysql2.js +3 -1
  20. package/packages/datadog-instrumentations/src/net.js +3 -1
  21. package/packages/datadog-instrumentations/src/next.js +6 -14
  22. package/packages/datadog-instrumentations/src/pg.js +5 -11
  23. package/packages/datadog-instrumentations/src/playwright.js +60 -69
  24. package/packages/datadog-instrumentations/src/url.js +9 -17
  25. package/packages/datadog-instrumentations/src/vitest.js +55 -75
  26. package/packages/datadog-plugin-cucumber/src/index.js +29 -18
  27. package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
  28. package/packages/datadog-plugin-jest/src/index.js +14 -8
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +8 -5
  30. package/packages/datadog-plugin-mocha/src/index.js +55 -35
  31. package/packages/datadog-plugin-playwright/src/index.js +26 -20
  32. package/packages/datadog-plugin-redis/src/index.js +8 -3
  33. package/packages/datadog-plugin-vitest/src/index.js +53 -42
  34. package/packages/datadog-shimmer/src/shimmer.js +164 -33
  35. package/packages/dd-trace/src/appsec/graphql.js +2 -2
  36. package/packages/dd-trace/src/appsec/index.js +14 -11
  37. package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
  38. package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
  39. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  40. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
  41. package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
  42. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
  43. package/packages/dd-trace/src/config.js +1 -1
  44. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +59 -7
  45. package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
  46. package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
  47. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
  48. package/packages/dd-trace/src/debugger/devtools_client/state.js +21 -1
  49. package/packages/dd-trace/src/dogstatsd.js +2 -0
  50. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  51. package/packages/dd-trace/src/plugins/index.js +1 -0
  52. package/packages/dd-trace/src/proxy.js +0 -4
  53. package/packages/dd-trace/src/serverless.js +0 -48
  54. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
  55. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
@@ -7,7 +7,7 @@ const {
7
7
  addAttemptToFixStringToTestName,
8
8
  removeAttemptToFixStringFromTestName
9
9
  } = require('../../../dd-trace/src/plugins/util/test')
10
- const { channel, AsyncResource } = require('../helpers/instrument')
10
+ const { channel } = require('../helpers/instrument')
11
11
  const shimmer = require('../../../datadog-shimmer')
12
12
 
13
13
  // test channels
@@ -17,15 +17,16 @@ const testFinishCh = channel('ci:mocha:test:finish')
17
17
  const testRetryCh = channel('ci:mocha:test:retry')
18
18
  const errorCh = channel('ci:mocha:test:error')
19
19
  const skipCh = channel('ci:mocha:test:skip')
20
+ const testFnCh = channel('ci:mocha:test:fn')
20
21
 
21
22
  // suite channels
22
23
  const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
23
24
 
24
25
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
25
- const testToAr = new WeakMap()
26
+ const testToContext = new WeakMap()
26
27
  const originalFns = new WeakMap()
27
28
  const testToStartLine = new WeakMap()
28
- const testFileToSuiteAr = new Map()
29
+ const testFileToSuiteCtx = new Map()
29
30
  const wrappedFunctions = new WeakSet()
30
31
  const newTests = {}
31
32
  const testsAttemptToFix = new Set()
@@ -125,7 +126,7 @@ function getTestStatus (test) {
125
126
  return 'pass'
126
127
  }
127
128
 
128
- function getTestToArKey (test) {
129
+ function getTestToContextKey (test) {
129
130
  if (!test.fn) {
130
131
  return test
131
132
  }
@@ -136,14 +137,14 @@ function getTestToArKey (test) {
136
137
  return originalFn
137
138
  }
138
139
 
139
- function getTestAsyncResource (test) {
140
- const key = getTestToArKey(test)
141
- return testToAr.get(key)
140
+ function getTestContext (test) {
141
+ const key = getTestToContextKey(test)
142
+ return testToContext.get(key)
142
143
  }
143
144
 
144
145
  function runnableWrapper (RunnablePackage, libraryConfig) {
145
146
  shimmer.wrap(RunnablePackage.prototype, 'run', run => function () {
146
- if (!testStartCh.hasSubscribers) {
147
+ if (!testFinishCh.hasSubscribers) {
147
148
  return run.apply(this, arguments)
148
149
  }
149
150
  // Flaky test retries does not work in parallel mode
@@ -167,14 +168,17 @@ function runnableWrapper (RunnablePackage, libraryConfig) {
167
168
 
168
169
  if (isTestHook || this.type === 'test') {
169
170
  const test = isTestHook ? this.ctx.currentTest : this
170
- const asyncResource = getTestAsyncResource(test)
171
+ const ctx = getTestContext(test)
171
172
 
172
- if (asyncResource) {
173
- // we bind the test fn to the correct async resource
174
- const newFn = asyncResource.bind(this.fn)
173
+ if (ctx) {
174
+ const originalFn = this.fn
175
+ // we bind the test fn to the correct context
176
+ const newFn = function () {
177
+ return testFnCh.runStores(ctx, () => originalFn.apply(this, arguments))
178
+ }
175
179
 
176
180
  // we store the original function, not to lose it
177
- originalFns.set(newFn, this.fn)
181
+ originalFns.set(newFn, originalFn)
178
182
  this.fn = newFn
179
183
 
180
184
  wrappedFunctions.add(this.fn)
@@ -189,7 +193,6 @@ function runnableWrapper (RunnablePackage, libraryConfig) {
189
193
  function getOnTestHandler (isMain) {
190
194
  return function (test) {
191
195
  const testStartLine = testToStartLine.get(test)
192
- const asyncResource = new AsyncResource('bound-anonymous-fn')
193
196
 
194
197
  // This may be a retry. If this is the case, `test.fn` is already wrapped,
195
198
  // so we need to restore it.
@@ -198,7 +201,6 @@ function getOnTestHandler (isMain) {
198
201
  test.fn = originalFn
199
202
  wrappedFunctions.delete(test.fn)
200
203
  }
201
- testToAr.set(test.fn, asyncResource)
202
204
 
203
205
  const {
204
206
  file: testSuiteAbsolutePath,
@@ -242,15 +244,15 @@ function getOnTestHandler (isMain) {
242
244
  test.pending = true
243
245
  }
244
246
 
245
- asyncResource.runInAsyncScope(() => {
246
- testStartCh.publish(testInfo)
247
- })
247
+ const ctx = testInfo
248
+ testToContext.set(test.fn, ctx)
249
+ testStartCh.runStores(ctx, () => { })
248
250
  }
249
251
  }
250
252
 
251
253
  function getOnTestEndHandler (config) {
252
254
  return async function (test) {
253
- const asyncResource = getTestAsyncResource(test)
255
+ const ctx = getTestContext(test)
254
256
  const status = getTestStatus(test)
255
257
 
256
258
  // After finishing it might take a bit for the snapshot to be handled.
@@ -295,18 +297,17 @@ function getOnTestEndHandler (config) {
295
297
  !test._ddIsEfdRetry
296
298
 
297
299
  // if there are afterEach to be run, we don't finish the test yet
298
- if (asyncResource && !getAfterEachHooks(test).length) {
299
- asyncResource.runInAsyncScope(() => {
300
- testFinishCh.publish({
301
- status,
302
- hasBeenRetried: isMochaRetry(test),
303
- isLastRetry: getIsLastRetry(test),
304
- hasFailedAllRetries,
305
- attemptToFixPassed,
306
- attemptToFixFailed,
307
- isAttemptToFixRetry,
308
- isAtrRetry
309
- })
300
+ if (ctx && !getAfterEachHooks(test).length) {
301
+ testFinishCh.publish({
302
+ status,
303
+ hasBeenRetried: isMochaRetry(test),
304
+ isLastRetry: getIsLastRetry(test),
305
+ hasFailedAllRetries,
306
+ attemptToFixPassed,
307
+ attemptToFixFailed,
308
+ isAttemptToFixRetry,
309
+ isAtrRetry,
310
+ ...ctx.currentStore
310
311
  })
311
312
  }
312
313
  }
@@ -320,10 +321,13 @@ function getOnHookEndHandler () {
320
321
  const isLastAfterEach = afterEachHooks.indexOf(hook) === afterEachHooks.length - 1
321
322
  if (isLastAfterEach) {
322
323
  const status = getTestStatus(test)
323
- const asyncResource = getTestAsyncResource(test)
324
- if (asyncResource) {
325
- asyncResource.runInAsyncScope(() => {
326
- testFinishCh.publish({ status, hasBeenRetried: isMochaRetry(test), isLastRetry: getIsLastRetry(test) })
324
+ const ctx = getTestContext(test)
325
+ if (ctx) {
326
+ testFinishCh.publish({
327
+ status,
328
+ hasBeenRetried: isMochaRetry(test),
329
+ isLastRetry: getIsLastRetry(test),
330
+ ...ctx.currentStore
327
331
  })
328
332
  }
329
333
  }
@@ -339,35 +343,34 @@ function getOnFailHandler (isMain) {
339
343
  if (isHook && testOrHook.ctx) {
340
344
  test = testOrHook.ctx.currentTest
341
345
  }
342
- let testAsyncResource
346
+ let testContext
343
347
  if (test) {
344
- testAsyncResource = getTestAsyncResource(test)
348
+ testContext = getTestContext(test)
345
349
  }
346
- if (testAsyncResource) {
347
- testAsyncResource.runInAsyncScope(() => {
348
- if (isHook) {
349
- err.message = `${testOrHook.fullTitle()}: ${err.message}`
350
- errorCh.publish(err)
351
- // if it's a hook and it has failed, 'test end' will not be called
352
- testFinishCh.publish({ status: 'fail', hasBeenRetried: isMochaRetry(test) })
353
- } else {
354
- errorCh.publish(err)
355
- }
356
- })
350
+ if (testContext) {
351
+ if (isHook) {
352
+ err.message = `${testOrHook.fullTitle()}: ${err.message}`
353
+ testContext.err = err
354
+ errorCh.runStores(testContext, () => { })
355
+ // if it's a hook and it has failed, 'test end' will not be called
356
+ testFinishCh.publish({ status: 'fail', hasBeenRetried: isMochaRetry(test), ...testContext.currentStore })
357
+ } else {
358
+ testContext.err = err
359
+ errorCh.runStores(testContext, () => { })
360
+ }
357
361
  }
358
362
 
359
363
  if (isMain) {
360
- const testSuiteAsyncResource = testFileToSuiteAr.get(testFile)
364
+ const testSuiteContext = testFileToSuiteCtx.get(testFile)
361
365
 
362
- if (testSuiteAsyncResource) {
366
+ if (testSuiteContext) {
363
367
  // we propagate the error to the suite
364
368
  const testSuiteError = new Error(
365
369
  `"${testOrHook.parent.fullTitle()}" failed with message "${err.message}"`
366
370
  )
367
371
  testSuiteError.stack = err.stack
368
- testSuiteAsyncResource.runInAsyncScope(() => {
369
- testSuiteErrorCh.publish(testSuiteError)
370
- })
372
+ testSuiteContext.error = testSuiteError
373
+ testSuiteErrorCh.runStores(testSuiteContext, () => { })
371
374
  }
372
375
  }
373
376
  }
@@ -375,20 +378,18 @@ function getOnFailHandler (isMain) {
375
378
 
376
379
  function getOnTestRetryHandler (config) {
377
380
  return function (test, err) {
378
- const asyncResource = getTestAsyncResource(test)
379
- if (asyncResource) {
381
+ const ctx = getTestContext(test)
382
+ if (ctx) {
380
383
  const isFirstAttempt = test._currentRetry === 0
381
384
  const willBeRetried = test._currentRetry < test._retries
382
385
  const isAtrRetry = !isFirstAttempt &&
383
386
  config.isFlakyTestRetriesEnabled &&
384
387
  !test._ddIsAttemptToFix &&
385
388
  !test._ddIsEfdRetry
386
- asyncResource.runInAsyncScope(() => {
387
- testRetryCh.publish({ isFirstAttempt, err, willBeRetried, test, isAtrRetry })
388
- })
389
+ testRetryCh.publish({ isFirstAttempt, err, willBeRetried, test, isAtrRetry, ...ctx.currentStore })
389
390
  }
390
- const key = getTestToArKey(test)
391
- testToAr.delete(key)
391
+ const key = getTestToContextKey(test)
392
+ testToContext.delete(key)
392
393
  }
393
394
  }
394
395
 
@@ -407,23 +408,19 @@ function getOnPendingHandler () {
407
408
  testStartLine
408
409
  }
409
410
 
410
- const asyncResource = getTestAsyncResource(test)
411
- if (asyncResource) {
412
- asyncResource.runInAsyncScope(() => {
413
- skipCh.publish(testInfo)
414
- })
411
+ const ctx = getTestContext(test)
412
+ if (ctx) {
413
+ skipCh.publish(testInfo)
415
414
  } else {
416
- // if there is no async resource, the test has been skipped through `test.skip`
415
+ // if there is no context, the test has been skipped through `test.skip`
417
416
  // or the parent suite is skipped
418
- const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
417
+ const testCtx = testInfo
419
418
  if (test.fn) {
420
- testToAr.set(test.fn, skippedTestAsyncResource)
419
+ testToContext.set(test.fn, testCtx)
421
420
  } else {
422
- testToAr.set(test, skippedTestAsyncResource)
421
+ testToContext.set(test, testCtx)
423
422
  }
424
- skippedTestAsyncResource.runInAsyncScope(() => {
425
- skipCh.publish(testInfo)
426
- })
423
+ skipCh.runStores(testCtx, () => { })
427
424
  }
428
425
  }
429
426
  }
@@ -484,9 +481,9 @@ module.exports = {
484
481
  getTestFullName,
485
482
  getTestStatus,
486
483
  runnableWrapper,
487
- testToAr,
484
+ testToContext,
488
485
  originalFns,
489
- getTestAsyncResource,
486
+ getTestContext,
490
487
  testToStartLine,
491
488
  getOnTestHandler,
492
489
  getOnTestEndHandler,
@@ -494,7 +491,7 @@ module.exports = {
494
491
  getOnHookEndHandler,
495
492
  getOnFailHandler,
496
493
  getOnPendingHandler,
497
- testFileToSuiteAr,
494
+ testFileToSuiteCtx,
498
495
  getRunTestsWrapper,
499
496
  newTests,
500
497
  testsQuarantined,
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { errorMonitor } = require('node:events')
4
+
3
5
  const {
4
6
  channel,
5
7
  addHook,
@@ -138,7 +140,7 @@ function wrapConnection (Connection, version) {
138
140
  onResult.apply(this, arguments)
139
141
  }, 'bound-anonymous-fn', this))
140
142
  } else {
141
- this.on('error', asyncResource.bind(error => errorCh.publish(error)))
143
+ this.on(errorMonitor, asyncResource.bind(error => errorCh.publish(error)))
142
144
  this.on('end', asyncResource.bind(() => finishCh.publish(undefined)))
143
145
  }
144
146
 
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { errorMonitor } = require('events')
4
+
3
5
  const { channel, addHook } = require('./helpers/instrument')
4
6
  const shimmer = require('../../datadog-shimmer')
5
7
 
@@ -99,7 +101,7 @@ function getOptions (args) {
99
101
  }
100
102
 
101
103
  function setupListeners (socket, protocol, ctx, finishCh, errorCh) {
102
- const events = ['connect', 'error', 'close', 'timeout']
104
+ const events = ['connect', errorMonitor, 'close', 'timeout']
103
105
 
104
106
  const wrapListener = function (error) {
105
107
  if (error) {
@@ -70,7 +70,7 @@ function wrapRenderToHTML (renderToHTML) {
70
70
 
71
71
  function wrapRenderErrorToHTML (renderErrorToHTML) {
72
72
  return function (err, req, res, pathname, query) {
73
- return instrument(req, res, err, () => renderErrorToHTML.apply(this, arguments))
73
+ return instrument(req, res, () => renderErrorToHTML.apply(this, arguments), err)
74
74
  }
75
75
  }
76
76
 
@@ -82,7 +82,7 @@ function wrapRenderToResponse (renderToResponse) {
82
82
 
83
83
  function wrapRenderErrorToResponse (renderErrorToResponse) {
84
84
  return function (ctx, err) {
85
- return instrument(ctx.req, ctx.res, err, () => renderErrorToResponse.apply(this, arguments))
85
+ return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments), err)
86
86
  }
87
87
  }
88
88
 
@@ -121,12 +121,7 @@ function getRequestMeta (req, key) {
121
121
  return typeof key === 'string' ? meta[key] : meta
122
122
  }
123
123
 
124
- function instrument (req, res, error, handler) {
125
- if (typeof error === 'function') {
126
- handler = error
127
- error = null
128
- }
129
-
124
+ function instrument (req, res, handler, error) {
130
125
  req = req.originalRequest || req
131
126
  res = res.originalResponse || res
132
127
 
@@ -216,13 +211,13 @@ addHook({
216
211
  name: 'next',
217
212
  versions: ['>=11.1'],
218
213
  file: 'dist/server/serve-static.js'
219
- }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
214
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic, { replaceGetter: true }))
220
215
 
221
216
  addHook({
222
217
  name: 'next',
223
218
  versions: ['>=10.2 <11.1'],
224
219
  file: 'dist/next-server/server/serve-static.js'
225
- }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
220
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic, { replaceGetter: true }))
226
221
 
227
222
  addHook({ name: 'next', versions: ['>=11.1'], file: 'dist/server/next-server.js' }, nextServer => {
228
223
  const Server = nextServer.default
@@ -282,8 +277,7 @@ addHook({
282
277
  versions: ['>=13'],
283
278
  file: 'dist/server/web/spec-extension/request.js'
284
279
  }, request => {
285
- const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')
286
- shimmer.wrap(nextUrlDescriptor, 'get', function (originalGet) {
280
+ shimmer.wrap(request.NextRequest.prototype, 'nextUrl', function (originalGet) {
287
281
  return function wrappedGet () {
288
282
  const nextUrl = originalGet.apply(this, arguments)
289
283
  if (queryParsedChannel.hasSubscribers) {
@@ -300,8 +294,6 @@ addHook({
300
294
  }
301
295
  })
302
296
 
303
- Object.defineProperty(request.NextRequest.prototype, 'nextUrl', nextUrlDescriptor)
304
-
305
297
  shimmer.massWrap(request.NextRequest.prototype, ['text', 'json'], function (originalMethod) {
306
298
  return async function wrappedJson () {
307
299
  const body = await originalMethod.apply(this, arguments)
@@ -45,7 +45,7 @@ function wrapQuery (query) {
45
45
  const textProp = Object.getOwnPropertyDescriptor(textPropObj, 'text')
46
46
  const stream = typeof textPropObj.read === 'function'
47
47
 
48
- // Only alter `text` property if safe to do so.
48
+ // Only alter `text` property if safe to do so. Initially, it's a property, not a getter.
49
49
  if (!textProp || textProp.configurable) {
50
50
  const originalText = textPropObj.text
51
51
 
@@ -129,21 +129,15 @@ function wrapQuery (query) {
129
129
  }
130
130
  } else if (newQuery.once) {
131
131
  newQuery
132
- .once('error', finish)
132
+ .once(errorMonitor, finish)
133
133
  .once('end', (res) => finish(null, res))
134
134
  } else {
135
+ // TODO: This code is never reached in our tests.
136
+ // Internally, pg always uses callbacks or streams, even for promise based queries.
137
+ // Investigate if this code should just be removed.
135
138
  newQuery.then((res) => finish(null, res), finish)
136
139
  }
137
140
 
138
- if (stream) {
139
- newQuery.on('end', () => {
140
- finish(null, [])
141
- })
142
- newQuery.on(errorMonitor, (err) => {
143
- finish(err)
144
- })
145
- }
146
-
147
141
  try {
148
142
  return retval
149
143
  } catch (err) {
@@ -25,8 +25,8 @@ const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
25
25
  const workerReportCh = channel('ci:playwright:worker:report')
26
26
  const testPageGotoCh = channel('ci:playwright:test:page-goto')
27
27
 
28
- const testToAr = new WeakMap()
29
- const testSuiteToAr = new Map()
28
+ const testToCtx = new WeakMap()
29
+ const testSuiteToCtx = new Map()
30
30
  const testSuiteToTestStatuses = new Map()
31
31
  const testSuiteToErrors = new Map()
32
32
  const testsToTestStatuses = new Map()
@@ -279,11 +279,9 @@ function testBeginHandler (test, browserName, isMainProcess) {
279
279
 
280
280
  if (isNewTestSuite) {
281
281
  startedSuites.push(testSuiteAbsolutePath)
282
- const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
283
- testSuiteToAr.set(testSuiteAbsolutePath, testSuiteAsyncResource)
284
- testSuiteAsyncResource.runInAsyncScope(() => {
285
- testSuiteStartCh.publish(testSuiteAbsolutePath)
286
- })
282
+ const testSuiteCtx = { testSuiteAbsolutePath }
283
+ testSuiteToCtx.set(testSuiteAbsolutePath, testSuiteCtx)
284
+ testSuiteStartCh.runStores(testSuiteCtx, () => { })
287
285
  }
288
286
 
289
287
  // We disable retries by default if attemptToFix is true
@@ -293,19 +291,17 @@ function testBeginHandler (test, browserName, isMainProcess) {
293
291
 
294
292
  // this handles tests that do not go through the worker process (because they're skipped)
295
293
  if (isMainProcess) {
296
- const testAsyncResource = new AsyncResource('bound-anonymous-fn')
297
- testToAr.set(test, testAsyncResource)
298
294
  const testName = getTestFullname(test)
295
+ const testCtx = {
296
+ testName,
297
+ testSuiteAbsolutePath,
298
+ testSourceLine,
299
+ browserName,
300
+ isDisabled: test._ddIsDisabled
301
+ }
302
+ testToCtx.set(test, testCtx)
299
303
 
300
- testAsyncResource.runInAsyncScope(() => {
301
- testStartCh.publish({
302
- testName,
303
- testSuiteAbsolutePath,
304
- testSourceLine,
305
- browserName,
306
- isDisabled: test._ddIsDisabled
307
- })
308
- })
304
+ testStartCh.runStores(testCtx, () => { })
309
305
  }
310
306
  }
311
307
 
@@ -350,28 +346,27 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
350
346
  // this handles tests that do not go through the worker process (because they're skipped)
351
347
  if (isMainProcess) {
352
348
  const testResult = results[results.length - 1]
353
- const testAsyncResource = testToAr.get(test)
349
+ const testCtx = testToCtx.get(test)
354
350
  const isAtrRetry = testResult?.retry > 0 &&
355
351
  isFlakyTestRetriesEnabled &&
356
352
  !test._ddIsAttemptToFix &&
357
353
  !test._ddIsEfdRetry
358
- testAsyncResource.runInAsyncScope(() => {
359
- testFinishCh.publish({
360
- testStatus,
361
- steps: testResult?.steps || [],
362
- isRetry: testResult?.retry > 0,
363
- error,
364
- extraTags: annotationTags,
365
- isNew: test._ddIsNew,
366
- isAttemptToFix: test._ddIsAttemptToFix,
367
- isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
368
- isQuarantined: test._ddIsQuarantined,
369
- isEfdRetry: test._ddIsEfdRetry,
370
- hasFailedAllRetries: test._ddHasFailedAllRetries,
371
- hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
372
- hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
373
- isAtrRetry
374
- })
354
+ testFinishCh.publish({
355
+ testStatus,
356
+ steps: testResult?.steps || [],
357
+ isRetry: testResult?.retry > 0,
358
+ error,
359
+ extraTags: annotationTags,
360
+ isNew: test._ddIsNew,
361
+ isAttemptToFix: test._ddIsAttemptToFix,
362
+ isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
363
+ isQuarantined: test._ddIsQuarantined,
364
+ isEfdRetry: test._ddIsEfdRetry,
365
+ hasFailedAllRetries: test._ddHasFailedAllRetries,
366
+ hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
367
+ hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
368
+ isAtrRetry,
369
+ ...testCtx.currentStore
375
370
  })
376
371
  }
377
372
 
@@ -401,10 +396,8 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
401
396
  }
402
397
 
403
398
  const suiteError = getTestSuiteError(testSuiteAbsolutePath)
404
- const testSuiteAsyncResource = testSuiteToAr.get(testSuiteAbsolutePath)
405
- testSuiteAsyncResource.runInAsyncScope(() => {
406
- testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError })
407
- })
399
+ const testSuiteCtx = testSuiteToCtx.get(testSuiteAbsolutePath)
400
+ testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError, ...testSuiteCtx.currentStore })
408
401
  }
409
402
  }
410
403
 
@@ -610,7 +603,7 @@ function runnerHook (runnerExport, playwrightVersion) {
610
603
  totalAttemptToFixFailedTestCount += testStatuses.filter(status => status === 'fail').length
611
604
  }
612
605
 
613
- if (totalFailedTestCount === totalAttemptToFixFailedTestCount) {
606
+ if (totalFailedTestCount > 0 && totalFailedTestCount === totalAttemptToFixFailedTestCount) {
614
607
  runAllTestsReturn = 'passed'
615
608
  }
616
609
  }
@@ -873,17 +866,16 @@ addHook({
873
866
  // If test events are created in the worker process I need to stop creating it in the main process
874
867
  // Probably yet another test worker exporter is needed in addition to the ones for mocha, jest and cucumber
875
868
  // it's probably hard to tell that's a playwright worker though, as I don't think there is a specific env variable
876
- const testAsyncResource = new AsyncResource('bound-anonymous-fn')
869
+ const testCtx = {
870
+ testName,
871
+ testSuiteAbsolutePath,
872
+ testSourceLine,
873
+ browserName
874
+ }
875
+ testToCtx.set(test, testCtx)
877
876
  // TODO - In the future we may need to implement a mechanism to send test properties
878
877
  // to the worker process before _runTest is called
879
- testAsyncResource.runInAsyncScope(() => {
880
- testStartCh.publish({
881
- testName,
882
- testSuiteAbsolutePath,
883
- testSourceLine,
884
- browserName
885
- })
886
-
878
+ testStartCh.runStores(testCtx, () => {
887
879
  let existAfterEachHook = false
888
880
 
889
881
  // We try to find an existing afterEach hook with _ddHook to avoid adding a new one
@@ -976,25 +968,24 @@ addHook({
976
968
  // Wait for the properties to be received
977
969
  await ddPropertiesPromise
978
970
 
979
- testAsyncResource.runInAsyncScope(() => {
980
- testFinishCh.publish({
981
- testStatus: STATUS_TO_TEST_STATUS[status],
982
- steps: steps.filter(step => step.testId === testId),
983
- error,
984
- extraTags: annotationTags,
985
- isNew: test._ddIsNew,
986
- isRetry: retry > 0,
987
- isEfdRetry: test._ddIsEfdRetry,
988
- isAttemptToFix: test._ddIsAttemptToFix,
989
- isDisabled: test._ddIsDisabled,
990
- isQuarantined: test._ddIsQuarantined,
991
- isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
992
- hasFailedAllRetries: test._ddHasFailedAllRetries,
993
- hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
994
- hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
995
- isAtrRetry: test._ddIsAtrRetry,
996
- onDone
997
- })
971
+ testFinishCh.publish({
972
+ testStatus: STATUS_TO_TEST_STATUS[status],
973
+ steps: steps.filter(step => step.testId === testId),
974
+ error,
975
+ extraTags: annotationTags,
976
+ isNew: test._ddIsNew,
977
+ isRetry: retry > 0,
978
+ isEfdRetry: test._ddIsEfdRetry,
979
+ isAttemptToFix: test._ddIsAttemptToFix,
980
+ isDisabled: test._ddIsDisabled,
981
+ isQuarantined: test._ddIsQuarantined,
982
+ isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
983
+ hasFailedAllRetries: test._ddHasFailedAllRetries,
984
+ hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
985
+ hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
986
+ isAtrRetry: test._ddIsAtrRetry,
987
+ onDone,
988
+ ...testCtx.currentStore
998
989
  })
999
990
 
1000
991
  await flushPromise
@@ -26,23 +26,17 @@ addHook({ name: names }, function (url) {
26
26
 
27
27
  const URLPrototype = url.URL.prototype.constructor.prototype
28
28
  instrumentedGetters.forEach(property => {
29
- const originalDescriptor = Object.getOwnPropertyDescriptor(URLPrototype, property)
29
+ shimmer.wrap(URLPrototype, property, function (originalGet) {
30
+ return function get () {
31
+ const result = originalGet.call(this)
32
+ if (!urlGetterChannel.hasSubscribers) return result
30
33
 
31
- if (originalDescriptor?.get) {
32
- const newDescriptor = shimmer.wrap(originalDescriptor, 'get', function (originalGet) {
33
- return function get () {
34
- const result = originalGet.apply(this, arguments)
35
- if (!urlGetterChannel.hasSubscribers) return result
34
+ const context = { urlObject: this, result, property }
35
+ urlGetterChannel.publish(context)
36
36
 
37
- const context = { urlObject: this, result, property }
38
- urlGetterChannel.publish(context)
39
-
40
- return context.result
41
- }
42
- })
43
-
44
- Object.defineProperty(URLPrototype, property, newDescriptor)
45
- }
37
+ return context.result
38
+ }
39
+ })
46
40
  })
47
41
 
48
42
  shimmer.wrap(url, 'URL', (URL) => {
@@ -83,6 +77,4 @@ addHook({ name: names }, function (url) {
83
77
  }
84
78
  })
85
79
  }
86
-
87
- return url
88
80
  })