dd-trace 4.38.1 → 4.40.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 (73) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/README.md +8 -18
  3. package/ci/init.js +7 -0
  4. package/ext/exporters.d.ts +1 -0
  5. package/ext/exporters.js +2 -1
  6. package/ext/tags.d.ts +1 -0
  7. package/ext/tags.js +1 -0
  8. package/index.d.ts +18 -3
  9. package/initialize.mjs +52 -0
  10. package/package.json +9 -12
  11. package/packages/datadog-instrumentations/src/amqplib.js +5 -2
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
  13. package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
  14. package/packages/datadog-instrumentations/src/body-parser.js +0 -1
  15. package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
  16. package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
  17. package/packages/datadog-instrumentations/src/express.js +0 -1
  18. package/packages/datadog-instrumentations/src/graphql.js +0 -2
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
  21. package/packages/datadog-instrumentations/src/http/server.js +0 -1
  22. package/packages/datadog-instrumentations/src/jest.js +6 -3
  23. package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
  24. package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
  25. package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
  26. package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
  27. package/packages/datadog-instrumentations/src/mocha.js +4 -673
  28. package/packages/datadog-instrumentations/src/openai.js +188 -17
  29. package/packages/datadog-instrumentations/src/playwright.js +4 -3
  30. package/packages/datadog-instrumentations/src/router.js +1 -1
  31. package/packages/datadog-instrumentations/src/selenium.js +13 -6
  32. package/packages/datadog-plugin-graphql/src/resolve.js +4 -0
  33. package/packages/datadog-plugin-mocha/src/index.js +82 -8
  34. package/packages/datadog-plugin-next/src/index.js +1 -2
  35. package/packages/datadog-plugin-openai/src/index.js +219 -73
  36. package/packages/dd-trace/src/appsec/addresses.js +4 -2
  37. package/packages/dd-trace/src/appsec/blocking.js +19 -25
  38. package/packages/dd-trace/src/appsec/channels.js +2 -1
  39. package/packages/dd-trace/src/appsec/graphql.js +10 -3
  40. package/packages/dd-trace/src/appsec/index.js +11 -4
  41. package/packages/dd-trace/src/appsec/rasp.js +35 -0
  42. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  43. package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
  44. package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
  45. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
  46. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
  47. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
  48. package/packages/dd-trace/src/config.js +97 -22
  49. package/packages/dd-trace/src/constants.js +2 -0
  50. package/packages/dd-trace/src/encode/0.4.js +47 -8
  51. package/packages/dd-trace/src/exporter.js +1 -0
  52. package/packages/dd-trace/src/flare/file.js +44 -0
  53. package/packages/dd-trace/src/flare/index.js +98 -0
  54. package/packages/dd-trace/src/log/channels.js +54 -29
  55. package/packages/dd-trace/src/log/writer.js +7 -49
  56. package/packages/dd-trace/src/opentelemetry/span.js +8 -0
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
  58. package/packages/dd-trace/src/plugins/index.js +1 -0
  59. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  60. package/packages/dd-trace/src/plugins/util/test.js +6 -0
  61. package/packages/dd-trace/src/priority_sampler.js +8 -4
  62. package/packages/dd-trace/src/profiler.js +2 -1
  63. package/packages/dd-trace/src/profiling/config.js +1 -0
  64. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  65. package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
  66. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
  67. package/packages/dd-trace/src/proxy.js +49 -15
  68. package/packages/dd-trace/src/ritm.js +13 -1
  69. package/packages/dd-trace/src/sampling_rule.js +2 -1
  70. package/packages/dd-trace/src/startup-log.js +19 -15
  71. package/packages/dd-trace/src/telemetry/index.js +6 -2
  72. package/packages/dd-trace/src/tracer.js +3 -0
  73. package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
