2021-06-06 17:09:53 -07:00
/ * *
* Copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2023-02-07 15:11:44 -08:00
import { test , expect } from './playwright-test-fixtures' ;
2021-06-06 17:09:53 -07:00
test ( 'should retry failures' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'retry-failures.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'flake' , async ( { } , testInfo ) = > {
// Passes on the second run.
expect ( testInfo . retry ) . toBe ( 1 ) ;
} ) ;
`
} , { retries : 10 } ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . flaky ) . toBe ( 1 ) ;
expect ( result . results . length ) . toBe ( 2 ) ;
expect ( result . results [ 0 ] . workerIndex ) . toBe ( 0 ) ;
expect ( result . results [ 0 ] . retry ) . toBe ( 0 ) ;
expect ( result . results [ 0 ] . status ) . toBe ( 'failed' ) ;
expect ( result . results [ 1 ] . workerIndex ) . toBe ( 1 ) ;
expect ( result . results [ 1 ] . retry ) . toBe ( 1 ) ;
expect ( result . results [ 1 ] . status ) . toBe ( 'passed' ) ;
} ) ;
test ( 'should retry based on config' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'playwright.config.js' : `
module . exports = { projects : [
{ retries : 0 , name : 'no-retries' } ,
{ retries : 2 , name : 'two-retries' } ,
] } ;
` ,
'a.test.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'pass' , ( { } , testInfo ) = > {
// Passes on the third run.
expect ( testInfo . retry ) . toBe ( 2 ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . flaky ) . toBe ( 1 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . results . length ) . toBe ( 4 ) ;
} ) ;
2022-10-27 15:53:27 -07:00
test ( 'should retry based on test.describe.configure' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'playwright.config.js' : `
module . exports = { retries : 2 } ;
` ,
'a.test.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-10-27 15:53:27 -07:00
test . describe . configure ( { retries : 1 } ) ;
test ( 'fail 1' , ( { } , testInfo ) = > {
console . log ( '%%fail1-' + testInfo . retry ) ;
expect ( 1 ) . toBe ( 2 ) ;
} ) ;
` ,
'b.test.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-10-27 15:53:27 -07:00
test ( 'fail 4' , ( { } , testInfo ) = > {
console . log ( '%%fail4-' + testInfo . retry ) ;
expect ( 1 ) . toBe ( 2 ) ;
} ) ;
test . describe ( ( ) = > {
test . describe . configure ( { retries : 0 } ) ;
test ( 'fail 2' , ( { } , testInfo ) = > {
console . log ( '%%fail2-' + testInfo . retry ) ;
expect ( 1 ) . toBe ( 2 ) ;
} ) ;
test . describe ( ( ) = > {
test . describe . configure ( { retries : 1 } ) ;
test . describe ( ( ) = > {
test ( 'fail 3' , ( { } , testInfo ) = > {
console . log ( '%%fail3-' + testInfo . retry ) ;
expect ( 1 ) . toBe ( 2 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
` ,
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 4 ) ;
expect ( result . results . length ) . toBe ( 8 ) ;
expect ( result . output ) . toContain ( '%%fail1-0' ) ;
expect ( result . output ) . toContain ( '%%fail1-1' ) ;
expect ( result . output ) . not . toContain ( '%%fail1-2' ) ;
expect ( result . output ) . toContain ( '%%fail4-0' ) ;
expect ( result . output ) . toContain ( '%%fail4-1' ) ;
expect ( result . output ) . toContain ( '%%fail4-2' ) ;
expect ( result . output ) . not . toContain ( '%%fail4-3' ) ;
expect ( result . output ) . toContain ( '%%fail2-0' ) ;
expect ( result . output ) . not . toContain ( '%%fail2-1' ) ;
expect ( result . output ) . toContain ( '%%fail3-0' ) ;
expect ( result . output ) . toContain ( '%%fail3-1' ) ;
expect ( result . output ) . not . toContain ( '%%fail3-2' ) ;
} ) ;
2021-06-06 17:09:53 -07:00
test ( 'should retry timeout' , async ( { runInlineTest } ) = > {
const { exitCode , passed , failed , output } = await runInlineTest ( {
'one-timeout.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'timeout' , async ( ) = > {
await new Promise ( f = > setTimeout ( f , 10000 ) ) ;
} ) ;
`
2023-02-18 13:08:17 -08:00
} , { timeout : 1000 , retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( passed ) . toBe ( 0 ) ;
expect ( failed ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
expect ( output . split ( '\n' ) [ 2 ] ) . toBe ( '× × T' ) ;
2021-06-06 17:09:53 -07:00
} ) ;
test ( 'should fail on unexpected pass with retries' , async ( { runInlineTest } ) = > {
const { exitCode , failed , output } = await runInlineTest ( {
'unexpected-pass.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'succeeds' , ( ) = > {
test . fail ( ) ;
expect ( 1 + 1 ) . toBe ( 2 ) ;
} ) ;
`
} , { retries : 1 } ) ;
expect ( exitCode ) . toBe ( 1 ) ;
expect ( failed ) . toBe ( 1 ) ;
2021-08-25 12:19:50 -07:00
expect ( output ) . toContain ( 'Expected to fail, but passed.' ) ;
2021-06-06 17:09:53 -07:00
} ) ;
2021-08-25 12:19:50 -07:00
test ( 'should retry unexpected pass' , async ( { runInlineTest } ) = > {
2021-06-06 17:09:53 -07:00
const { exitCode , passed , failed , output } = await runInlineTest ( {
'unexpected-pass.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'succeeds' , ( ) = > {
test . fail ( ) ;
expect ( 1 + 1 ) . toBe ( 2 ) ;
} ) ;
`
2023-02-18 13:08:17 -08:00
} , { retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( passed ) . toBe ( 0 ) ;
expect ( failed ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
expect ( output . split ( '\n' ) [ 2 ] ) . toBe ( '× × F' ) ;
2021-06-06 17:09:53 -07:00
} ) ;
test ( 'should not retry expected failure' , async ( { runInlineTest } ) = > {
const { exitCode , passed , failed , output } = await runInlineTest ( {
'expected-failure.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'fails' , ( ) = > {
test . fail ( ) ;
expect ( 1 + 1 ) . toBe ( 3 ) ;
} ) ;
test ( 'non-empty remaining' , ( ) = > {
expect ( 1 + 1 ) . toBe ( 2 ) ;
} ) ;
`
2023-02-18 13:08:17 -08:00
} , { retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( exitCode ) . toBe ( 0 ) ;
expect ( passed ) . toBe ( 2 ) ;
expect ( failed ) . toBe ( 0 ) ;
2023-02-07 15:11:44 -08:00
expect ( output . split ( '\n' ) [ 2 ] ) . toBe ( '··' ) ;
2021-06-06 17:09:53 -07:00
} ) ;
test ( 'should retry unhandled rejection' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'unhandled-rejection.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test ( 'unhandled rejection' , async ( ) = > {
setTimeout ( ( ) = > {
throw new Error ( 'Unhandled rejection in the test' ) ;
} ) ;
2021-07-28 15:43:37 -07:00
await new Promise ( f = > setTimeout ( f , 2000 ) ) ;
2021-06-06 17:09:53 -07:00
} ) ;
`
2023-02-18 13:08:17 -08:00
} , { retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
expect ( result . output . split ( '\n' ) [ 2 ] ) . toBe ( '× × F' ) ;
2021-06-06 17:09:53 -07:00
expect ( result . output ) . toContain ( 'Unhandled rejection' ) ;
} ) ;
test ( 'should retry beforeAll failure' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.js' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test . beforeAll ( async ( ) = > {
throw new Error ( 'BeforeAll is bugged!' ) ;
} ) ;
test ( 'passing test' , async ( ) = > {
} ) ;
2021-08-24 12:22:16 -07:00
test ( 'another passing test' , async ( ) = > {
} ) ;
2021-06-06 17:09:53 -07:00
`
2023-02-18 13:08:17 -08:00
} , { retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
2024-04-25 13:39:14 -07:00
expect ( result . didNotRun ) . toBe ( 1 ) ;
2023-11-27 16:08:20 -08:00
expect ( result . output . split ( '\n' ) [ 2 ] ) . toBe ( '× °× °F°' ) ;
2021-06-06 17:09:53 -07:00
expect ( result . output ) . toContain ( 'BeforeAll is bugged!' ) ;
} ) ;
test ( 'should retry worker fixture setup failure' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'helper.ts' : `
2023-02-14 19:20:56 -08:00
import { test as base , expect } from '@playwright/test' ;
export const test = base . extend ( {
2021-06-06 17:09:53 -07:00
worker : [ async ( ) = > {
throw new Error ( 'worker setup is bugged!' ) ;
} , { scope : 'worker' } ]
} ) ;
` ,
'a.spec.ts' : `
import { test } from './helper' ;
test ( 'passing test' , async ( { worker } ) = > {
} ) ;
`
2023-02-18 13:08:17 -08:00
} , { retries : 2 , reporter : 'dot' } ) ;
2021-06-06 17:09:53 -07:00
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
expect ( result . output . split ( '\n' ) [ 2 ] ) . toBe ( '× × F' ) ;
2021-06-06 17:09:53 -07:00
expect ( result . output ) . toContain ( 'worker setup is bugged!' ) ;
} ) ;
2023-08-09 16:35:14 -07:00
test ( 'failed and skipped on retry should be marked as flaky' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
import { test } from '@playwright/test' ;
test ( 'flaky test' , async ( { } , testInfo ) = > {
if ( ! testInfo . retry )
throw new Error ( 'Failed on first run' ) ;
test . skip ( true , 'Skipped on first retry' ) ;
} ) ;
`
} , { retries : 1 , reporter : 'dot' } ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 0 ) ;
expect ( result . flaky ) . toBe ( 1 ) ;
expect ( result . output ) . toContain ( 'Failed on first run' ) ;
2025-04-15 09:07:42 +02:00
expect ( result . report . suites [ 0 ] . specs [ 0 ] . tests [ 0 ] . annotations ) . toEqual ( [ { type : 'skip' , description : 'Skipped on first retry' , location : expect.anything ( ) } ] ) ;
2023-08-09 16:35:14 -07:00
} ) ;