@@ -129,13 +129,41 @@ function getTestEnvironmentOptions (config) {
129129 return { }
130130}
131131
132+ const MAX_IGNORED_TEST_NAMES = 10
133+
132134function getTestStats ( testStatuses ) {
133135 return testStatuses . reduce ( ( acc , testStatus ) => {
134136 acc [ testStatus ] ++
135137 return acc
136138 } , { pass : 0 , fail : 0 } )
137139}
138140
141+ /**
142+ * @param {string[] } efdNames
143+ * @param {string[] } quarantineNames
144+ * @param {number } totalCount
145+ */
146+ function logIgnoredFailuresSummary ( efdNames , quarantineNames , totalCount ) {
147+ const names = [ ]
148+ for ( const n of efdNames ) {
149+ names . push ( { name : n , reason : 'Early Flake Detection' } )
150+ }
151+ for ( const n of quarantineNames ) {
152+ names . push ( { name : n , reason : 'Quarantine' } )
153+ }
154+ const shown = names . slice ( 0 , MAX_IGNORED_TEST_NAMES )
155+ const more = names . length - shown . length
156+ const moreSuffix = more > 0 ? `\n ... and ${ more } more` : ''
157+ const list = shown . map ( ( { name, reason } ) => ` • ${ name } (${ reason } )` ) . join ( '\n' )
158+ const line = '-' . repeat ( 50 )
159+ // eslint-disable-next-line no-console -- Intentional user-facing message when exit code is flipped
160+ console . warn (
161+ `\n${ line } \nDatadog Test Optimization\n${ line } \n` +
162+ `${ totalCount } test failure(s) were ignored. Exit code set to 0.\n\n` +
163+ `${ list } ${ moreSuffix } \n`
164+ )
165+ }
166+
139167function getWrappedEnvironment ( BaseEnvironment , jestVersion ) {
140168 return class DatadogEnvironment extends BaseEnvironment {
141169 constructor ( config , context ) {
@@ -1025,11 +1053,30 @@ function getCliWrapper (isNewJestVersion) {
10251053 * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
10261054 */
10271055 let numEfdFailedTestsToIgnore = 0
1056+ const efdIgnoredNames = [ ]
1057+ const quarantineIgnoredNames = [ ]
1058+
1059+ // Build fullName -> suite map from results (for EFD display)
1060+ const fullNameToSuite = new Map ( )
1061+ for ( const { testResults, testFilePath } of result . results . testResults ) {
1062+ const suite = getTestSuitePath ( testFilePath , result . globalConfig . rootDir )
1063+ for ( const { fullName } of testResults ) {
1064+ const name = testSuiteAbsolutePathsWithFastCheck . has ( testFilePath )
1065+ ? fullName . replace ( SEED_SUFFIX_RE , '' )
1066+ : fullName
1067+ fullNameToSuite . set ( name , suite )
1068+ }
1069+ }
1070+
1071+ /** @type {{ efdNames: string[], quarantineNames: string[], totalCount: number } | undefined } */
1072+ let ignoredFailuresSummary
10281073 if ( isEarlyFlakeDetectionEnabled ) {
1029- for ( const testStatuses of newTestsTestStatuses . values ( ) ) {
1074+ for ( const [ testName , testStatuses ] of newTestsTestStatuses ) {
10301075 const { pass, fail } = getTestStats ( testStatuses )
10311076 if ( pass > 0 ) { // as long as one passes, we'll consider the test passed
10321077 numEfdFailedTestsToIgnore += fail
1078+ const suite = fullNameToSuite . get ( testName )
1079+ efdIgnoredNames . push ( suite ? `${ suite } › ${ testName } ` : testName )
10331080 }
10341081 }
10351082 // If every test that failed was an EFD retry, we'll consider the suite passed
@@ -1039,6 +1086,11 @@ function getCliWrapper (isNewJestVersion) {
10391086 result . results . numFailedTests === numEfdFailedTestsToIgnore
10401087 ) {
10411088 result . results . success = true
1089+ ignoredFailuresSummary = {
1090+ efdNames : efdIgnoredNames ,
1091+ quarantineNames : [ ] ,
1092+ totalCount : numEfdFailedTestsToIgnore ,
1093+ }
10421094 }
10431095 }
10441096
@@ -1073,22 +1125,31 @@ function getCliWrapper (isNewJestVersion) {
10731125 // This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
10741126 if ( testManagementTest ?. attempt_to_fix && ( testManagementTest ?. quarantined || testManagementTest ?. disabled ) ) {
10751127 numFailedQuarantinedOrDisabledAttemptedToFixTests ++
1128+ quarantineIgnoredNames . push ( `${ testSuite } › ${ testName } ` )
10761129 } else if ( testManagementTest ?. quarantined ) {
10771130 numFailedQuarantinedTests ++
1131+ quarantineIgnoredNames . push ( `${ testSuite } › ${ testName } ` )
10781132 }
10791133 }
10801134
10811135 // If every test that failed was quarantined, we'll consider the suite passed
10821136 // Note that if a test is attempted to fix,
10831137 // it's considered quarantined both if it's disabled and if it's quarantined
10841138 // (it'll run but its status is ignored)
1139+ // Skip if EFD block already flipped (to avoid logging twice)
10851140 if (
1141+ ! result . results . success &&
10861142 ! mustNotFlipSuccess &&
10871143 ( numFailedQuarantinedOrDisabledAttemptedToFixTests !== 0 || numFailedQuarantinedTests !== 0 ) &&
10881144 result . results . numFailedTests ===
10891145 numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
10901146 ) {
10911147 result . results . success = true
1148+ ignoredFailuresSummary = {
1149+ efdNames : [ ] ,
1150+ quarantineNames : quarantineIgnoredNames ,
1151+ totalCount : numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests ,
1152+ }
10921153 }
10931154 }
10941155
@@ -1106,6 +1167,11 @@ function getCliWrapper (isNewJestVersion) {
11061167 result . results . numFailedTests === totalIgnoredFailures
11071168 ) {
11081169 result . results . success = true
1170+ ignoredFailuresSummary = {
1171+ efdNames : efdIgnoredNames ,
1172+ quarantineNames : quarantineIgnoredNames ,
1173+ totalCount : totalIgnoredFailures ,
1174+ }
11091175 }
11101176 }
11111177
@@ -1163,6 +1229,14 @@ function getCliWrapper (isNewJestVersion) {
11631229 } )
11641230 }
11651231
1232+ if ( ignoredFailuresSummary ) {
1233+ logIgnoredFailuresSummary (
1234+ ignoredFailuresSummary . efdNames ,
1235+ ignoredFailuresSummary . quarantineNames ,
1236+ ignoredFailuresSummary . totalCount
1237+ )
1238+ }
1239+
11661240 numSkippedSuites = 0
11671241
11681242 return result
0 commit comments