@@ -192,6 +192,33 @@ class Codecept {
192192 }
193193 }
194194
195+ /**
196+ * Filter tests to only include failed tests from a failed tests data
197+ *
198+ * @param {Array<object> } failedTests - Array of failed test objects with uid, title, file, etc.
199+ */
200+ filterByFailedTests ( failedTests ) {
201+ if ( ! failedTests || failedTests . length === 0 ) {
202+ this . testFiles = [ ]
203+ return
204+ }
205+
206+ // Extract unique file paths from failed tests
207+ const failedTestFiles = [ ...new Set ( failedTests . map ( test => test . file ) . filter ( Boolean ) ) ]
208+
209+ // Filter testFiles to only include files that contain failed tests
210+ this . testFiles = this . testFiles . filter ( file => {
211+ const normalizedFile = fsPath . resolve ( file )
212+ return failedTestFiles . some ( failedFile => {
213+ const normalizedFailedFile = fsPath . resolve ( failedFile )
214+ return normalizedFile === normalizedFailedFile
215+ } )
216+ } )
217+
218+ // Store failed test info for filtering during test execution
219+ this . failedTestsFilter = failedTests
220+ }
221+
195222 /**
196223 * Apply sharding to test files based on shard configuration
197224 *
@@ -246,6 +273,13 @@ class Codecept {
246273 }
247274 mocha . files = mocha . files . filter ( t => fsPath . basename ( t , '.js' ) === test || t === test )
248275 }
276+
277+ // Apply failed test filtering if specified
278+ if ( this . failedTestsFilter && this . failedTestsFilter . length > 0 ) {
279+ mocha . loadFiles ( )
280+ this . _filterMochaTestsByFailedTests ( mocha , this . failedTestsFilter )
281+ }
282+
249283 const done = ( ) => {
250284 event . emit ( event . all . result , container . result ( ) )
251285 event . emit ( event . all . after , this )
@@ -262,6 +296,67 @@ class Codecept {
262296 } )
263297 }
264298
299+ /**
300+ * Filter Mocha tests to only include failed tests
301+ *
302+ * @private
303+ * @param {Mocha } mocha - Mocha instance
304+ * @param {Array<object> } failedTests - Array of failed test objects
305+ */
306+ _filterMochaTestsByFailedTests ( mocha , failedTests ) {
307+ const failedTestUids = new Set ( failedTests . map ( t => t . uid ) . filter ( Boolean ) )
308+ const failedTestTitles = new Set ( failedTests . map ( t => t . title ) . filter ( Boolean ) )
309+ const failedTestFullTitles = new Set ( failedTests . map ( t => t . fullTitle ) . filter ( Boolean ) )
310+
311+ // Remove tests that are not in the failed tests list
312+ for ( const suite of mocha . suite . suites ) {
313+ suite . tests = suite . tests . filter ( test => {
314+ return failedTestUids . has ( test . uid ) || failedTestTitles . has ( test . title ) || failedTestFullTitles . has ( test . fullTitle ( ) )
315+ } )
316+
317+ // Remove nested suites' tests as well
318+ this . _filterNestedSuites ( suite , failedTestUids , failedTestTitles , failedTestFullTitles )
319+ }
320+
321+ // Clean up empty suites
322+ mocha . suite . suites = mocha . suite . suites . filter ( suite => this . _hasTests ( suite ) )
323+ }
324+
325+ /**
326+ * Recursively filter nested suites
327+ *
328+ * @private
329+ * @param {Suite } suite - Mocha suite
330+ * @param {Set<string> } failedTestUids - Set of failed test UIDs
331+ * @param {Set<string> } failedTestTitles - Set of failed test titles
332+ * @param {Set<string> } failedTestFullTitles - Set of failed test full titles
333+ */
334+ _filterNestedSuites ( suite , failedTestUids , failedTestTitles , failedTestFullTitles ) {
335+ for ( const childSuite of suite . suites || [ ] ) {
336+ childSuite . tests = childSuite . tests . filter ( test => {
337+ return failedTestUids . has ( test . uid ) || failedTestTitles . has ( test . title ) || failedTestFullTitles . has ( test . fullTitle ( ) )
338+ } )
339+
340+ this . _filterNestedSuites ( childSuite , failedTestUids , failedTestTitles , failedTestFullTitles )
341+ }
342+
343+ // Remove empty child suites
344+ suite . suites = suite . suites . filter ( childSuite => this . _hasTests ( childSuite ) )
345+ }
346+
347+ /**
348+ * Check if suite or any nested suite has tests
349+ *
350+ * @private
351+ * @param {Suite } suite - Mocha suite
352+ * @returns {boolean }
353+ */
354+ _hasTests ( suite ) {
355+ if ( suite . tests && suite . tests . length > 0 ) return true
356+ if ( suite . suites && suite . suites . some ( childSuite => this . _hasTests ( childSuite ) ) ) return true
357+ return false
358+ }
359+
265360 static version ( ) {
266361 return JSON . parse ( readFileSync ( `${ __dirname } /../package.json` , 'utf8' ) ) . version
267362 }
0 commit comments