dd-trace 5.55.0 → 5.56.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 (24) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/package.json +5 -4
  3. package/packages/datadog-core/src/utils/src/set.js +8 -10
  4. package/packages/datadog-instrumentations/src/jest.js +396 -320
  5. package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
  6. package/packages/datadog-plugin-oracledb/src/index.js +2 -1
  7. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  8. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
  9. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
  10. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
  11. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
  12. package/packages/dd-trace/src/appsec/index.js +12 -1
  13. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  14. package/packages/dd-trace/src/baggage.js +2 -2
  15. package/packages/dd-trace/src/config.js +58 -52
  16. package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
  17. package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
  18. package/packages/dd-trace/src/exporters/agent/writer.js +3 -1
  19. package/packages/dd-trace/src/opentracing/propagation/text_map.js +18 -24
  20. package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
  21. package/packages/dd-trace/src/supported-configurations.json +1 -0
  22. package/packages/dd-trace/src/telemetry/telemetry.js +6 -3
  23. package/packages/datadog-core/src/utils/src/get.js +0 -11
  24. package/packages/datadog-core/src/utils/src/has.js +0 -14
@@ -89,6 +89,7 @@ const originalHookFns = new WeakMap()
89
89
  const retriedTestsToNumAttempts = new Map()
90
90
  const newTestsTestStatuses = new Map()
91
91
  const attemptToFixRetriedTestsStatuses = new Map()
92
+ const wrappedWorkers = new WeakSet()
92
93
 
93
94
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
94
95
 
@@ -425,6 +426,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
425
426
  if (event.name === 'add_test') {
426
427
  const originalTestName = this.getTestNameFromAddTestEvent(event, state)
427
428
 
429
+ if (event.failing) {
430
+ return
431
+ }
432
+
428
433
  const isSkipped = event.mode === 'todo' || event.mode === 'skip'
429
434
  if (this.isTestManagementTestsEnabled) {
430
435
  const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
@@ -592,6 +597,17 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
592
597
  })
593
598
  }
594
599
  }
600
+
601
+ teardown () {
602
+ if (this._globalProxy?.propertyToValue) {
603
+ for (const [key] of this._globalProxy.propertyToValue) {
604
+ if (typeof key === 'string' && key.startsWith('_dd')) {
605
+ this._globalProxy.propertyToValue.delete(key)
606
+ }
607
+ }
608
+ }
609
+ return super.teardown()
610
+ }
595
611
  }
596
612
  }
597
613
 
@@ -647,299 +663,305 @@ function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
647
663
  }
648
664
  }
649
665
 
650
- addHook({
651
- name: '@jest/core',
652
- file: 'build/TestScheduler.js',
653
- versions: ['>=27.0.0']
654
- }, (testSchedulerPackage, frameworkVersion) => {
655
- const oldCreateTestScheduler = testSchedulerPackage.createTestScheduler
656
- const newCreateTestScheduler = async function () {
657
- if (!isSuitesSkippingEnabled || hasFilteredSkippableSuites) {
658
- return oldCreateTestScheduler.apply(this, arguments)
659
- }
660
- // If suite skipping is enabled and has not filtered skippable suites yet, we'll attempt to do it
661
- const scheduler = await oldCreateTestScheduler.apply(this, arguments)
662
- shimmer.wrap(scheduler, 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion))
663
- return scheduler
664
- }
665
- testSchedulerPackage.createTestScheduler = newCreateTestScheduler
666
- return testSchedulerPackage
667
- })
666
+ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
667
+ const SearchSource = searchSourcePackage.default ?? searchSourcePackage
668
668
 
669
- addHook({
670
- name: '@jest/core',
671
- file: 'build/TestScheduler.js',
672
- versions: ['>=24.8.0 <27.0.0']
673
- }, (testSchedulerPackage, frameworkVersion) => {
674
- shimmer.wrap(
675
- testSchedulerPackage.default.prototype,
676
- 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
677
- )
678
- return testSchedulerPackage
679
- })
669
+ shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
670
+ const testPaths = await getTestPaths.apply(this, arguments)
671
+ const [{ rootDir, shard }] = arguments
680
672
 
