dd-trace 6.20.1 → 6.21.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 (43) hide show
  1. package/index.d.ts +91 -3
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +57 -4
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  5. package/packages/datadog-instrumentations/src/jest.js +119 -1
  6. package/packages/datadog-instrumentations/src/mocha/utils.js +124 -36
  7. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
  8. package/packages/datadog-instrumentations/src/mocha/worker.js +6 -3
  9. package/packages/datadog-instrumentations/src/playwright.js +3 -0
  10. package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
  11. package/packages/datadog-instrumentations/src/webdriverio.js +115 -15
  12. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
  13. package/packages/datadog-plugin-mocha/src/index.js +216 -29
  14. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +449 -0
  15. package/packages/dd-trace/src/debugger/constants.js +1 -0
  16. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
  17. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
  18. package/packages/dd-trace/src/debugger/devtools_client/send.js +6 -2
  19. package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
  20. package/packages/dd-trace/src/debugger/index.js +24 -2
  21. package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
  22. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  23. package/packages/dd-trace/src/exporters/common/url.js +1 -1
  24. package/packages/dd-trace/src/id.js +1 -4
  25. package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
  26. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  27. package/packages/dd-trace/src/llmobs/sdk.js +23 -6
  28. package/packages/dd-trace/src/llmobs/span_processor.js +17 -6
  29. package/packages/dd-trace/src/llmobs/tagger.js +54 -1
  30. package/packages/dd-trace/src/llmobs/writers/base.js +3 -1
  31. package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
  32. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
  33. package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
  34. package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
  35. package/packages/dd-trace/src/openfeature/flagging_provider.js +29 -0
  36. package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
  37. package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
  38. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
  39. package/packages/dd-trace/src/otel-thread-ctx.js +2 -2
  40. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
  41. package/packages/dd-trace/src/proxy.js +2 -0
  42. package/packages/dd-trace/src/serverless.js +5 -0
  43. package/packages/dd-trace/src/storage-channels.js +112 -46
@@ -1,13 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const { performance } = require('node:perf_hooks')
4
- const { fileURLToPath } = require('node:url')
4
+ const { clearTimeout, setTimeout } = require('node:timers')
5
5
 
6
6
  const { channel } = require('dc-polyfill')
7
7
 
8
8
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
9
9
  const { storage } = require('../../datadog-core')