@@ -1,674 +1,5 @@
1
- const { createCoverageMap } = require('istanbul-lib-coverage')
2
-
3
- const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
4
-
5
- const { addHook, channel, AsyncResource } = require('./helpers/instrument')
6
- const shimmer = require('../../datadog-shimmer')
7
- const log = require('../../dd-trace/src/log')
8
- const {
9
- getCoveredFilenamesFromCoverage,
10
- resetCoverage,
11
- mergeCoverage,
12
- getTestSuitePath,
13
- fromCoverageMapToCoverage,
14
- getCallSites,
15
- addEfdStringToTestName,
16
- removeEfdStringFromTestName
17
- } = require('../../dd-trace/src/plugins/util/test')
18
-
19
- const testStartCh = channel('ci:mocha:test:start')
20
- const errorCh = channel('ci:mocha:test:error')
21
- const skipCh = channel('ci:mocha:test:skip')
22
- const testFinishCh = channel('ci:mocha:test:finish')
23
- const parameterizedTestCh = channel('ci:mocha:test:parameterize')
24
-
25
- const libraryConfigurationCh = channel('ci:mocha:library-configuration')
26
- const knownTestsCh = channel('ci:mocha:known-tests')
27
- const skippableSuitesCh = channel('ci:mocha:test-suite:skippable')
28
-
29
- const testSessionStartCh = channel('ci:mocha:session:start')
30
- const testSessionFinishCh = channel('ci:mocha:session:finish')
31
-
32
- const testSuiteStartCh = channel('ci:mocha:test-suite:start')
33
- const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
34
- const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
35
- const testSuiteCodeCoverageCh = channel('ci:mocha:test-suite:code-coverage')
36
-
37
- const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
38
-
39
- // TODO: remove when root hooks and fixtures are implemented
40
- const patched = new WeakSet()
41
-
42
- const testToAr = new WeakMap()
43
- const originalFns = new WeakMap()
44
- const testFileToSuiteAr = new Map()
45
- const testToStartLine = new WeakMap()
46
- const newTests = {}
47
-
48
- // `isWorker` is true if it's a Mocha worker
49
- let isWorker = false
50
-
51
- // We'll preserve the original coverage here
52
- const originalCoverageMap = createCoverageMap()
53
-
54
- let suitesToSkip = []
55
- let frameworkVersion
56
- let isSuitesSkipped = false
57
- let skippedSuites = []
58
- const unskippableSuites = []
59
- let isForcedToRun = false
60
- let itrCorrelationId = ''
61
- let isEarlyFlakeDetectionEnabled = false
62
- let earlyFlakeDetectionNumRetries = 0
63
- let isSuitesSkippingEnabled = false
64
- let knownTests = []
65
-
66
- function getSuitesByTestFile (root) {
67
- const suitesByTestFile = {}
68
- function getSuites (suite) {
69
- if (suite.file) {
70
- if (suitesByTestFile[suite.file]) {
71
- suitesByTestFile[suite.file].push(suite)
72
- } else {
73
- suitesByTestFile[suite.file] = [suite]
74
- }
75
- }
76
- suite.suites.forEach(suite => {
77
- getSuites(suite)
78
- })
79
- }
80
- getSuites(root)
81
-
82
- const numSuitesByTestFile = Object.keys(suitesByTestFile).reduce((acc, testFile) => {
83
- acc[testFile] = suitesByTestFile[testFile].length
84
- return acc
85
- }, {})
86
-
87
- return { suitesByTestFile, numSuitesByTestFile }
1
+ if (process.env.MOCHA_WORKER_ID) {
2
+ require('./mocha/worker')
3
+ } else {
4
+ require('./mocha/main')
88
5
  }
89
-
90
- function getTestStatus (test) {
91
- if (test.isPending()) {
92
- return 'skip'
93
- }
94
- if (test.isFailed() || test.timedOut) {
95
- return 'fail'
96
- }
97
- return 'pass'
98
- }
99
-
100
- function isRetry (test) {
101
- return test._currentRetry !== undefined && test._currentRetry !== 0
102
- }
103
-
104
- function getTestFullName (test) {
105
- return `mocha.${getTestSuitePath(test.file, process.cwd())}.${removeEfdStringFromTestName(test.fullTitle())}`
106
- }
107
-
108
- function isNewTest (test) {
109
- const testSuite = getTestSuitePath(test.file, process.cwd())
110
- const testName = removeEfdStringFromTestName(test.fullTitle())
111
- const testsForSuite = knownTests.mocha?.[testSuite] || []
112
- return !testsForSuite.includes(testName)
113
- }
114
-
115
- function retryTest (test) {
116
- const originalTestName = test.title
117
- const suite = test.parent
118
- for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
119
- const clonedTest = test.clone()
120
- clonedTest.title = addEfdStringToTestName(originalTestName, retryIndex + 1)
121
- suite.addTest(clonedTest)
122
- clonedTest._ddIsNew = true
123
- clonedTest._ddIsEfdRetry = true
124
- }
125
- }
126
-
127
- function getTestAsyncResource (test) {
128
- if (!test.fn) {
129
- return testToAr.get(test)
130
- }
131
- if (!test.fn.asyncResource) {
132
- return testToAr.get(test.fn)
133
- }
134
- const originalFn = originalFns.get(test.fn)
135
- return testToAr.get(originalFn)
136
- }
137
-
138
- function getFilteredSuites (originalSuites) {
139
- return originalSuites.reduce((acc, suite) => {
140
- const testPath = getTestSuitePath(suite.file, process.cwd())
141
- const shouldSkip = suitesToSkip.includes(testPath)
142
- const isUnskippable = unskippableSuites.includes(suite.file)
143
- if (shouldSkip && !isUnskippable) {
144
- acc.skippedSuites.add(testPath)
145
- } else {
146
- acc.suitesToRun.push(suite)
147
- }
148
- return acc
149
- }, { suitesToRun: [], skippedSuites: new Set() })
150
- }
151
-
152
- function mochaHook (Runner) {
153
- if (patched.has(Runner)) return Runner
154
-
155
- patched.add(Runner)
156
-
157
- shimmer.wrap(Runner.prototype, 'runTests', runTests => function (suite, fn) {
158
- if (isEarlyFlakeDetectionEnabled) {
159
- // by the time we reach `this.on('test')`, it is too late. We need to add retries here
160
- suite.tests.forEach(test => {
161
- if (!test.isPending() && isNewTest(test)) {
162
- test._ddIsNew = true
163
- retryTest(test)
164
- }
165
- })
166
- }
167
- return runTests.apply(this, arguments)
168
- })
169
-
170
- shimmer.wrap(Runner.prototype, 'run', run => function () {
171
- if (!testStartCh.hasSubscribers || isWorker) {
172
- return run.apply(this, arguments)
173
- }
174
-
175
- const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
176
-
177
- const testRunAsyncResource = new AsyncResource('bound-anonymous-fn')
178
-
179
- this.once('end', testRunAsyncResource.bind(function () {
180
- let status = 'pass'
181
- let error
182
- if (this.stats) {
183
- status = this.stats.failures === 0 ? 'pass' : 'fail'
184
- if (this.stats.tests === 0) {
185
- status = 'skip'
186
- }
187
- } else if (this.failures !== 0) {
188
- status = 'fail'
189
- }
190
-
191
- if (isEarlyFlakeDetectionEnabled) {
192
- /**
193
- * If Early Flake Detection (EFD) is enabled the logic is as follows:
194
- * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
195
- * - If just a single attempt passes, we will prevent the test process from failing.
196
- * The rationale behind is the following: you may still be able to block your CI pipeline by gating
197
- * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
198
- */
199
- for (const tests of Object.values(newTests)) {
200
- const failingNewTests = tests.filter(test => test.isFailed())
201
- const areAllNewTestsFailing = failingNewTests.length === tests.length
202
- if (failingNewTests.length && !areAllNewTestsFailing) {
203
- this.stats.failures -= failingNewTests.length
204
- this.failures -= failingNewTests.length
205
- }
206
- }
207
- }
208
-
209
- if (status === 'fail') {
210
- error = new Error(`Failed tests: ${this.failures}.`)
211
- }
212
-
213
- testFileToSuiteAr.clear()
214
-
215
- let testCodeCoverageLinesTotal
216
- if (global.__coverage__) {
217
- try {
218
- testCodeCoverageLinesTotal = originalCoverageMap.getCoverageSummary().lines.pct
219
- } catch (e) {
220
- // ignore errors
221
- }
222
- // restore the original coverage
223
- global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
224
- }
225
-
226
- testSessionFinishCh.publish({
227
- status,
228
- isSuitesSkipped,
229
- testCodeCoverageLinesTotal,
230
- numSkippedSuites: skippedSuites.length,
231
- hasForcedToRunSuites: isForcedToRun,
232
- hasUnskippableSuites: !!unskippableSuites.length,
233
- error,
234
- isEarlyFlakeDetectionEnabled
235
- })
236
- }))
237
-
238
- this.once('start', testRunAsyncResource.bind(function () {
239
- const processArgv = process.argv.slice(2).join(' ')
240
- const command = `mocha ${processArgv}`
241
- testSessionStartCh.publish({ command, frameworkVersion })
242
- if (skippedSuites.length) {
243
- itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
244
- }
245
- }))
246
-
247
- this.on('suite', function (suite) {
248
- if (suite.root || !suite.tests.length) {
249
- return
250
- }
251
- let asyncResource = testFileToSuiteAr.get(suite.file)
252
- if (!asyncResource) {
253
- asyncResource = new AsyncResource('bound-anonymous-fn')
254
- testFileToSuiteAr.set(suite.file, asyncResource)
255
- const isUnskippable = unskippableSuites.includes(suite.file)
256
- isForcedToRun = isUnskippable && suitesToSkip.includes(getTestSuitePath(suite.file, process.cwd()))
257
- asyncResource.runInAsyncScope(() => {
258
- testSuiteStartCh.publish({
259
- testSuite: suite.file,
260
- isUnskippable,
261
- isForcedToRun,
262
- itrCorrelationId
263
- })
264
- })
265
- }
266
- })
267
-
268
- this.on('suite end', function (suite) {
269
- if (suite.root) {
270
- return
271
- }
272
- const suitesInTestFile = suitesByTestFile[suite.file]
273
-
274
- const isLastSuite = --numSuitesByTestFile[suite.file] === 0
275
- if (!isLastSuite) {
276
- return
277
- }
278
-
279
- let status = 'pass'
280
- if (suitesInTestFile.every(suite => suite.pending)) {
281
- status = 'skip'
282
- } else {
283
- // has to check every test in the test file
284
- suitesInTestFile.forEach(suite => {
285
- suite.eachTest(test => {
286
- if (test.state === 'failed' || test.timedOut) {
287
- status = 'fail'
288
- }
289
- })
290
- })
291
- }
292
-
293
- if (global.__coverage__) {
294
- const coverageFiles = getCoveredFilenamesFromCoverage(global.__coverage__)
295
-
296
- testSuiteCodeCoverageCh.publish({
297
- coverageFiles,
298
- suiteFile: suite.file
299
- })
300
- // We need to reset coverage to get a code coverage per suite
301
- // Before that, we preserve the original coverage
302
- mergeCoverage(global.__coverage__, originalCoverageMap)
303
- resetCoverage(global.__coverage__)
304
- }
305
-
306
- const asyncResource = testFileToSuiteAr.get(suite.file)
307
- asyncResource.runInAsyncScope(() => {
308
- testSuiteFinishCh.publish(status)
309
- })
310
- })
311
-
312
- this.on('test', (test) => {
313
- if (isRetry(test)) {
314
- return
315
- }
316
- const testStartLine = testToStartLine.get(test)
317
- const asyncResource = new AsyncResource('bound-anonymous-fn')
318
- testToAr.set(test.fn, asyncResource)
319
-
320
- const {
321
- file: testSuiteAbsolutePath,
322
- title,
323
- _ddIsNew: isNew,
324
- _ddIsEfdRetry: isEfdRetry
325
- } = test
326
-
327
- const testInfo = {
328
- testName: test.fullTitle(),
329
- testSuiteAbsolutePath,
330
- title,
331
- isNew,
332
- isEfdRetry,
333
- testStartLine
334
- }
335
-
336
- // We want to store the result of the new tests
337
- if (isNew) {
338
- const testFullName = getTestFullName(test)
339
- if (newTests[testFullName]) {
340
- newTests[testFullName].push(test)
341
- } else {
342
- newTests[testFullName] = [test]
343
- }
344
- }
345
-
346
- asyncResource.runInAsyncScope(() => {
347
- testStartCh.publish(testInfo)
348
- })
349
- })
350
-
351
- this.on('test end', (test) => {
352
- const asyncResource = getTestAsyncResource(test)
353
- const status = getTestStatus(test)
354
-
355
- // if there are afterEach to be run, we don't finish the test yet
356
- if (asyncResource && !test.parent._afterEach.length) {
357
- asyncResource.runInAsyncScope(() => {
358
- testFinishCh.publish(status)
359
- })
360
- }
361
- })
362
-
363
- // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
364
- this.on('hook end', (hook) => {
365
- const test = hook.ctx.currentTest
366
- if (test && hook.parent._afterEach.includes(hook)) { // only if it's an afterEach
367
- const isLastAfterEach = hook.parent._afterEach.indexOf(hook) === hook.parent._afterEach.length - 1
368
- if (isLastAfterEach) {
369
- const status = getTestStatus(test)
370
- const asyncResource = getTestAsyncResource(test)
371
- asyncResource.runInAsyncScope(() => {
372
- testFinishCh.publish(status)
373
- })
374
- }
375
- }
376
- })
377
-
378
- this.on('fail', (testOrHook, err) => {
379
- const testFile = testOrHook.file
380
- let test = testOrHook
381
- const isHook = testOrHook.type === 'hook'
382
- if (isHook && testOrHook.ctx) {
383
- test = testOrHook.ctx.currentTest
384
- }
385
- let testAsyncResource
386
- if (test) {
387
- testAsyncResource = getTestAsyncResource(test)
388
- }
389
- if (testAsyncResource) {
390
- testAsyncResource.runInAsyncScope(() => {
391
- if (isHook) {
392
- err.message = `${testOrHook.fullTitle()}: ${err.message}`
393
- errorCh.publish(err)
394
- // if it's a hook and it has failed, 'test end' will not be called
395
- testFinishCh.publish('fail')
396
- } else {
397
- errorCh.publish(err)
398
- }
399
- })
400
- }
401
- const testSuiteAsyncResource = testFileToSuiteAr.get(testFile)
402
-
403
- if (testSuiteAsyncResource) {
404
- // we propagate the error to the suite
405
- const testSuiteError = new Error(
406
- `"${testOrHook.parent.fullTitle()}" failed with message "${err.message}"`
407
- )
408
- testSuiteError.stack = err.stack
409
- testSuiteAsyncResource.runInAsyncScope(() => {
410
- testSuiteErrorCh.publish(testSuiteError)
411
- })
412
- }
413
- })
414
-
415
- this.on('pending', (test) => {
416
- const testStartLine = testToStartLine.get(test)
417
- const {
418
- file: testSuiteAbsolutePath,
419
- title
420
- } = test
421
-
422
- const testInfo = {
423
- testName: test.fullTitle(),
424
- testSuiteAbsolutePath,
425
- title,
426
- testStartLine
427
- }
428
-
429
- const asyncResource = getTestAsyncResource(test)
430
- if (asyncResource) {
431
- asyncResource.runInAsyncScope(() => {
432
- skipCh.publish(testInfo)
433
- })
434
- } else {
435
- // if there is no async resource, the test has been skipped through `test.skip`
436
- // or the parent suite is skipped
437
- const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
438
- if (test.fn) {
439
- testToAr.set(test.fn, skippedTestAsyncResource)
440
- } else {
441
- testToAr.set(test, skippedTestAsyncResource)
442
- }
443
- skippedTestAsyncResource.runInAsyncScope(() => {
444
- skipCh.publish(testInfo)
445
- })
446
- }
447
- })
448
-
449
- return run.apply(this, arguments)
450
- })
451
-
452
- return Runner
453
- }
454
-
455
- function mochaEachHook (mochaEach) {
456
- if (patched.has(mochaEach)) return mochaEach
457
-
458
- patched.add(mochaEach)
459
-
460
- return shimmer.wrap(mochaEach, function () {
461
- const [params] = arguments
462
- const { it, ...rest } = mochaEach.apply(this, arguments)
463
- return {
464
- it: function (title) {
465
- parameterizedTestCh.publish({ title, params })
466
- it.apply(this, arguments)
467
- },
468
- ...rest
469
- }
470
- })
471
- }
472
-
473
- addHook({
474
- name: 'mocha',
475
- versions: ['>=5.2.0'],
476
- file: 'lib/mocha.js'
477
- }, (Mocha, mochaVersion) => {
478
- frameworkVersion = mochaVersion
479
- const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn')
480
- /**
481
- * Get ITR configuration and skippable suites
482
- * If ITR is disabled, `onDone` is called immediately on the subscriber
483
- */
484
- shimmer.wrap(Mocha.prototype, 'run', run => function () {
485
- if (this.options.parallel) {
486
- log.warn('Unable to initialize CI Visibility because Mocha is running in parallel mode.')
487
- return run.apply(this, arguments)
488
- }
489
-
490
- if (!libraryConfigurationCh.hasSubscribers || this.isWorker) {
491
- if (this.isWorker) {
492
- isWorker = true
493
- }
494
- return run.apply(this, arguments)
495
- }
496
- this.options.delay = true
497
-
498
- const runner = run.apply(this, arguments)
499
-
500
- this.files.forEach(path => {
501
- const isUnskippable = isMarkedAsUnskippable({ path })
502
- if (isUnskippable) {
503
- unskippableSuites.push(path)
504
- }
505
- })
506
-
507
- const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
508
- if (err) {
509
- suitesToSkip = []
510
- } else {
511
- suitesToSkip = skippableSuites
512
- itrCorrelationId = responseItrCorrelationId
513
- }
514
- // We remove the suites that we skip through ITR
515
- const filteredSuites = getFilteredSuites(runner.suite.suites)
516
- const { suitesToRun } = filteredSuites
517
-
518
- isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length
519
-
520
- log.debug(
521
- () => `${suitesToRun.length} out of ${runner.suite.suites.length} suites are going to run.`
522
- )
523
-
524
- runner.suite.suites = suitesToRun
525
-
526
- skippedSuites = Array.from(filteredSuites.skippedSuites)
527
-
528
- global.run()
529
- }
530
-
531
- const onReceivedKnownTests = ({ err, knownTests: receivedKnownTests }) => {
532
- if (err) {
533
- knownTests = []
534
- isEarlyFlakeDetectionEnabled = false
535
- } else {
536
- knownTests = receivedKnownTests
537
- }
538
-
539
- if (isSuitesSkippingEnabled) {
540
- skippableSuitesCh.publish({
541
- onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
542
- })
543
- } else {
544
- global.run()
545
- }
546
- }
547
-
548
- const onReceivedConfiguration = ({ err, libraryConfig }) => {
549
- if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
550
- return global.run()
551
- }
552
-
553
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
554
- isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
555
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
556
-
557
- if (isEarlyFlakeDetectionEnabled) {
558
- knownTestsCh.publish({
559
- onDone: mochaRunAsyncResource.bind(onReceivedKnownTests)
560
- })
561
- } else if (isSuitesSkippingEnabled) {
562
- skippableSuitesCh.publish({
563
- onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites)
564
- })
565
- } else {
566
- global.run()
567
- }
568
- }
569
-
570
- mochaRunAsyncResource.runInAsyncScope(() => {
571
- libraryConfigurationCh.publish({
572
- onDone: mochaRunAsyncResource.bind(onReceivedConfiguration)
573
- })
574
- })
575
- return runner
576
- })
577
- return Mocha
578
- })
579
-
580
- addHook({
581
- name: 'mocha',
582
- versions: ['>=5.2.0'],
583
- file: 'lib/suite.js'
584
- }, (Suite) => {
585
- shimmer.wrap(Suite.prototype, 'addTest', addTest => function (test) {
586
- const callSites = getCallSites()
587
- let startLine
588
- const testCallSite = callSites.find(site => site.getFileName() === test.file)
589
- if (testCallSite) {
590
- startLine = testCallSite.getLineNumber()
591
- testToStartLine.set(test, startLine)
592
- }
593
- return addTest.apply(this, arguments)
594
- })
595
- return Suite
596
- })
597
-
598
- addHook({
599
- name: 'mocha',
600
- versions: ['>=5.2.0'],
601
- file: 'lib/runner.js'
602
- }, mochaHook)
603
-
604
- addHook({
605
- name: 'mocha',
606
- versions: ['>=5.2.0'],
607
- file: 'lib/cli/run-helpers.js'
608
- }, (run) => {
609
- shimmer.wrap(run, 'runMocha', runMocha => async function () {
610
- if (!testStartCh.hasSubscribers) {
611
- return runMocha.apply(this, arguments)
612
- }
613
- const mocha = arguments[0]
614
- /**
615
- * This attaches `run` to the global context, which we'll call after
616
- * our configuration and skippable suites requests
617
- */
618
- if (!mocha.options.parallel) {
619
- mocha.options.delay = true
620
- }
621
- return runMocha.apply(this, arguments)
622
- })
623
- return run
624
- })
625
-
626
- addHook({
627
- name: 'mocha',
628
- versions: ['>=5.2.0'],
629
- file: 'lib/runnable.js'
630
- }, (Runnable) => {
631
- shimmer.wrap(Runnable.prototype, 'run', run => function () {
632
- if (!testStartCh.hasSubscribers) {
633
- return run.apply(this, arguments)
634
- }
635
- const isBeforeEach = this.parent._beforeEach.includes(this)
636
- const isAfterEach = this.parent._afterEach.includes(this)
637
-
638
- const isTestHook = isBeforeEach || isAfterEach
639
-
640
- // we restore the original user defined function
641
- if (this.fn.asyncResource) {
642
- const originalFn = originalFns.get(this.fn)
643
- this.fn = originalFn
644
- }
645
-
646
- if (isTestHook || this.type === 'test') {
647
- const test = isTestHook ? this.ctx.currentTest : this
648
- const asyncResource = getTestAsyncResource(test)
649
-
650
- if (asyncResource) {
651
- // we bind the test fn to the correct async resource
652
- const newFn = asyncResource.bind(this.fn)
653
-
654
- // we store the original function, not to lose it
655
- originalFns.set(newFn, this.fn)
656
- this.fn = newFn
657
-
658
- // Temporarily keep functionality when .asyncResource is removed from node
659
- // in https://github.com/nodejs/node/pull/46432
660
- if (!this.fn.asyncResource) {
661
- this.fn.asyncResource = asyncResource
662
- }
663
- }
664
- }
665
-
666
- return run.apply(this, arguments)
667
- })
668
- return Runnable
669
- })
670
-
671
- addHook({
672
- name: 'mocha-each',
673
- versions: ['>=2.0.1']
674
- }, mochaEachHook)