@@ -15,6 +15,7 @@ import { getCurrentTheme } from './helpers/themeUtils';
1515
1616const LAUNCH_RETRY_ATTEMPTS = 5 ;
1717const LAUNCH_RETRY_TIMEOUT = 10000 ;
18+ const FAILED_TESTS_RETRY_ATTEMPTS = 3 ;
1819
1920const wait = async (
2021 timeout : number ,
@@ -55,6 +56,7 @@ interface ParsedArgs {
5556 shadowDom : boolean ;
5657 skipUnstable : boolean ;
5758 disableScreenshots : boolean ;
59+ retryFailed : boolean ;
5860}
5961
6062const TESTCAFE_CONFIG : Partial < TestCafeConfigurationOptions > = {
@@ -101,7 +103,7 @@ function getArgs(): ParsedArgs {
101103 concurrency : 0 ,
102104 browsers : 'chrome' ,
103105 test : '' ,
104- reporter : process . env . CI === 'true' ? 'list' : ' spec',
106+ reporter : ' spec-time ',
105107 componentFolder : '' ,
106108 file : '*' ,
107109 cache : true ,
@@ -112,6 +114,7 @@ function getArgs(): ParsedArgs {
112114 shadowDom : false ,
113115 skipUnstable : true ,
114116 disableScreenshots : false ,
117+ retryFailed : true ,
115118 } ,
116119 } ) as ParsedArgs ;
117120}
@@ -157,77 +160,87 @@ async function main() {
157160 // eslint-disable-next-line no-console
158161 console . info ( 'Browsers:' , browsers ) ;
159162
160- const runner : Runner = testCafe . createRunner ( )
161- . browsers ( browsers )
162- . reporter ( reporter )
163- . src ( [ `./tests/${ componentFolder } /${ file } .ts` ] ) ;
163+ const failedTests : Set < string > = new Set ( ) ;
164164
165- runner . compilerOptions ( {
166- typescript : {
167- customCompilerModulePath : '../../node_modules/typescript' ,
168- } ,
169- } ) ;
170-
171- runner . concurrency ( args . concurrency || 4 ) ;
172-
173- const filters : FilterFunction [ ] = [ ] ;
165+ const createRunner = ( filterByFailedTests = false ) => {
166+ const runner : Runner = testCafe ! . createRunner ( )
167+ . browsers ( browsers )
168+ . reporter ( reporter )
169+ . src ( [ `./tests/${ componentFolder } /${ file } .ts` ] ) ;
174170
175- if ( indices ) {
176- const [ current , total ] = indices . split ( / _ | o f | \\ | \/ / ig) . map ( ( x ) => + x ) ;
177- const fixtures = globSync ( [ `./tests/${ componentFolder } /*.ts` ] ) ;
178- const fixtureChunks = split ( fixtures , total ) ;
179- const targetFixtureChunk = fixtureChunks [ current - 1 ] ?? [ ] ;
180- const targetFixtureChunkSet = new Set ( targetFixtureChunk ) ;
171+ runner . compilerOptions ( {
172+ typescript : {
173+ customCompilerModulePath : '../../node_modules/typescript' ,
174+ } ,
175+ } ) ;
181176
182- /* eslint-disable no-console */
183- console . info ( ' === test run config ===' ) ;
184- console . info ( ` > indices: current = ${ current } | total = ${ total } ` ) ;
185- console . info ( ' > glob: ' , [ `./tests/${ componentFolder } /*.ts` ] ) ;
186- console . info ( ' > all fixtures: ' , fixtureChunks ) ;
187- console . info ( ' > fixtures: ' , targetFixtureChunk , '\n' ) ;
188- /* eslint-enable no-console */
177+ runner . concurrency ( args . concurrency || 2 ) ;
178+
179+ const filters : FilterFunction [ ] = [ ] ;
180+
181+ if ( indices ) {
182+ const [ current , total ] = indices . split ( / _ | o f | \\ | \/ / ig) . map ( ( x ) => + x ) ;
183+ const fixtures = globSync ( [ `./tests/${ componentFolder } /*.ts` ] ) ;
184+ const fixtureChunks = split ( fixtures , total ) ;
185+ const targetFixtureChunk = fixtureChunks [ current - 1 ] ?? [ ] ;
186+ const targetFixtureChunkSet = new Set ( targetFixtureChunk ) ;
187+
188+ /* eslint-disable no-console */
189+ console . info ( ' === test run config ===' ) ;
190+ console . info ( ` > indices: current = ${ current } | total = ${ total } ` ) ;
191+ console . info ( ' > glob: ' , [ `./tests/${ componentFolder } /*.ts` ] ) ;
192+ console . info ( ' > all fixtures: ' , fixtureChunks ) ;
193+ console . info ( ' > fixtures: ' , targetFixtureChunk , '\n' ) ;
194+ /* eslint-enable no-console */
195+
196+ filters . push ( (
197+ _testName : string ,
198+ _fixtureName : string ,
199+ fixturePath : string ,
200+ ) => {
201+ const testPath = fixturePath . split ( '/testcafe-devextreme/' ) [ 1 ] ;
202+ return targetFixtureChunkSet . has ( testPath ) ;
203+ } ) ;
204+ }
189205
190- filters . push ( (
191- _testName : string ,
192- _fixtureName : string ,
193- fixturePath : string ,
194- ) => {
195- const testPath = fixturePath . split ( '/testcafe-devextreme/' ) [ 1 ] ;
196- return targetFixtureChunkSet . has ( testPath ) ;
197- } ) ;
198- }
206+ if ( testName ) {
207+ filters . push ( ( name : string ) => name === testName ) ;
208+ }
199209
200- if ( testName ) {
201- filters . push ( ( name : string ) => name === testName ) ;
202- }
210+ if ( filterByFailedTests && failedTests . size > 0 ) {
211+ filters . push ( ( name : string ) => failedTests . has ( name ) ) ;
212+ }
203213
204- if ( args . skipUnstable ) {
205- filters . push ( (
206- _testName : string ,
207- _fixtureName : string ,
208- _fixturePath : string ,
209- testMeta ?: any ,
210- ) => ! ( testMeta ) ?. unstable ) ;
211- }
214+ if ( args . skipUnstable ) {
215+ filters . push ( (
216+ _testName : string ,
217+ _fixtureName : string ,
218+ _fixturePath : string ,
219+ testMeta ?: any ,
220+ ) => ! ( testMeta ) ?. unstable ) ;
221+ }
212222
213- if ( filters . length ) {
214- runner . filter ( ( ...filterArgs : Parameters < FilterFunction > ) => {
215- // eslint-disable-next-line @typescript-eslint/prefer-for-of
216- for ( let i = 0 ; i < filters . length ; i += 1 ) {
217- if ( ! filters [ i ] ( ...filterArgs ) ) {
218- return false ;
223+ if ( filters . length ) {
224+ runner . filter ( ( ...filterArgs : Parameters < FilterFunction > ) => {
225+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
226+ for ( let i = 0 ; i < filters . length ; i += 1 ) {
227+ if ( ! filters [ i ] ( ...filterArgs ) ) {
228+ return false ;
229+ }
219230 }
220- }
221- return true ;
222- } ) ;
223- }
231+ return true ;
232+ } ) ;
233+ }
224234
225- if ( args . cache ) {
226- ( runner as any ) . cache = args . cache ;
227- }
235+ if ( args . cache ) {
236+ ( runner as any ) . cache = args . cache ;
237+ }
238+
239+ return runner ;
240+ } ;
228241
229242 const runOptions : RunOptions = {
230- quarantineMode : { successThreshold : 1 , attemptLimit : 2 } ,
243+ quarantineMode : false ,
231244 // @ts -expect-error ts-error
232245 hooks : {
233246 test : {
@@ -266,6 +279,14 @@ async function main() {
266279 } ,
267280 after : async ( t : TestController ) => {
268281 await clearTestPage ( t ) ;
282+
283+ if ( args . retryFailed ) {
284+ // @ts -expect-error ts-errors
285+ const { test, errs } = t . testRun ;
286+ if ( errs && errs . length > 0 ) {
287+ failedTests . add ( test . name ) ;
288+ }
289+ }
269290 } ,
270291 } ,
271292 } ,
@@ -275,7 +296,43 @@ async function main() {
275296 runOptions . disableScreenshots = true ;
276297 }
277298
278- const failedCount = await retry ( ( ) => runner . run ( runOptions ) , LAUNCH_RETRY_ATTEMPTS ) ;
299+ // First run - all tests
300+ const runner = createRunner ( false ) ;
301+ let failedCount = await retry ( ( ) => runner . run ( runOptions ) , LAUNCH_RETRY_ATTEMPTS ) ;
302+
303+ // Retry failed tests if enabled and there are failures
304+ if ( args . retryFailed && failedTests . size > 0 && failedCount > 0 ) {
305+ /* eslint-disable no-console */
306+ console . info ( '\n' ) ;
307+ console . info ( '=' . repeat ( 60 ) ) ;
308+ console . info ( `RETRYING ${ failedTests . size } FAILED TEST(S)` ) ;
309+ console . info ( '=' . repeat ( 60 ) ) ;
310+ console . info ( 'Failed tests:' ) ;
311+ failedTests . forEach ( ( failedTestName ) => console . info ( ` - ${ failedTestName } ` ) ) ;
312+ console . info ( '=' . repeat ( 60 ) ) ;
313+ console . info ( '\n' ) ;
314+ /* eslint-enable no-console */
315+
316+ const retryRunner = createRunner ( true ) ;
317+ const retryFailedCount = await retry (
318+ ( ) => retryRunner . run ( runOptions ) ,
319+ FAILED_TESTS_RETRY_ATTEMPTS ,
320+ ) ;
321+
322+ /* eslint-disable no-console */
323+ console . info ( '\n' ) ;
324+ console . info ( '=' . repeat ( 60 ) ) ;
325+ console . info ( 'RETRY RESULTS' ) ;
326+ console . info ( '=' . repeat ( 60 ) ) ;
327+ console . info ( `Initially failed: ${ failedTests . size } ` ) ;
328+ console . info ( `Still failing after retry: ${ retryFailedCount } ` ) ;
329+ console . info ( `Successfully passed on retry: ${ failedTests . size - retryFailedCount } ` ) ;
330+ console . info ( '=' . repeat ( 60 ) ) ;
331+ console . info ( '\n' ) ;
332+ /* eslint-enable no-console */
333+
334+ failedCount = retryFailedCount ;
335+ }
279336
280337 await testCafe . close ( ) ;
281338
0 commit comments