@@ -12,6 +12,8 @@ let isModifiedTest = false
1212let isTestIsolationEnabled = false
1313// Array of test names that have been retried and the reason
1414const retryReasonsByTestName = new Map ( )
15+ // Track quarantined test errors - we catch them in Cypress.on('fail') but need to report to Datadog
16+ const quarantinedTestErrors = new Map ( )
1517
1618// We need to grab the original window as soon as possible,
1719// in case the test changes the origin. If the test does change the origin,
@@ -46,6 +48,30 @@ function getTestProperties (testName) {
4648 return { isAttemptToFix, isDisabled, isQuarantined }
4749}
4850
51+ // Catch test failures for quarantined tests and suppress them
52+ // By not re-throwing the error, Cypress marks the test as passed
53+ // This allows quarantined tests to run but not affect the exit code
54+ Cypress . on ( 'fail' , ( err , runnable ) => {
55+ if ( ! isTestManagementEnabled ) {
56+ throw err
57+ }
58+
59+ const testName = runnable . fullTitle ( )
60+ const { isQuarantined, isAttemptToFix } = getTestProperties ( testName )
61+
62+ // For pure quarantined tests (not attemptToFix), suppress the failure
63+ // This makes the test "pass" from Cypress's perspective while we still track the error
64+ if ( isQuarantined && ! isAttemptToFix ) {
65+ // Store the error so we can report it to Datadog in afterEach
66+ quarantinedTestErrors . set ( testName , err )
67+ // Don't re-throw - this prevents Cypress from marking the test as failed
68+ return
69+ }
70+
71+ // For all other tests (including attemptToFix), let the error propagate normally
72+ throw err
73+ } )
74+
4975function getRetriedTests ( test , numRetries , tags ) {
5076 const retriedTests = [ ]
5177 for ( let retryIndex = 0 ; retryIndex < numRetries ; retryIndex ++ ) {
@@ -185,16 +211,31 @@ after(() => {
185211
186212afterEach ( function ( ) {
187213 const currentTest = Cypress . mocha . getRunner ( ) . suite . ctx . currentTest
214+ const testName = currentTest . fullTitle ( )
215+
216+ // Check if this was a quarantined test that we suppressed the failure for
217+ const quarantinedError = quarantinedTestErrors . get ( testName )
218+ const isQuarantinedTestThatFailed = ! ! quarantinedError
219+
220+ // For quarantined tests, convert Error to a serializable format for cy.task
221+ const errorToReport = isQuarantinedTestThatFailed
222+ ? { message : quarantinedError . message , stack : quarantinedError . stack }
223+ : currentTest . err
224+
188225 const testInfo = {
189- testName : currentTest . fullTitle ( ) ,
226+ testName,
190227 testSuite : Cypress . mocha . getRootSuite ( ) . file ,
191228 testSuiteAbsolutePath : Cypress . spec && Cypress . spec . absolute ,
192- state : currentTest . state ,
193- error : currentTest . err ,
229+ // For quarantined tests, report the actual state (failed) to Datadog, not what Cypress thinks (passed)
230+ state : isQuarantinedTestThatFailed ? 'failed' : currentTest . state ,
231+ // For quarantined tests, include the actual error that was suppressed
232+ error : errorToReport ,
194233 isNew : currentTest . _ddIsNew ,
195234 isEfdRetry : currentTest . _ddIsEfdRetry ,
196235 isAttemptToFix : currentTest . _ddIsAttemptToFix ,
197236 isModified : currentTest . _ddIsModified ,
237+ // Mark quarantined tests that failed so the plugin knows to tag them appropriately
238+ isQuarantined : isQuarantinedTestThatFailed ,
198239 }
199240 try {
200241 testInfo . testSourceLine = Cypress . mocha . getRunner ( ) . currentRunnable . invocationDetails . line
@@ -209,5 +250,11 @@ afterEach(function () {
209250 } catch {
210251 // ignore error and continue
211252 }
253+
254+ // Clean up the quarantined error tracking
255+ if ( isQuarantinedTestThatFailed ) {
256+ quarantinedTestErrors . delete ( testName )
257+ }
258+
212259 cy . task ( 'dd:afterEach' , { test : testInfo , coverage } )
213260} )
0 commit comments