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.
- package/LICENSE-3rdparty.csv +1 -0
- package/package.json +5 -4
- package/packages/datadog-core/src/utils/src/set.js +8 -10
- package/packages/datadog-instrumentations/src/jest.js +396 -320
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
- package/packages/datadog-plugin-oracledb/src/index.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
- package/packages/dd-trace/src/appsec/index.js +12 -1
- package/packages/dd-trace/src/appsec/reporter.js +6 -4
- package/packages/dd-trace/src/baggage.js +2 -2
- package/packages/dd-trace/src/config.js +58 -52
- package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
- package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +3 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +18 -24
- package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
- package/packages/dd-trace/src/supported-configurations.json +1 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +6 -3
- package/packages/datadog-core/src/utils/src/get.js +0 -11
- 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
|
-
|
|
651
|
-
|
|
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
|
-
|
|
670
|
-
|
|
671
|
-
|
|
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
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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 (
|
|
689
|
-
|
|
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
|
|
692
|
-
const rootDir = test?.context?.config?.rootDir
|
|
701
|
+
const { tests } = testPaths
|
|
693
702
|
|
|
694
|
-
|
|
703
|
+
const suitesToRun = applySuiteSkipping(tests, rootDir, frameworkVersion)
|
|
704
|
+
return { ...testPaths, tests: suitesToRun }
|
|
695
705
|
})
|
|
696
|
-
return sequencerPackage
|
|
697
|
-
})
|
|
698
706
|
|
|
699
|
-
|
|
700
|
-
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
-
|
|
729
|
-
const
|
|
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
|
-
|
|
729
|
+
libraryConfigurationCh.publish({ onDone })
|
|
734
730
|
|
|
735
731
|
try {
|
|
736
|
-
const { err,
|
|
737
|
-
if (err) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
|
745
|
+
log.error('Jest library configuration error', err)
|
|
746
746
|
}
|
|
747
|
-
}
|
|
748
747
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
748
|
+
if (isKnownTestsEnabled) {
|
|
749
|
+
const knownTestsPromise = new Promise((resolve) => {
|
|
750
|
+
onDone = resolve
|
|
751
|
+
})
|
|
753
752
|
|
|
754
|
-
|
|
753
|
+
knownTestsCh.publish({ onDone })
|
|
755
754
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
769
|
+
if (isSuitesSkippingEnabled) {
|
|
770
|
+
const skippableSuitesPromise = new Promise((resolve) => {
|
|
771
|
+
onDone = resolve
|
|
772
|
+
})
|
|
770
773
|
|
|
771
|
-
|
|
774
|
+
skippableSuitesCh.publish({ onDone })
|
|
772
775
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
786
|
+
if (isTestManagementTestsEnabled) {
|
|
787
|
+
const testManagementTestsPromise = new Promise((resolve) => {
|
|
788
|
+
onDone = resolve
|
|
789
|
+
})
|
|
787
790
|
|
|
788
|
-
|
|
791
|
+
testManagementTestsCh.publish({ onDone })
|
|
789
792
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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
|
-
|
|
801
|
-
|
|
803
|
+
if (isImpactedTestsEnabled) {
|
|
804
|
+
const impactedTestsPromise = new Promise((resolve) => {
|
|
805
|
+
onDone = resolve
|
|
806
|
+
})
|
|
802
807
|
|
|
803
|
-
|
|
808
|
+
impactedTestsCh.publish({ onDone })
|
|
804
809
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
820
|
+
const processArgv = process.argv.slice(2).join(' ')
|
|
821
|
+
testSessionStartCh.publish({ command: `jest ${processArgv}`, frameworkVersion: jestVersion })
|
|
817
822
|
|
|
818
|
-
|
|
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
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
825
|
+
const {
|
|
826
|
+
results: {
|
|
827
|
+
success,
|
|
828
|
+
coverageMap,
|
|
829
|
+
numFailedTestSuites,
|
|
830
|
+
numFailedTests,
|
|
831
|
+
numTotalTests,
|
|
832
|
+
numTotalTestSuites
|
|
833
|
+
}
|
|
834
|
+
} = result
|
|
835
835
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
|
|
864
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
865
|
+
timeoutId = setTimeout(() => {
|
|
866
|
+
resolve('timeout')
|
|
867
|
+
}, FLUSH_TIMEOUT).unref()
|
|
868
|
+
})
|
|
867
869
|
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
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
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
(
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
|
|
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
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
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
|
+
})
|