10
- const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
10
+ const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
11
11
  const {
12
12
  getEfdRetryCountForDuration,
13
13
  hasEfdRetries,
@@ -15,16 +15,20 @@ const {
15
15
  const {
16
16
  getDynamicAtrRetryCount,
17
17
  } = require('../../dd-trace/src/ci-visibility/dynamic-atr-retries')
18
+ const { FINAL_FLUSH_TIMEOUT } = require('../../dd-trace/src/ci-visibility/final-flush')
18
19
  const {
19
20
  SCREENSHOT_UPLOAD_RESULT_ERROR,
20
21
  SCREENSHOT_UPLOAD_RESULT_UPLOADED,
21
22
  setScreenshotUploadTags,
22
23
  } = require('../../dd-trace/src/ci-visibility/test-screenshot')
24
+ const { setVideoUploadTags } = require('../../dd-trace/src/ci-visibility/test-video')
23
25
  const log = require('../../dd-trace/src/log')
24
26
  const {
25
27
  requestWebdriverioScreenshotUpload,
28
+ requestWebdriverioVideoUpload,
26
29
  sendWebdriverioWorkerMessage,
27
30
  SUITE_FINISH,
31
+ VIDEO_UPLOAD_FLUSH,
28
32
  WEBDRIVERIO_WORKER_ENV,
29
33
  } = require('../../datadog-instrumentations/src/mocha/webdriverio-protocol')
30
34
 
@@ -83,6 +87,11 @@ const {
83
87
  TELEMETRY_TEST_SESSION,
84
88
  } = require('../../dd-trace/src/ci-visibility/telemetry')
85
89
 
90
+ // Load the recorder and capture clocks before test code can replace globals with fake timers.
91
+ const createWebdriverioVideo = require('./webdriverio-video')
92
+
93
+ const dateNow = Date.now
94
+
86
95
  const jasmineAdapterRunAsyncEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineAdapter_run:asyncEnd'
87
96
  const jasmineDoneCh = 'ci:webdriverio:jasmine:done'
88
97
  const jasmineExecuteAsyncEndCh = 'tracing:orchestrion:@wdio/utils:executeAsync:asyncEnd'
@@ -106,6 +115,7 @@ const WEBDRIVERIO_JASMINE_FAILED_EXPECTATION_COUNT = Symbol('webdriverioJasmineF
106
115
  const WEBDRIVERIO_JASMINE_FUNCTION_TYPE = Symbol('webdriverioJasmineFunctionType')
107
116
  const WEBDRIVERIO_JASMINE_TEST = Symbol('webdriverioJasmineTest')
108
117
  const isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
118
+ const shouldRecordWebdriverioVideos = getValueFromEnvSources('DD_TEST_FAILURE_VIDEOS_ENABLED', true) === true
109
119
  const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
110
120
  const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/
111
121
 
@@ -121,6 +131,20 @@ const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$
121
131
  * @property {string|undefined} status
122
132
  */
123
133
 
134
+ /**
135
+ * Waits for an attempt's final capture, bounded by its existing deadline, without waiting for encoding or upload.
136
+ *
137
+ * @param {ReturnType<typeof createWebdriverioVideo>} video
138
+ * @param {Promise<unknown>} [finishPromise]
139
+ */
140
+ function waitForWebdriverioCapture (video, finishPromise) {
141
+ if (!video) return finishPromise
142
+ // WDIO awaits its async framework hook, so bridge the recorder's callback before it can start the next attempt.
143
+ /** @type {Promise<void>} */
144
+ const captureWait = new Promise(resolve => video.waitForCapture(resolve))
145
+ return finishPromise ? Promise.all([finishPromise, captureWait]) : captureWait
146
+ }
147
+
124
148
  /**
125
149
  * Normalizes a WebdriverIO Jasmine spec identifier to a filesystem path.
126
150
  *
@@ -128,7 +152,20 @@ const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$
128
152
  * @returns {string|undefined}
129
153
  */
130
154
  function normalizeJasmineFile (file) {
131
- return file?.startsWith('file://') ? fileURLToPath(file) : file
155
+ return file?.startsWith('file://') ? require('node:url').fileURLToPath(file) : file
156
+ }
157
+
158
+ function getWebdriverioJasmineSpecResult (spec) {
159
+ if (spec?.result) return spec.result
160
+ if (typeof spec?.startedEvent === 'function') return spec.startedEvent()
161
+ if (!spec?.id) return
162
+ return {
163
+ description: spec.description,
164
+ filename: spec.filename,
165
+ fullName: typeof spec.getFullName === 'function' ? spec.getFullName() : spec.description,
166
+ id: spec.id,
167
+ parentSuiteId: spec.parentSuiteId,
168
+ }
132
169
  }
133
170
 
134
171
  /**
@@ -245,14 +282,19 @@ function getJasmineFailureFile (result, specs) {
245
282
  class MochaPlugin extends CiPlugin {
246
283
  static id = 'mocha'
247
284
 
285
+ #pendingWebdriverioVideos = new Set()
286
+ #webdriverioCapture
287
+ #webdriverioVideoFlushTimer
288
+
248
289
  constructor (...args) {
249
290
  super(...args)
250
291
 
251
292
  this._testTitleToParams = {}
252
293
  this.sourceRoot = process.cwd()
253
294
  this._webdriverioScreenshotUploads = new WeakMap()
254
- this._pendingWebdriverioScreenshotUploads = 0
255
- this._webdriverioScreenshotUploadCallbacks = []
295
+ this._webdriverioVideos = new Map()
296
+ this._pendingWebdriverioMediaUploads = 0
297
+ this._webdriverioMediaUploadCallbacks = []
256
298
 
257
299
  this.addSub('ci:webdriverio:screenshot:capabilities', (ctx) => {
258
300
  ctx.enabled = Boolean(
@@ -283,6 +325,36 @@ class MochaPlugin extends CiPlugin {
283
325
  exporter.uploadTestScreenshot({ content, traceId, idempotencyKey, capturedAtMs }, onDone)
284
326
  })
285
327
 
328
+ this.addSub('ci:webdriverio:video:capabilities',
329
+ /** @type {import('node:diagnostics_channel').ChannelListener} */ (
330
+ /** @param {{enabled?: boolean}} ctx */
331
+ (ctx) => {
332
+ ctx.enabled = Boolean(
333
+ shouldRecordWebdriverioVideos &&
334
+ this._tracerConfig.testOptimization?.DD_TEST_FAILURE_VIDEOS_ENABLED &&
335
+ (isWebdriverioWorker
336
+ ? this.libraryConfig?.isTestFailureVideosEnabled && createWebdriverioVideo.isFfmpegAvailable()
337
+ : this.tracer._exporter?.canUploadTestVideos?.())
338
+ )
339
+ }
340
+ ))
341
+
342
+ this.addSub('ci:webdriverio:video:upload',
343
+ /** @type {import('node:diagnostics_channel').ChannelListener} */ (
344
+ /**
345
+ * @param {{filePath: string, traceId: string, idempotencyKey: string, capturedAtMs: number,
346
+ * signal?: AbortSignal, onDone: (error?: Error) => void}} options
347
+ */
348
+ ({ filePath, traceId, idempotencyKey, capturedAtMs, signal, onDone }) => {
349
+ const exporter = this.tracer?._exporter
350
+ if (!exporter?.canUploadTestVideos?.() || !exporter.uploadTestVideo) {
351
+ onDone(new Error('WebdriverIO video upload is not supported by the active Test Optimization transport'))
352
+ return
353
+ }
354
+ exporter.uploadTestVideo({ filePath, traceId, idempotencyKey, capturedAtMs, signal }, onDone)
355
+ }
356
+ ))
357
+
286
358
  this.addSub('ci:mocha:worker:configuration', ({
287
359
  libraryConfig,
288
360
  repositoryRoot,
@@ -306,6 +378,19 @@ class MochaPlugin extends CiPlugin {
306
378
  'active Test Optimization transport.'
307
379
  )
308
380
  }
381
+ if (
382
+ testFramework === WEBDRIVERIO_FRAMEWORK &&
383
+ shouldRecordWebdriverioVideos &&
384
+ this._tracerConfig.testOptimization.DD_TEST_FAILURE_VIDEOS_ENABLED &&
385
+ !(isWebdriverioWorker
386
+ ? libraryConfig.isTestFailureVideosEnabled
387
+ : this.tracer._exporter?.canUploadTestVideos?.())
388
+ ) {
389
+ log.warn(
390
+ 'DD_TEST_FAILURE_VIDEOS_ENABLED is true, but WebdriverIO video upload is not supported by the %s',
391
+ 'active Test Optimization transport.'
392
+ )
393
+ }
309
394
  if (testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
310
395
  this._webdriverioJasmineState = {
311
396
  completedTestStatuses: new Map(),
@@ -481,7 +566,14 @@ class MochaPlugin extends CiPlugin {
481
566
  this.addSub(jasmineReporterSpecDoneEndCh, (ctx) => {
482
567
  if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
483
568
  const result = ctx.result
484
- const finishPromise = this.#finishWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs)
569
+ const testResult = ctx.arguments?.[0]
570
+ const test = this._webdriverioJasmineState?.tests.get(testResult?.id)
571
+ const video = this._webdriverioVideos.get(test?.span)
572
+ const finishTestPromise = this.#finishWebdriverioJasmineTest(testResult, ctx.self?._specs)
573
+ // Replay can defer video.finish(), which must start the final capture before we wait for it.
574
+ const finishPromise = finishTestPromise
575
+ ? finishTestPromise.then(() => waitForWebdriverioCapture(video))
576
+ : waitForWebdriverioCapture(video)
485
577
  if (finishPromise) {
486
578
  ctx.result = finishPromise.then(() => result)
487
579
  }
@@ -641,6 +733,8 @@ class MochaPlugin extends CiPlugin {
641
733
  this.addBind('ci:mocha:test:start', (ctx) => {
642
734
  const store = storage('legacy').getStore()
643
735
  const span = this.startTestSpan(ctx)
736
+ const { isDisabled, isAttemptToFix } = /** @type {{isDisabled?: boolean, isAttemptToFix?: boolean}} */ (ctx)
737
+ if (!isDisabled || isAttemptToFix) this.#startWebdriverioVideo(span)
644
738
 
645
739
  ctx.parentStore = store
646
740
  ctx.currentStore = { ...store, span }
@@ -650,13 +744,42 @@ class MochaPlugin extends CiPlugin {
650
744
  return ctx.currentStore
651
745
  })
652
746
 
747
+ this.addSub('ci:mocha:after-each:finish', (ctx) => {
748
+ const context = /** @type {{isWaiting: boolean, onDone: () => void}} */ (ctx)
749
+ const video = this.#webdriverioCapture
750
+ this.#webdriverioCapture = undefined
751
+ if (!video) return
752
+ context.isWaiting = true
753
+ video.waitForCapture(context.onDone)
754
+ })
755
+
653
756
  this.addSub('ci:mocha:worker:finish', ({ onDone } = {}) => {
654
- const flush = () => this.tracer._exporter.flush(onDone)
655
- if (this._pendingWebdriverioScreenshotUploads === 0) {
757
+ // An interrupted runner may exit without reporting the active attempt.
758
+ for (const span of this._webdriverioVideos.keys()) {
759
+ this.#finishWebdriverioMedia(span, 'skip', () => {})
760
+ }
761
+ const flush = () => {
762
+ clearTimeout(this.#webdriverioVideoFlushTimer)
763
+ this.#webdriverioVideoFlushTimer = undefined
764
+ if (isWebdriverioWorker && this.libraryConfig?.isTestFailureVideosEnabled) {
765
+ sendWebdriverioWorkerMessage({ name: VIDEO_UPLOAD_FLUSH, content: { complete: true } })
766
+ }
767
+ this.tracer._exporter.flush(onDone)
768
+ }
769
+ if (this._pendingWebdriverioMediaUploads === 0) {
656
770
  flush()
657
771
  return
658
772
  }
659
- this._webdriverioScreenshotUploadCallbacks.push(flush)
773
+ if (isWebdriverioWorker && this.libraryConfig?.isTestFailureVideosEnabled) {
774
+ sendWebdriverioWorkerMessage({ name: VIDEO_UPLOAD_FLUSH })
775
+ }
776
+ this._webdriverioMediaUploadCallbacks.push(flush)
777
+ if (this.#pendingWebdriverioVideos.size) {
778
+ this.#webdriverioVideoFlushTimer ??= setTimeout(() => {
779
+ for (const video of this.#pendingWebdriverioVideos) video.cancel()
780
+ }, FINAL_FLUSH_TIMEOUT)
781
+ this.#webdriverioVideoFlushTimer.unref?.()
782
+ }
660
783
  })
661
784
 
662
785
  this.addSub('ci:mocha:test:finish', ({
@@ -720,7 +843,7 @@ class MochaPlugin extends CiPlugin {
720
843
  this.activeTestSpan = null
721
844
  }
722
845
  }
723
- if (status !== 'fail' || !this.#startWebdriverioScreenshotUpload(span, finishSpan)) {
846
+ if (!this.#finishWebdriverioMedia(span, status, finishSpan)) {
724
847
  finishSpan()
725
848
  }
726
849
  }
@@ -830,23 +953,24 @@ class MochaPlugin extends CiPlugin {
830
953
  this.runningTestProbe) {
831
954
  finishWait = this.waitForInFlightDiBreakpointHits()
832
955
  }
833
- let screenshotFinished = false
834
- let canFinishScreenshot = false
956
+ let mediaFinished = false
957
+ let canFinishMedia = false
835
958
  let replayFinished = !finishWait
836
959
  let spanFinished = false
837
960
  const finishWhenReady = () => {
838
- if (!spanFinished && replayFinished && (screenshotStarted === false || screenshotFinished)) {
961
+ if (!spanFinished && replayFinished && (mediaStarted === false || mediaFinished)) {
839
962
  spanFinished = true
840
963
  finishSpan()
841
964
  }
842
965
  }
843
- const screenshotStarted = this.#startWebdriverioScreenshotUpload(span, () => {
844
- screenshotFinished = true
845
- if (canFinishScreenshot) {
966
+ const video = this._webdriverioVideos.get(span)
967
+ const mediaStarted = this.#finishWebdriverioMedia(span, 'fail', () => {
968
+ mediaFinished = true
969
+ if (canFinishMedia) {
846
970
  finishWhenReady()
847
971
  }
848
972
  })
849
- canFinishScreenshot = true
973
+ canFinishMedia = true
850
974
  if (finishWait) {
851
975
  const finishReplay = () => {
852
976
  replayFinished = true
@@ -857,6 +981,9 @@ class MochaPlugin extends CiPlugin {
857
981
  promises.finishTestPromise = finishTestPromise
858
982
  }
859
983
  }
984
+ if (video && promises) {
985
+ promises.finishTestPromise = waitForWebdriverioCapture(video, promises.finishTestPromise)
986
+ }
860
987
  finishWhenReady()
861
988
  }
862
989
  })
@@ -1131,6 +1258,7 @@ class MochaPlugin extends CiPlugin {
1131
1258
  title: test.title,
1132
1259
  })
1133
1260
  test.span = span
1261
+ if (!test.isDisabled || test.isAttemptToFix) this.#startWebdriverioVideo(span)
1134
1262
  test.currentStore = {
1135
1263
  ...parentStore,
1136
1264
  span,
@@ -1153,8 +1281,9 @@ class MochaPlugin extends CiPlugin {
1153
1281
  return
1154
1282
  }
1155
1283
 
1284
+ const specResult = getWebdriverioJasmineSpecResult(spec)
1156
1285
  if (this.libraryConfig?.isTestManagementTestsEnabled) {
1157
- this.#startWebdriverioJasmineTest(spec.result)
1286
+ this.#startWebdriverioJasmineTest(specResult)
1158
1287
  const test = this._webdriverioJasmineState?.tests.get(spec.id)
1159
1288
  if (test?.isDisabled && !test.isAttemptToFix) {
1160
1289
  spec.pend('Skipped by Datadog Test Management')
@@ -1486,6 +1615,71 @@ class MochaPlugin extends CiPlugin {
1486
1615
  }
1487
1616
  }
1488
1617
 
1618
+ /** @param {object} span - Span owning one WebdriverIO attempt */
1619
+ #startWebdriverioVideo (span) {
1620
+ if (this.testFramework !== WEBDRIVERIO_FRAMEWORK ||
1621
+ !shouldRecordWebdriverioVideos ||
1622
+ !this._tracerConfig.testOptimization?.DD_TEST_FAILURE_VIDEOS_ENABLED) return
1623
+ const canUpload = isWebdriverioWorker
1624
+ ? this.libraryConfig?.isTestFailureVideosEnabled
1625
+ : this.tracer._exporter?.canUploadTestVideos?.()
1626
+ if (!canUpload) return
1627
+
1628
+ const browser = globalThis._wdioGlobals?.get?.('browser') || globalThis.browser
1629
+ const video = createWebdriverioVideo(browser)
1630
+ this.#webdriverioCapture = video
1631
+ if (video) this._webdriverioVideos.set(span, video)
1632
+ }
1633
+
1634
+ /**
1635
+ * Finishes a span only after its independent screenshot and video uploads settle.
1636
+ *
1637
+ * @param {object} span
1638
+ * @param {string} status
1639
+ * @param {() => void} onDone
1640
+ */
1641
+ #finishWebdriverioMedia (span, status, onDone) {
1642
+ const video = this._webdriverioVideos.get(span)
1643
+ if (!video) return status === 'fail' && this.#startWebdriverioScreenshotUpload(span, onDone)
1644
+
1645
+ this._webdriverioVideos.delete(span)
1646
+ this.#pendingWebdriverioVideos.add(video)
1647
+ this._pendingWebdriverioMediaUploads++
1648
+ const traceId = span.context().toTraceId()
1649
+ const capturedAtMs = dateNow()
1650
+ let pending = 2
1651
+ const complete = () => {
1652
+ if (--pending !== 0) return
1653
+ onDone()
1654
+ this.#completeWebdriverioMediaUpload()
1655
+ }
1656
+ if (status !== 'fail' || !this.#startWebdriverioScreenshotUpload(span, complete)) complete()
1657
+ video.finish(status === 'fail', (filePath, index, callback) => {
1658
+ const options = {
1659
+ filePath,
1660
+ traceId,
1661
+ idempotencyKey: `${traceId}:webdriverio-failure-${index}.webm`,
1662
+ capturedAtMs,
1663
+ }
1664
+ if (isWebdriverioWorker) requestWebdriverioVideoUpload(options, callback)
1665
+ else this.tracer._exporter.uploadTestVideo(options, callback)
1666
+ }, result => {
1667
+ this.#pendingWebdriverioVideos.delete(video)
1668
+ setVideoUploadTags(span, result)
1669
+ complete()
1670
+ })
1671
+ return true
1672
+ }
1673
+
1674
+ /** Releases worker shutdown after all media-owned spans have finished. */
1675
+ #completeWebdriverioMediaUpload () {
1676
+ this._pendingWebdriverioMediaUploads--
1677
+ if (this._pendingWebdriverioMediaUploads !== 0) return
1678
+ const callbacks = this._webdriverioMediaUploadCallbacks
1679
+ this._webdriverioMediaUploadCallbacks = []
1680
+ for (const callback of callbacks) callback()
1681
+ }
1682
+
1489
1683
  /**
1490
1684
  * Captures and uploads the current WebdriverIO browser state once for a failed test attempt.
1491
1685
  *
@@ -1493,6 +1687,7 @@ class MochaPlugin extends CiPlugin {
1493
1687
  * @param {() => void} [onDone] - Called after the screenshot upload finishes
1494
1688
  */
1495
1689
  #startWebdriverioScreenshotUpload (span, onDone) {
1690
+ this._webdriverioVideos.get(span)?.capture()
1496
1691
  if (
1497
1692
  !span ||
1498
1693
  this.testFramework !== WEBDRIVERIO_FRAMEWORK ||
@@ -1527,7 +1722,7 @@ class MochaPlugin extends CiPlugin {
1527
1722
  finished: false,
1528
1723
  }
1529
1724
  this._webdriverioScreenshotUploads.set(span, upload)
1530
- this._pendingWebdriverioScreenshotUploads++
1725
+ this._pendingWebdriverioMediaUploads++
1531
1726
  const finishUpload = (result) => this.#finishWebdriverioScreenshotUpload(span, upload, result)
1532
1727
  let captureTimeout
1533
1728
  const failCapture = (error) => {
@@ -1586,18 +1781,10 @@ class MochaPlugin extends CiPlugin {
1586
1781
  const uploadCallbacks = upload.callbacks
1587
1782
  upload.callbacks = []
1588
1783
 
1589
- this._pendingWebdriverioScreenshotUploads--
1590
- let workerCallbacks = []
1591
- if (this._pendingWebdriverioScreenshotUploads === 0) {
1592
- workerCallbacks = this._webdriverioScreenshotUploadCallbacks
1593
- this._webdriverioScreenshotUploadCallbacks = []
1594
- }
1595
1784
  for (const callback of uploadCallbacks) {
1596
1785
  callback()
1597
1786
  }
1598
- for (const callback of workerCallbacks) {
1599
- callback()
1600
- }
1787
+ this.#completeWebdriverioMediaUpload()
1601
1788
  }
1602
1789
 
1603
1790
  /**
@@ -1616,7 +1803,7 @@ class MochaPlugin extends CiPlugin {
1616
1803
  }
1617
1804
  const exporter = this.tracer?._exporter
1618
1805
  const traceId = span.context().toTraceId()
1619
- const capturedAtMs = Date.now()
1806
+ const capturedAtMs = dateNow()
1620
1807
  let pendingUploads = screenshotList.length
1621
1808
  let hasUploadError = false
1622
1809
  const finishOne = (error) => {