@@ -8,15 +8,15 @@ import {
88 Events ,
99 RunLocation ,
1010 PrivateRunLocation ,
11- CheckRunId ,
11+ SequenceId ,
1212 DEFAULT_CHECK_RUN_TIMEOUT_SECONDS ,
1313} from '../services/abstract-check-runner'
1414import TestRunner from '../services/test-runner'
1515import { loadChecklyConfig } from '../services/checkly-config-loader'
1616import { filterByFileNamePattern , filterByCheckNamePattern , filterByTags } from '../services/test-filters'
1717import type { Runtime } from '../rest/runtimes'
1818import { AuthCommand } from './authCommand'
19- import { BrowserCheck , Check , HeartbeatCheck , MultiStepCheck , Project , Session } from '../constructs'
19+ import { BrowserCheck , Check , HeartbeatCheck , MultiStepCheck , Project , RetryStrategyBuilder , Session } from '../constructs'
2020import type { Region } from '..'
2121import { splitConfigFilePath , getGitInformation , getCiInformation , getEnvs } from '../services/util'
2222import { createReporters , ReporterType } from '../reporters/reporter'
@@ -26,6 +26,7 @@ import { printLn, formatCheckTitle, CheckStatus } from '../reporters/util'
2626import { uploadSnapshots } from '../services/snapshot-service'
2727
2828const DEFAULT_REGION = 'eu-central-1'
29+ const MAX_RETRIES = 3
2930
3031export default class Test extends AuthCommand {
3132 static coreCommand = true
@@ -100,6 +101,9 @@ export default class Test extends AuthCommand {
100101 description : 'Update any snapshots using the actual result of this test run.' ,
101102 default : false ,
102103 } ) ,
104+ retries : Flags . integer ( {
105+ description : `[default: 0, max: ${ MAX_RETRIES } ] How many times to retry a failing test run.` ,
106+ } ) ,
103107 }
104108
105109 static args = {
@@ -132,6 +136,7 @@ export default class Test extends AuthCommand {
132136 record : shouldRecord ,
133137 'test-session-name' : testSessionName ,
134138 'update-snapshots' : updateSnapshots ,
139+ retries,
135140 } = flags
136141 const filePatterns = argv as string [ ]
137142
@@ -228,6 +233,7 @@ export default class Test extends AuthCommand {
228233 const reporters = createReporters ( reporterTypes , location , verbose )
229234 const repoInfo = getGitInformation ( project . repoUrl )
230235 const ciInfo = getCiInformation ( )
236+ const testRetryStrategy = this . prepareTestRetryStrategy ( retries , checklyConfig ?. cli ?. retries )
231237
232238 const runner = new TestRunner (
233239 config . getAccountId ( ) ,
@@ -241,34 +247,45 @@ export default class Test extends AuthCommand {
241247 ciInfo . environment ,
242248 updateSnapshots ,
243249 configDirectory ,
250+ testRetryStrategy ,
244251 )
245252
246253 runner . on ( Events . RUN_STARTED ,
247- ( checks : Array < { check : any , checkRunId : CheckRunId , testResultId ?: string } > , testSessionId : string ) =>
254+ ( checks : Array < { check : any , sequenceId : SequenceId } > , testSessionId : string ) =>
248255 reporters . forEach ( r => r . onBegin ( checks , testSessionId ) ) ,
249256 )
250257
251- runner . on ( Events . CHECK_INPROGRESS , ( check : any , checkRunId : CheckRunId ) => {
252- reporters . forEach ( r => r . onCheckInProgress ( check , checkRunId ) )
258+ runner . on ( Events . CHECK_INPROGRESS , ( check : any , sequenceId : SequenceId ) => {
259+ reporters . forEach ( r => r . onCheckInProgress ( check , sequenceId ) )
253260 } )
254261
255262 runner . on ( Events . MAX_SCHEDULING_DELAY_EXCEEDED , ( ) => {
256263 reporters . forEach ( r => r . onSchedulingDelayExceeded ( ) )
257264 } )
258265
259- runner . on ( Events . CHECK_SUCCESSFUL , ( checkRunId , check , result , links ?: TestResultsShortLinks ) => {
260- if ( result . hasFailures ) {
261- process . exitCode = 1
262- }
263-
264- reporters . forEach ( r => r . onCheckEnd ( checkRunId , {
266+ runner . on ( Events . CHECK_ATTEMPT_RESULT , ( sequenceId : SequenceId , check , result , links ?: TestResultsShortLinks ) => {
267+ reporters . forEach ( r => r . onCheckAttemptResult ( sequenceId , {
265268 logicalId : check . logicalId ,
266269 sourceFile : check . getSourceFile ( ) ,
267270 ...result ,
268271 } , links ) )
269272 } )
270- runner . on ( Events . CHECK_FAILED , ( checkRunId , check , message : string ) => {
271- reporters . forEach ( r => r . onCheckEnd ( checkRunId , {
273+
274+ runner . on ( Events . CHECK_SUCCESSFUL ,
275+ ( sequenceId : SequenceId , check , result , testResultId , links ?: TestResultsShortLinks ) => {
276+ if ( result . hasFailures ) {
277+ process . exitCode = 1
278+ }
279+
280+ reporters . forEach ( r => r . onCheckEnd ( sequenceId , {
281+ logicalId : check . logicalId ,
282+ sourceFile : check . getSourceFile ( ) ,
283+ ...result ,
284+ } , testResultId , links ) )
285+ } )
286+
287+ runner . on ( Events . CHECK_FAILED , ( sequenceId : SequenceId , check , message : string ) => {
288+ reporters . forEach ( r => r . onCheckEnd ( sequenceId , {
272289 ...check ,
273290 logicalId : check . logicalId ,
274291 sourceFile : check . getSourceFile ( ) ,
@@ -337,6 +354,19 @@ export default class Test extends AuthCommand {
337354 }
338355 }
339356
357+ prepareTestRetryStrategy ( retries ?: number , configRetries ?: number ) {
358+ const numRetries = retries ?? configRetries ?? 0
359+ if ( numRetries > MAX_RETRIES ) {
360+ printLn ( `Defaulting to the maximum of ${ MAX_RETRIES } retries.` )
361+ }
362+ return numRetries
363+ ? RetryStrategyBuilder . fixedStrategy ( {
364+ maxRetries : Math . min ( numRetries , MAX_RETRIES ) ,
365+ baseBackoffSeconds : 0 ,
366+ } )
367+ : null
368+ }
369+
340370 private listChecks ( checks : Array < Check > ) {
341371 // Sort and print the checks in a way that's consistent with AbstractListReporter
342372 const sortedCheckFiles = [ ...new Set ( checks . map ( ( check ) => check . getSourceFile ( ) ) ) ] . sort ( )
0 commit comments