681
- addHook({
682
- name: '@jest/test-sequencer',
683
- versions: ['>=28']
684
- }, (sequencerPackage, frameworkVersion) => {
685
- shimmer.wrap(sequencerPackage.default.prototype, 'shard', shard => function () {
686
- const shardedTests = shard.apply(this, arguments)
673
+ if (isKnownTestsEnabled) {
674
+ const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
675
+ const isFaulty =
676
+ getIsFaultyEarlyFlakeDetection(projectSuites, knownTests?.jest || {}, earlyFlakeDetectionFaultyThreshold)
677
+ if (isFaulty) {
678
+ log.error('Early flake detection is disabled because the number of new suites is too high.')
679
+ isEarlyFlakeDetectionEnabled = false
680
+ isKnownTestsEnabled = false
681
+ const testEnvironmentOptions = testPaths.tests[0]?.context?.config?.testEnvironmentOptions
682
+ // Project config is shared among all tests, so we can modify it here
683
+ if (testEnvironmentOptions) {
684
+ testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled = false
685
+ testEnvironmentOptions._ddIsKnownTestsEnabled = false
686
+ }
687
+ isEarlyFlakeDetectionFaulty = true
688
+ }
689
+ }
687
690
 
688
- if (!shardedTests.length || !isSuitesSkippingEnabled || !skippableSuites.length) {
689
- return shardedTests
691
+ if (shard?.shardCount > 1 || !isSuitesSkippingEnabled || !skippableSuites.length) {
692
+ // If the user is using jest sharding, we want to apply the filtering of tests in the shard process.
693
+ // The reason for this is the following:
694
+ // The tests for different shards are likely being run in different CI jobs so
695
+ // the requests to the skippable endpoint might be done at different times and their responses might be different.
696
+ // If the skippable endpoint is returning different suites and we filter the list of tests here,
697
+ // the base list of tests that is used for sharding might be different,
698
+ // causing the shards to potentially run the same suite.
699
+ return testPaths
690
700
  }
691
- const [test] = shardedTests
692
- const rootDir = test?.context?.config?.rootDir
701
+ const { tests } = testPaths
693
702
 
694
- return applySuiteSkipping(shardedTests, rootDir, frameworkVersion)
703
+ const suitesToRun = applySuiteSkipping(tests, rootDir, frameworkVersion)
704
+ return { ...testPaths, tests: suitesToRun }
695
705
  })
696
- return sequencerPackage
697
- })
698
706
 
699
- function cliWrapper (cli, jestVersion) {
700
- shimmer.wrap(cli, 'runCLI', runCLI => async function () {
701
- let onDone
702
- const configurationPromise = new Promise((resolve) => {
703
- onDone = resolve
704
- })
705
- if (!libraryConfigurationCh.hasSubscribers) {
706
- return runCLI.apply(this, arguments)
707
- }
707
+ return searchSourcePackage
708
+ }
708
709
 
709
- libraryConfigurationCh.publish({ onDone })
710
-
711
- try {
712
- const { err, libraryConfig } = await configurationPromise
713
- if (!err) {
714
- isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
715
- isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
716
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
717
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
718
- earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
719
- isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
720
- isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
721
- testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
722
- isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
723
- }
724
- } catch (err) {
725
- log.error('Jest library configuration error', err)
710
+ function getCliWrapper (isNewJestVersion) {
711
+ return function cliWrapper (cli, jestVersion) {
712
+ if (isNewJestVersion) {
713
+ cli = shimmer.wrap(
714
+ cli,
715
+ 'SearchSource',
716
+ searchSource => searchSourceWrapper(searchSource, jestVersion),
717
+ { replaceGetter: true }
718
+ )
726
719
  }
727
-
728
- if (isKnownTestsEnabled) {
729
- const knownTestsPromise = new Promise((resolve) => {
720
+ return shimmer.wrap(cli, 'runCLI', runCLI => async function () {
721
+ let onDone
722
+ const configurationPromise = new Promise((resolve) => {
730
723
  onDone = resolve
731
724
  })
725
+ if (!libraryConfigurationCh.hasSubscribers) {
726
+ return runCLI.apply(this, arguments)
727
+ }
732
728
 
733
- knownTestsCh.publish({ onDone })
729
+ libraryConfigurationCh.publish({ onDone })
734
730
 
735
731
  try {
736
- const { err, knownTests: receivedKnownTests } = await knownTestsPromise
737
- if (err) {
738
- // We disable EFD if there has been an error in the known tests request
739
- isEarlyFlakeDetectionEnabled = false
740
- isKnownTestsEnabled = false
741
- } else {
742
- knownTests = receivedKnownTests
732
+ const { err, libraryConfig } = await configurationPromise
733
+ if (!err) {
734
+ isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
735
+ isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
736
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
737
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
738
+ earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
739
+ isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
740
+ isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
741
+ testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
742
+ isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
743
743
  }
744
744
  } catch (err) {
745
- log.error('Jest known tests error', err)
745
+ log.error('Jest library configuration error', err)
746
746
  }
747
- }
748
747
 
749
- if (isSuitesSkippingEnabled) {
750
- const skippableSuitesPromise = new Promise((resolve) => {
751
- onDone = resolve
752
- })
748
+ if (isKnownTestsEnabled) {
749
+ const knownTestsPromise = new Promise((resolve) => {
750
+ onDone = resolve
751
+ })
753
752
 
754
- skippableSuitesCh.publish({ onDone })
753
+ knownTestsCh.publish({ onDone })
755
754
 
756
- try {
757
- const { err, skippableSuites: receivedSkippableSuites } = await skippableSuitesPromise
758
- if (!err) {
759
- skippableSuites = receivedSkippableSuites
755
+ try {
756
+ const { err, knownTests: receivedKnownTests } = await knownTestsPromise
757
+ if (err) {
758
+ // We disable EFD if there has been an error in the known tests request
759
+ isEarlyFlakeDetectionEnabled = false
760
+ isKnownTestsEnabled = false
761
+ } else {
762
+ knownTests = receivedKnownTests
763
+ }
764
+ } catch (err) {
765
+ log.error('Jest known tests error', err)
760
766
  }
761
- } catch (err) {
762
- log.error('Jest test-suite skippable error', err)
763
767
  }
764
- }
765
768
 
766
- if (isTestManagementTestsEnabled) {
767
- const testManagementTestsPromise = new Promise((resolve) => {
768
- onDone = resolve
769
- })
769
+ if (isSuitesSkippingEnabled) {
770
+ const skippableSuitesPromise = new Promise((resolve) => {
771
+ onDone = resolve
772
+ })
770
773
 
771
- testManagementTestsCh.publish({ onDone })
774
+ skippableSuitesCh.publish({ onDone })
772
775
 
773
- try {
774
- const { err, testManagementTests: receivedTestManagementTests } = await testManagementTestsPromise
775
- if (!err) {
776
- testManagementTests = receivedTestManagementTests
776
+ try {
777
+ const { err, skippableSuites: receivedSkippableSuites } = await skippableSuitesPromise
778
+ if (!err) {
779
+ skippableSuites = receivedSkippableSuites
780
+ }
781
+ } catch (err) {
782
+ log.error('Jest test-suite skippable error', err)
777
783
  }
778
- } catch (err) {
779
- log.error('Jest test management tests error', err)
780
784
  }
781
- }
782
785
 
783
- if (isImpactedTestsEnabled) {
784
- const impactedTestsPromise = new Promise((resolve) => {
785
- onDone = resolve
786
- })
786
+ if (isTestManagementTestsEnabled) {
787
+ const testManagementTestsPromise = new Promise((resolve) => {
788
+ onDone = resolve
789
+ })
787
790
 
788
- impactedTestsCh.publish({ onDone })
791
+ testManagementTestsCh.publish({ onDone })
789
792
 
790
- try {
791
- const { err, modifiedTests: receivedModifiedTests } = await impactedTestsPromise
792
- if (!err) {
793
- modifiedTests = receivedModifiedTests
793
+ try {
794
+ const { err, testManagementTests: receivedTestManagementTests } = await testManagementTestsPromise
795
+ if (!err) {
796
+ testManagementTests = receivedTestManagementTests
797
+ }
798
+ } catch (err) {
799
+ log.error('Jest test management tests error', err)
794
800
  }
795
- } catch (err) {
796
- log.error('Jest impacted tests error', err)
797
801
  }
798
- }
799
802
 
800
- const processArgv = process.argv.slice(2).join(' ')
801
- testSessionStartCh.publish({ command: `jest ${processArgv}`, frameworkVersion: jestVersion })
803
+ if (isImpactedTestsEnabled) {
804
+ const impactedTestsPromise = new Promise((resolve) => {
805
+ onDone = resolve
806
+ })
802
807
 
803
- const result = await runCLI.apply(this, arguments)
808
+ impactedTestsCh.publish({ onDone })
804
809
 
805
- const {
806
- results: {
807
- success,
808
- coverageMap,
809
- numFailedTestSuites,
810
- numFailedTests,
811
- numTotalTests,
812
- numTotalTestSuites
810
+ try {
811
+ const { err, modifiedTests: receivedModifiedTests } = await impactedTestsPromise
812
+ if (!err) {
813
+ modifiedTests = receivedModifiedTests
814
+ }
815
+ } catch (err) {
816
+ log.error('Jest impacted tests error', err)
817
+ }
813
818
  }
814
- } = result
815
819
 
816
- let testCodeCoverageLinesTotal
820
+ const processArgv = process.argv.slice(2).join(' ')
821
+ testSessionStartCh.publish({ command: `jest ${processArgv}`, frameworkVersion: jestVersion })
817
822
 
818
- if (isUserCodeCoverageEnabled) {
819
- try {
820
- const { pct, total } = coverageMap.getCoverageSummary().lines
821
- testCodeCoverageLinesTotal = total === 0 ? 0 : pct
822
- } catch {
823
- // ignore errors
824
- }
825
- }
826
- let status, error
823
+ const result = await runCLI.apply(this, arguments)
827
824
 
828
- if (success) {
829
- status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
830
- } else {
831
- status = 'fail'
832
- error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
833
- }
834
- let timeoutId
825
+ const {
826
+ results: {
827
+ success,
828
+ coverageMap,
829
+ numFailedTestSuites,
830
+ numFailedTests,
831
+ numTotalTests,
832
+ numTotalTestSuites
833
+ }
834
+ } = result
835
835
 
836
- // Pass the resolve callback to defer it to DC listener
837
- const flushPromise = new Promise((resolve) => {
838
- onDone = () => {
839
- clearTimeout(timeoutId)
840
- resolve()
836
+ let testCodeCoverageLinesTotal
837
+
838
+ if (isUserCodeCoverageEnabled) {
839
+ try {
840
+ const { pct, total } = coverageMap.getCoverageSummary().lines
841
+ testCodeCoverageLinesTotal = total === 0 ? 0 : pct
842
+ } catch {
843
+ // ignore errors
844
+ }
841
845
  }
842
- })
846
+ let status, error
843
847
 
844
- const timeoutPromise = new Promise((resolve) => {
845
- timeoutId = setTimeout(() => {
846
- resolve('timeout')
847
- }, FLUSH_TIMEOUT).unref()
848
- })
848
+ if (success) {
849
+ status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
850
+ } else {
851
+ status = 'fail'
852
+ error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
853
+ }
854
+ let timeoutId
849
855
 
850
- testSessionFinishCh.publish({
851
- status,
852
- isSuitesSkipped,
853
- isSuitesSkippingEnabled,
854
- isCodeCoverageEnabled,
855
- testCodeCoverageLinesTotal,
856
- numSkippedSuites,
857
- hasUnskippableSuites,
858
- hasForcedToRunSuites,
859
- error,
860
- isEarlyFlakeDetectionEnabled,
861
- isEarlyFlakeDetectionFaulty,
862
- isTestManagementTestsEnabled,
863
- onDone
864
- })
856
+ // Pass the resolve callback to defer it to DC listener
857
+ const flushPromise = new Promise((resolve) => {
858
+ onDone = () => {
859
+ clearTimeout(timeoutId)
860
+ resolve()
861
+ }
862
+ })
865
863
 
866
- const waitingResult = await Promise.race([flushPromise, timeoutPromise])
864
+ const timeoutPromise = new Promise((resolve) => {
865
+ timeoutId = setTimeout(() => {
866
+ resolve('timeout')
867
+ }, FLUSH_TIMEOUT).unref()
868
+ })
867
869
 
868
- if (waitingResult === 'timeout') {
869
- log.error('Timeout waiting for the tracer to flush')
870
- }
870
+ testSessionFinishCh.publish({
871
+ status,
872
+ isSuitesSkipped,
873
+ isSuitesSkippingEnabled,
874
+ isCodeCoverageEnabled,
875
+ testCodeCoverageLinesTotal,
876
+ numSkippedSuites,
877
+ hasUnskippableSuites,
878
+ hasForcedToRunSuites,
879
+ error,
880
+ isEarlyFlakeDetectionEnabled,
881
+ isEarlyFlakeDetectionFaulty,
882
+ isTestManagementTestsEnabled,
883
+ onDone
884
+ })
871
885
 
872
- numSkippedSuites = 0
873
-
874
- /**
875
- * If Early Flake Detection (EFD) is enabled the logic is as follows:
876
- * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
877
- * - If just a single attempt passes, we will prevent the test process from failing.
878
- * The rationale behind is the following: you may still be able to block your CI pipeline by gating
879
- * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
880
- */
881
-
882
- if (isEarlyFlakeDetectionEnabled) {
883
- let numFailedTestsToIgnore = 0
884
- for (const testStatuses of newTestsTestStatuses.values()) {
885
- const { pass, fail } = getTestStats(testStatuses)
886
- if (pass > 0) { // as long as one passes, we'll consider the test passed
887
- numFailedTestsToIgnore += fail
888
- }
886
+ const waitingResult = await Promise.race([flushPromise, timeoutPromise])
887
+
888
+ if (waitingResult === 'timeout') {
889
+ log.error('Timeout waiting for the tracer to flush')
889
890
  }
890
- // If every test that failed was an EFD retry, we'll consider the suite passed
891
- if (numFailedTestsToIgnore !== 0 && result.results.numFailedTests === numFailedTestsToIgnore) {
892
- result.results.success = true
891
+
892
+ numSkippedSuites = 0
893
+
894
+ /**
895
+ * If Early Flake Detection (EFD) is enabled the logic is as follows:
896
+ * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
897
+ * - If just a single attempt passes, we will prevent the test process from failing.
898
+ * The rationale behind is the following: you may still be able to block your CI pipeline by gating
899
+ * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
900
+ */
901
+
902
+ if (isEarlyFlakeDetectionEnabled) {
903
+ let numFailedTestsToIgnore = 0
904
+ for (const testStatuses of newTestsTestStatuses.values()) {
905
+ const { pass, fail } = getTestStats(testStatuses)
906
+ if (pass > 0) { // as long as one passes, we'll consider the test passed
907
+ numFailedTestsToIgnore += fail
908
+ }
909
+ }
910
+ // If every test that failed was an EFD retry, we'll consider the suite passed
911
+ if (numFailedTestsToIgnore !== 0 && result.results.numFailedTests === numFailedTestsToIgnore) {
912
+ result.results.success = true
913
+ }
893
914
  }
894
- }
895
915
 
896
- if (isTestManagementTestsEnabled) {
897
- const failedTests = result
898
- .results
899
- .testResults.flatMap(({ testResults, testFilePath: testSuiteAbsolutePath }) => (
900
- testResults.map(({ fullName: testName, status }) => (
901
- { testName, testSuiteAbsolutePath, status }
916
+ if (isTestManagementTestsEnabled) {
917
+ const failedTests = result
918
+ .results
919
+ .testResults.flatMap(({ testResults, testFilePath: testSuiteAbsolutePath }) => (
920
+ testResults.map(({ fullName: testName, status }) => (
921
+ { testName, testSuiteAbsolutePath, status }
922
+ ))
902
923
  ))
903
- ))
904
- .filter(({ status }) => status === 'failed')
905
-
906
- let numFailedQuarantinedTests = 0
907
- let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
908
-
909
- for (const { testName, testSuiteAbsolutePath } of failedTests) {
910
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, result.globalConfig.rootDir)
911
- const originalName = removeAttemptToFixStringFromTestName(testName)
912
- const testManagementTest = testManagementTests
913
- ?.jest
914
- ?.suites
915
- ?.[testSuite]
916
- ?.tests
917
- ?.[originalName]
918
- ?.properties
919
- // This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
920
- if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
921
- numFailedQuarantinedOrDisabledAttemptedToFixTests++
922
- } else if (testManagementTest?.quarantined) {
923
- numFailedQuarantinedTests++
924
+ .filter(({ status }) => status === 'failed')
925
+
926
+ let numFailedQuarantinedTests = 0
927
+ let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
928
+
929
+ for (const { testName, testSuiteAbsolutePath } of failedTests) {
930
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, result.globalConfig.rootDir)
931
+ const originalName = removeAttemptToFixStringFromTestName(testName)
932
+ const testManagementTest = testManagementTests
933
+ ?.jest
934
+ ?.suites
935
+ ?.[testSuite]
936
+ ?.tests
937
+ ?.[originalName]
938
+ ?.properties
939
+ // This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
940
+ if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
941
+ numFailedQuarantinedOrDisabledAttemptedToFixTests++
942
+ } else if (testManagementTest?.quarantined) {
943
+ numFailedQuarantinedTests++
944
+ }
924
945
  }
925
- }
926
946
 
927
- // If every test that failed was quarantined, we'll consider the suite passed
928
- // Note that if a test is attempted to fix,
929
- // it's considered quarantined both if it's disabled and if it's quarantined (it'll run but its status is ignored)
930
- if (
931
- (numFailedQuarantinedOrDisabledAttemptedToFixTests !== 0 || numFailedQuarantinedTests !== 0) &&
932
- result.results.numFailedTests ===
933
- numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
934
- ) {
935
- result.results.success = true
947
+ // If every test that failed was quarantined, we'll consider the suite passed
948
+ // Note that if a test is attempted to fix,
949
+ // it's considered quarantined both if it's disabled and if it's quarantined
950
+ // (it'll run but its status is ignored)
951
+ if (
952
+ (numFailedQuarantinedOrDisabledAttemptedToFixTests !== 0 || numFailedQuarantinedTests !== 0) &&
953
+ result.results.numFailedTests ===
954
+ numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
955
+ ) {
956
+ result.results.success = true
957
+ }
936
958
  }
937
- }
938
959
 
939
- return result
940
- })
941
-
942
- return cli
960
+ return result
961
+ }, {
962
+ replaceGetter: true
963
+ })
964
+ }
943
965
  }
944
966
 
945
967
  function coverageReporterWrapper (coverageReporter) {
@@ -963,6 +985,55 @@ function coverageReporterWrapper (coverageReporter) {
963
985
  return coverageReporter
964
986
  }
965
987
 
988
+ addHook({
989
+ name: '@jest/core',
990
+ file: 'build/TestScheduler.js',
991
+ versions: ['>=27.0.0']
992
+ }, (testSchedulerPackage, frameworkVersion) => {
993
+ const oldCreateTestScheduler = testSchedulerPackage.createTestScheduler
994
+ const newCreateTestScheduler = async function () {
995
+ if (!isSuitesSkippingEnabled || hasFilteredSkippableSuites) {
996
+ return oldCreateTestScheduler.apply(this, arguments)
997
+ }
998
+ // If suite skipping is enabled and has not filtered skippable suites yet, we'll attempt to do it
999
+ const scheduler = await oldCreateTestScheduler.apply(this, arguments)
1000
+ shimmer.wrap(scheduler, 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion))
1001
+ return scheduler
1002
+ }
1003
+ testSchedulerPackage.createTestScheduler = newCreateTestScheduler
1004
+ return testSchedulerPackage
1005
+ })
1006
+
1007
+ addHook({
1008
+ name: '@jest/core',
1009
+ file: 'build/TestScheduler.js',
1010
+ versions: ['>=24.8.0 <27.0.0']
1011
+ }, (testSchedulerPackage, frameworkVersion) => {
1012
+ shimmer.wrap(
1013
+ testSchedulerPackage.default.prototype,
1014
+ 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
1015
+ )
1016
+ return testSchedulerPackage
1017
+ })
1018
+
1019
+ addHook({
1020
+ name: '@jest/test-sequencer',
1021
+ versions: ['>=28']
1022
+ }, (sequencerPackage, frameworkVersion) => {
1023
+ shimmer.wrap(sequencerPackage.default.prototype, 'shard', shard => function () {
1024
+ const shardedTests = shard.apply(this, arguments)
1025
+
1026
+ if (!shardedTests.length || !isSuitesSkippingEnabled || !skippableSuites.length) {
1027
+ return shardedTests
1028
+ }
1029
+ const [test] = shardedTests
1030
+ const rootDir = test?.context?.config?.rootDir
1031
+
1032
+ return applySuiteSkipping(shardedTests, rootDir, frameworkVersion)
1033
+ })
1034
+ return sequencerPackage
1035
+ })
1036
+
966
1037
  addHook({
967
1038
  name: '@jest/reporters',
968
1039
  file: 'build/coverage_reporter.js',
@@ -975,11 +1046,23 @@ addHook({
975
1046
  versions: ['>=26.6.2']
976
1047
  }, coverageReporterWrapper)
977
1048
 
1049
+ addHook({
1050
+ name: '@jest/reporters',
1051
+ versions: ['>=30.0.0']
1052
+ }, (reporters) => {
1053
+ return shimmer.wrap(reporters, 'CoverageReporter', coverageReporterWrapper, { replaceGetter: true })
1054
+ })
1055
+
978
1056
  addHook({
979
1057
  name: '@jest/core',
980
1058
  file: 'build/cli/index.js',
981
- versions: ['>=24.8.0']
982
- }, cliWrapper)
1059
+ versions: ['>=24.8.0 <30.0.0']
1060
+ }, getCliWrapper(false))
1061
+
1062
+ addHook({
1063
+ name: '@jest/core',
1064
+ versions: ['>=30.0.0']
1065
+ }, getCliWrapper(true))
983
1066
 
984
1067
  function jestAdapterWrapper (jestAdapter, jestVersion) {
985
1068
  const adapter = jestAdapter.default ?? jestAdapter
@@ -1033,6 +1116,12 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
1033
1116
  return jestAdapter
1034
1117
  }
1035
1118
 
1119
+ addHook({
1120
+ name: 'jest-circus',
1121
+ file: 'build/runner.js',
1122
+ versions: ['>=30.0.0']
1123
+ }, jestAdapterWrapper)
1124
+
1036
1125
  addHook({
1037
1126
  name: 'jest-circus',
1038
1127
  file: 'build/legacy-code-todo-rewrite/jestAdapter.js',
@@ -1077,21 +1166,19 @@ function configureTestEnvironment (readConfigsResult) {
1077
1166
  }
1078
1167
 
1079
1168
  function jestConfigAsyncWrapper (jestConfig) {
1080
- shimmer.wrap(jestConfig, 'readConfigs', readConfigs => async function () {
1169
+ return shimmer.wrap(jestConfig, 'readConfigs', readConfigs => async function () {
1081
1170
  const readConfigsResult = await readConfigs.apply(this, arguments)
1082
1171
  configureTestEnvironment(readConfigsResult)
1083
1172
  return readConfigsResult
1084
1173
  })
1085
- return jestConfig
1086
1174
  }
1087
1175
 
1088
1176
  function jestConfigSyncWrapper (jestConfig) {
1089
- shimmer.wrap(jestConfig, 'readConfigs', readConfigs => function () {
1177
+ return shimmer.wrap(jestConfig, 'readConfigs', readConfigs => function () {
1090
1178
  const readConfigsResult = readConfigs.apply(this, arguments)
1091
1179
  configureTestEnvironment(readConfigsResult)
1092
1180
  return readConfigsResult
1093
1181
  })
1094
- return jestConfig
1095
1182
  }
1096
1183
 
1097
1184
  addHook({
@@ -1142,51 +1229,9 @@ addHook({
1142
1229
  */
1143
1230
  addHook({
1144
1231
  name: '@jest/core',
1145
- versions: ['>=24.8.0'],
1232
+ versions: ['>=24.8.0 <30.0.0'],
1146
1233
  file: 'build/SearchSource.js'
1147
- }, (searchSourcePackage, frameworkVersion) => {
1148
- const SearchSource = searchSourcePackage.default ?? searchSourcePackage
1149
-
1150
- shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
1151
- const testPaths = await getTestPaths.apply(this, arguments)
1152
- const [{ rootDir, shard }] = arguments
1153
-
1154
- if (isKnownTestsEnabled) {
1155
- const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
1156
- const isFaulty =
1157
- getIsFaultyEarlyFlakeDetection(projectSuites, knownTests?.jest || {}, earlyFlakeDetectionFaultyThreshold)
1158
- if (isFaulty) {
1159
- log.error('Early flake detection is disabled because the number of new suites is too high.')
1160
- isEarlyFlakeDetectionEnabled = false
1161
- isKnownTestsEnabled = false
1162
- const testEnvironmentOptions = testPaths.tests[0]?.context?.config?.testEnvironmentOptions
1163
- // Project config is shared among all tests, so we can modify it here
1164
- if (testEnvironmentOptions) {
1165
- testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled = false
1166
- testEnvironmentOptions._ddIsKnownTestsEnabled = false
1167
- }
1168
- isEarlyFlakeDetectionFaulty = true
1169
- }
1170
- }
1171
-
1172
- if (shard?.shardCount > 1 || !isSuitesSkippingEnabled || !skippableSuites.length) {
1173
- // If the user is using jest sharding, we want to apply the filtering of tests in the shard process.
1174
- // The reason for this is the following:
1175
- // The tests for different shards are likely being run in different CI jobs so
1176
- // the requests to the skippable endpoint might be done at different times and their responses might be different.
1177
- // If the skippable endpoint is returning different suites and we filter the list of tests here,
1178
- // the base list of tests that is used for sharding might be different,
1179
- // causing the shards to potentially run the same suite.
1180
- return testPaths
1181
- }
1182
- const { tests } = testPaths
1183
-
1184
- const suitesToRun = applySuiteSkipping(tests, rootDir, frameworkVersion)
1185
- return { ...testPaths, tests: suitesToRun }
1186
- })
1187
-
1188
- return searchSourcePackage
1189
- })
1234
+ }, searchSourceWrapper)
1190
1235
 
1191
1236
  // from 25.1.0 on, readConfigs becomes async
1192
1237
  addHook({
@@ -1234,19 +1279,27 @@ addHook({
1234
1279
  return runtimePackage
1235
1280
  })
1236
1281
 
1237
- /*
1238
- * This hook does three things:
1239
- * - Pass known tests to the workers.
1240
- * - Pass test management tests to the workers.
1241
- * - Receive trace, coverage and logs payloads from the workers.
1242
- */
1243
- addHook({
1244
- name: 'jest-worker',
1245
- versions: ['>=24.9.0'],
1246
- file: 'build/workers/ChildProcessWorker.js'
1247
- }, (childProcessWorker) => {
1248
- const ChildProcessWorker = childProcessWorker.default
1249
- shimmer.wrap(ChildProcessWorker.prototype, 'send', send => function (request) {
1282
+ function onMessageWrapper (onMessage) {
1283
+ return function () {
1284
+ const [code, data] = arguments[0]
1285
+ if (code === JEST_WORKER_TRACE_PAYLOAD_CODE) { // datadog trace payload
1286
+ workerReportTraceCh.publish(data)
1287
+ return
1288
+ }
1289
+ if (code === JEST_WORKER_COVERAGE_PAYLOAD_CODE) { // datadog coverage payload
1290
+ workerReportCoverageCh.publish(data)
1291
+ return
1292
+ }
1293
+ if (code === JEST_WORKER_LOGS_PAYLOAD_CODE) { // datadog logs payload
1294
+ workerReportLogsCh.publish(data)
1295
+ return
1296
+ }
1297
+ return onMessage.apply(this, arguments)
1298
+ }
1299
+ }
1300
+
1301
+ function sendWrapper (send) {
1302
+ return function (request) {
1250
1303
  if (!isKnownTestsEnabled && !isTestManagementTestsEnabled && !isImpactedTestsEnabled) {
1251
1304
  return send.apply(this, arguments)
1252
1305
  }
@@ -1284,24 +1337,47 @@ addHook({
1284
1337
  }
1285
1338
  }
1286
1339
  }
1287
-
1288
1340
  return send.apply(this, arguments)
1289
- })
1290
- shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', _onMessage => function () {
1291
- const [code, data] = arguments[0]
1292
- if (code === JEST_WORKER_TRACE_PAYLOAD_CODE) { // datadog trace payload
1293
- workerReportTraceCh.publish(data)
1294
- return
1295
- }
1296
- if (code === JEST_WORKER_COVERAGE_PAYLOAD_CODE) { // datadog coverage payload
1297
- workerReportCoverageCh.publish(data)
1298
- return
1299
- }
1300
- if (code === JEST_WORKER_LOGS_PAYLOAD_CODE) { // datadog logs payload
1301
- workerReportLogsCh.publish(data)
1302
- return
1303
- }
1304
- return _onMessage.apply(this, arguments)
1305
- })
1341
+ }
1342
+ }
1343
+
1344
+ function enqueueWrapper (enqueue) {
1345
+ return function () {
1346
+ shimmer.wrap(arguments[0], 'onStart', onStart => function (worker) {
1347
+ if (worker && !wrappedWorkers.has(worker)) {
1348
+ shimmer.wrap(worker._child, 'send', sendWrapper)
1349
+ shimmer.wrap(worker, '_onMessage', onMessageWrapper)
1350
+ worker._child.on('message', worker._onMessage.bind(worker))
1351
+ wrappedWorkers.add(worker)
1352
+ }
1353
+ return onStart.apply(this, arguments)
1354
+ })
1355
+ return enqueue.apply(this, arguments)
1356
+ }
1357
+ }
1358
+
1359
+ /*
1360
+ * This hook does three things:
1361
+ * - Pass known tests to the workers.
1362
+ * - Pass test management tests to the workers.
1363
+ * - Receive trace, coverage and logs payloads from the workers.
1364
+ */
1365
+ addHook({
1366
+ name: 'jest-worker',
1367
+ versions: ['>=24.9.0 <30.0.0'],
1368
+ file: 'build/workers/ChildProcessWorker.js'
1369
+ }, (childProcessWorker) => {
1370
+ const ChildProcessWorker = childProcessWorker.default
1371
+ shimmer.wrap(ChildProcessWorker.prototype, 'send', sendWrapper)
1372
+ shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', onMessageWrapper)
1306
1373
  return childProcessWorker
1307
1374
  })
1375
+
1376
+ addHook({
1377
+ name: 'jest-worker',
1378
+ versions: ['>=30.0.0']
1379
+ }, (jestWorkerPackage) => {
1380
+ shimmer.wrap(jestWorkerPackage.FifoQueue.prototype, 'enqueue', enqueueWrapper)
1381
+ shimmer.wrap(jestWorkerPackage.PriorityQueue.prototype, 'enqueue', enqueueWrapper)
1382
+ return jestWorkerPackage
1383
+ })