2025-02-19 10:11:04 -08: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 .
* /
2025-03-31 07:16:00 -07:00
import { test , expect } from './playwright-test-fixtures' ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
const description = 'This async call was not awaited by the end of the test. This can cause flakiness. It is recommended to run ESLint with "@typescript-eslint/no-floating-promises" to verify.' ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test . describe . configure ( { mode : 'parallel' } ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test . describe ( 'await' , ( ) = > {
test ( 'should not care about non-API promises' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test } from '@playwright/test' ;
2025-04-08 07:34:12 -07:00
test ( 'test' , async ( ) = > {
2025-03-31 07:16:00 -07:00
new Promise ( ( ) = > { } ) ;
2025-04-08 07:34:12 -07:00
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should warn on failure' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'custom test name' , async ( { page } ) = > {
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
// Timeout to make sure the expect actually gets processed
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 1 ) ;
2025-04-08 07:34:12 -07:00
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 39 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should not warn on success' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
2025-04-08 07:34:12 -07:00
test ( 'custom test name' , async ( { page } ) = > {
2025-03-31 07:16:00 -07:00
await page . setContent ( '<div>A</div>' ) ;
2025-04-08 07:34:12 -07:00
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 0 ) ;
2025-04-08 07:34:12 -07:00
expect ( results [ 0 ] . annotations ) . toEqual ( [ ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should warn about missing await on expects' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
2025-04-08 07:34:12 -07:00
test ( 'custom test name' , async ( { page } ) = > {
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 1 ) ;
2025-04-08 07:34:12 -07:00
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 39 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should not warn when not missing await on expects' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
2025-04-08 07:34:12 -07:00
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should not warn when using then() on expects' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' ) . then ( ( ) = > { } ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-20 10:46:49 -08:00
2025-04-08 07:34:12 -07:00
test ( 'should warn about missing await on resolve' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
2025-04-08 07:34:12 -07:00
expect ( Promise . reject ( new Error ( 'foo' ) ) ) . resolves . toBe ( 'foo' ) ;
2025-03-31 07:16:00 -07:00
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 61 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should warn about missing await on reject.not' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
expect ( Promise . reject ( new Error ( 'foo' ) ) ) . rejects . not . toThrow ( 'foo' ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 1 ) ;
2025-04-08 07:34:12 -07:00
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 64 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should warn about missing await on test.step' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
test . step ( 'step' , ( ) = > { } ) ;
2025-04-08 07:34:12 -07:00
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 16 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should not warn when not missing await on test.step' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
await test . step ( 'step' , ( ) = > { } ) ;
2025-04-08 07:34:12 -07:00
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should warn about missing await on test.step.skip' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
test . step . skip ( 'step' , ( ) = > { } ) ;
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' ) ;
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 21 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
2025-02-19 10:11:04 -08:00
2025-03-31 07:16:00 -07:00
test ( 'traced promise should be instanceof Promise' , async ( { runInlineTest } ) = > {
const { exitCode } = await runInlineTest ( {
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
await page . setContent ( '<div>A</div>' ) ;
const expectPromise = expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' ) ;
expect ( expectPromise instanceof Promise ) . toBeTruthy ( ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 0 ) ;
} ) ;
2025-02-20 10:46:49 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should warn about missing await in before hooks' , async ( { runInlineTest } ) = > {
const group = [ 'beforeAll' , 'beforeEach' ] ;
for ( const hook of group ) {
await test . step ( hook , async ( ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
let page ;
test . $ { hook } ( async ( { browser } ) = > {
page = await browser . newPage ( ) ;
await page . setContent ( '<div>A</div>' ) ;
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
test ( 'test ${hook}' , async ( ) = > {
2025-04-08 07:34:12 -07:00
await expect ( page . locator ( 'button' ) ) . toBeVisible ( { timeout : 100 } ) ;
2025-03-31 07:16:00 -07:00
} ) ;
`
} ) ;
2025-02-20 10:46:49 -08:00
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 7 , column : 43 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
}
} ) ;
2025-02-20 10:46:49 -08:00
2025-03-31 07:16:00 -07:00
test . describe ( 'should warn about missing await in after hooks' , ( ) = > {
const group = [ 'afterAll' , 'afterEach' ] ;
for ( const hook of group ) {
test ( hook , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
let page ;
test ( 'test ${hook}' , async ( { browser } ) = > {
2025-04-08 07:34:12 -07:00
await expect ( Promise . resolve ( ) ) . resolves . toBe ( 'A' ) ;
2025-03-31 07:16:00 -07:00
} ) ;
test . $ { hook } ( async ( ) = > {
expect ( Promise . resolve ( ) ) . resolves . toBe ( undefined ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
2025-02-20 10:46:49 -08:00
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [ { type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 8 , column : 50 } ) } ] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
}
} ) ;
2025-02-20 10:46:49 -08:00
2025-03-31 07:16:00 -07:00
test ( 'should warn about missing await across hooks and test' , async ( { runInlineTest } ) = > {
2025-04-08 07:34:12 -07:00
const { exitCode , results } = await runInlineTest ( {
2025-03-31 07:16:00 -07:00
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test . beforeAll ( async ( ) = > {
expect ( Promise . resolve ( ) ) . resolves . toBe ( undefined ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
test ( 'test' , async ( ) = > {
2025-04-08 07:34:12 -07:00
expect ( Promise . resolve ( ) ) . resolves . toBe ( 'A' ) ;
2025-03-31 07:16:00 -07:00
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
test . afterEach ( async ( ) = > {
expect ( Promise . resolve ( ) ) . resolves . toBe ( undefined ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
2025-04-08 07:34:12 -07:00
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [
{ type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 4 , column : 46 } ) } ,
{ type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 8 , column : 46 } ) } ,
{ type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 12 , column : 46 } ) } ,
] ) ;
} ) ;
test ( 'should dedupe warnings that occur at the same location' , async ( { runInlineTest } ) = > {
const { exitCode , results } = await runInlineTest ( {
'a.test.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'test' , async ( { page } ) = > {
for ( let i = 0 ; i < 3 ; i ++ ) {
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
}
expect ( page . locator ( 'div' ) ) . toHaveText ( 'A' , { timeout : 100 } ) ;
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
} ) ;
`
} ) ;
expect ( exitCode ) . toBe ( 1 ) ;
expect ( results [ 0 ] . annotations ) . toEqual ( [
{ type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 5 , column : 41 } ) } ,
{ type : 'warning' , description , location : expect.objectContaining ( { file : expect.stringMatching ( /a\.test\.ts$/ ) , line : 7 , column : 39 } ) } ,
] ) ;
2025-03-31 07:16:00 -07:00
} ) ;
} ) ;