2018-11-21 02:57:28 +00:00
/ * *
* Copyright 2018 Google Inc . All rights reserved .
*
* 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 .
* /
2020-07-13 09:22:26 +00:00
import utils from './utils.js' ;
2020-07-28 08:37:49 +00:00
import sinon from 'sinon' ;
2020-06-23 05:18:46 +00:00
import expect from 'expect' ;
import {
2020-05-07 10:54:55 +00:00
getTestState ,
setupTestBrowserHooks ,
setupTestPageAndContextHooks ,
2020-06-23 05:18:46 +00:00
itFailsFirefox ,
2020-07-13 09:22:26 +00:00
} from './mocha-utils' ; // eslint-disable-line import/extensions
2018-11-21 02:57:28 +00:00
2020-05-07 10:54:55 +00:00
describe ( 'waittask specs' , function ( ) {
2020-04-09 05:56:25 +00:00
setupTestBrowserHooks ( ) ;
setupTestPageAndContextHooks ( ) ;
2018-11-21 02:57:28 +00:00
2020-05-07 10:54:55 +00:00
describe ( 'Page.waitFor' , function ( ) {
2020-07-28 08:37:49 +00:00
/ * T h i s m e t h o d i s d e p r e c a t e d b u t w e d o n ' t w a n t t h e w a r n i n g s s h o w i n g u p i n
* tests . Until we remove this method we still want to ensure we don ' t break
* it .
* /
beforeEach ( ( ) = > sinon . stub ( console , 'warn' ) . callsFake ( ( ) = > { } ) ) ;
2020-06-12 13:55:51 +00:00
it ( 'should wait for selector' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let found = false ;
2020-05-07 10:54:55 +00:00
const waitFor = page . waitFor ( 'div' ) . then ( ( ) = > ( found = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
expect ( found ) . toBe ( false ) ;
await page . goto ( server . PREFIX + '/grid.html' ) ;
await waitFor ;
expect ( found ) . toBe ( true ) ;
} ) ;
2020-04-09 05:56:25 +00:00
2020-06-12 13:55:51 +00:00
it ( 'should wait for an xpath' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let found = false ;
2020-05-07 10:54:55 +00:00
const waitFor = page . waitFor ( '//div' ) . then ( ( ) = > ( found = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
expect ( found ) . toBe ( false ) ;
await page . goto ( server . PREFIX + '/grid.html' ) ;
await waitFor ;
expect ( found ) . toBe ( true ) ;
} ) ;
2020-06-12 13:55:51 +00:00
it ( 'should not allow you to select an element with single slash xpath' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-05-07 10:54:55 +00:00
2020-06-12 13:55:51 +00:00
await page . setContent ( ` <div>some text</div> ` ) ;
let error = null ;
await page . waitFor ( '/html/body/div' ) . catch ( ( error_ ) = > ( error = error_ ) ) ;
expect ( error ) . toBeTruthy ( ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should timeout' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
const startTime = Date . now ( ) ;
const timeout = 42 ;
await page . waitFor ( timeout ) ;
expect ( Date . now ( ) - startTime ) . not . toBeLessThan ( timeout / 2 ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should work with multiline body' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-01-11 05:41:09 +00:00
const result = await page . waitForFunction ( `
( ( ) = > true ) ( )
` );
expect ( await result . jsonValue ( ) ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should wait for predicate' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-02-20 06:36:02 +00:00
await Promise . all ( [
page . waitFor ( ( ) = > window . innerWidth < 100 ) ,
2020-05-07 10:54:55 +00:00
page . setViewport ( { width : 10 , height : 10 } ) ,
2019-02-20 06:36:02 +00:00
] ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should throw when unknown type' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
2020-06-23 05:18:46 +00:00
// @ts-expect-error
2020-05-07 10:54:55 +00:00
await page . waitFor ( { foo : 'bar' } ) . catch ( ( error_ ) = > ( error = error_ ) ) ;
2018-11-21 02:57:28 +00:00
expect ( error . message ) . toContain ( 'Unsupported target type' ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should wait for predicate with arguments' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . waitFor ( ( arg1 , arg2 ) = > arg1 !== arg2 , { } , 1 , 2 ) ;
} ) ;
2020-07-28 08:37:49 +00:00
it ( 'should log a deprecation warning' , async ( ) = > {
const { page } = getTestState ( ) ;
await page . waitFor ( ( ) = > true ) ;
const consoleWarnStub = console . warn as sinon . SinonSpy ;
expect ( consoleWarnStub . calledOnce ) . toBe ( true ) ;
expect (
consoleWarnStub . firstCall . calledWith (
'waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.'
)
) . toBe ( true ) ;
expect ( ( console . warn as sinon . SinonSpy ) . calledOnce ) . toBe ( true ) ;
} ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
describe ( 'Frame.waitForFunction' , function ( ) {
it ( 'should accept a string' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
const watchdog = page . waitForFunction ( 'window.__FOO === 1' ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __FOO = 1 ) ) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
} ) ;
2020-10-05 05:17:21 +00:00
it ( 'should work when resolved right before execution context disposal' , async ( ) = > {
const { page } = getTestState ( ) ;
await page . evaluateOnNewDocument ( ( ) = > ( globalThis . __RELOADED = true ) ) ;
await page . waitForFunction ( ( ) = > {
if ( ! globalThis . __RELOADED ) window . location . reload ( ) ;
return true ;
} ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should poll on interval' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let success = false ;
const startTime = Date . now ( ) ;
const polling = 100 ;
2020-05-07 10:54:55 +00:00
const watchdog = page
2020-06-23 05:18:46 +00:00
. waitForFunction ( ( ) = > globalThis . __FOO === 'hit' , { polling } )
2020-05-07 10:54:55 +00:00
. then ( ( ) = > ( success = true ) ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2018-11-21 02:57:28 +00:00
expect ( success ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . body . appendChild ( document . createElement ( 'div' ) )
) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
expect ( Date . now ( ) - startTime ) . not . toBeLessThan ( polling / 2 ) ;
} ) ;
2020-05-19 07:09:31 +00:00
it ( 'should poll on interval async' , async ( ) = > {
const { page } = getTestState ( ) ;
let success = false ;
const startTime = Date . now ( ) ;
const polling = 100 ;
const watchdog = page
2020-06-23 05:18:46 +00:00
. waitForFunction ( async ( ) = > globalThis . __FOO === 'hit' , { polling } )
2020-05-19 07:09:31 +00:00
. then ( ( ) = > ( success = true ) ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( async ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2020-05-19 07:09:31 +00:00
expect ( success ) . toBe ( false ) ;
await page . evaluate ( async ( ) = >
document . body . appendChild ( document . createElement ( 'div' ) )
) ;
await watchdog ;
expect ( Date . now ( ) - startTime ) . not . toBeLessThan ( polling / 2 ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should poll on mutation' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let success = false ;
2020-05-07 10:54:55 +00:00
const watchdog = page
2020-06-23 05:18:46 +00:00
. waitForFunction ( ( ) = > globalThis . __FOO === 'hit' , {
polling : 'mutation' ,
} )
2020-05-07 10:54:55 +00:00
. then ( ( ) = > ( success = true ) ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2018-11-21 02:57:28 +00:00
expect ( success ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . body . appendChild ( document . createElement ( 'div' ) )
) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
} ) ;
2020-05-19 07:09:31 +00:00
it ( 'should poll on mutation async' , async ( ) = > {
const { page } = getTestState ( ) ;
let success = false ;
const watchdog = page
2020-06-23 05:18:46 +00:00
. waitForFunction ( async ( ) = > globalThis . __FOO === 'hit' , {
2020-05-19 07:09:31 +00:00
polling : 'mutation' ,
} )
. then ( ( ) = > ( success = true ) ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( async ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2020-05-19 07:09:31 +00:00
expect ( success ) . toBe ( false ) ;
await page . evaluate ( async ( ) = >
document . body . appendChild ( document . createElement ( 'div' ) )
) ;
await watchdog ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should poll on raf' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2020-06-23 05:18:46 +00:00
const watchdog = page . waitForFunction ( ( ) = > globalThis . __FOO === 'hit' , {
2020-05-07 10:54:55 +00:00
polling : 'raf' ,
} ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
} ) ;
2020-05-19 07:09:31 +00:00
it ( 'should poll on raf async' , async ( ) = > {
const { page } = getTestState ( ) ;
const watchdog = page . waitForFunction (
2020-06-23 05:18:46 +00:00
async ( ) = > globalThis . __FOO === 'hit' ,
2020-05-19 07:09:31 +00:00
{
polling : 'raf' ,
}
) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( async ( ) = > ( globalThis . __FOO = 'hit' ) ) ;
2020-05-19 07:09:31 +00:00
await watchdog ;
} ) ;
2020-05-07 10:54:55 +00:00
itFailsFirefox ( 'should work with strict CSP policy' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
server . setCSP ( '/empty.html' , 'script-src ' + server . PREFIX ) ;
await page . goto ( server . EMPTY_PAGE ) ;
2019-02-06 03:49:47 +00:00
let error = null ;
await Promise . all ( [
2020-05-07 10:54:55 +00:00
page
2020-06-23 05:18:46 +00:00
. waitForFunction ( ( ) = > globalThis . __FOO === 'hit' , { polling : 'raf' } )
2020-05-07 10:54:55 +00:00
. catch ( ( error_ ) = > ( error = error_ ) ) ,
2020-06-23 05:18:46 +00:00
page . evaluate ( ( ) = > ( globalThis . __FOO = 'hit' ) ) ,
2019-02-06 03:49:47 +00:00
] ) ;
expect ( error ) . toBe ( null ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should throw on bad polling value' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
try {
2020-05-07 10:54:55 +00:00
await page . waitForFunction ( ( ) = > ! ! document . body , {
polling : 'unknown' ,
} ) ;
2020-04-28 13:16:28 +00:00
} catch ( error_ ) {
error = error_ ;
2018-11-21 02:57:28 +00:00
}
expect ( error ) . toBeTruthy ( ) ;
expect ( error . message ) . toContain ( 'polling' ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should throw negative polling interval' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
try {
2020-05-07 10:54:55 +00:00
await page . waitForFunction ( ( ) = > ! ! document . body , { polling : - 10 } ) ;
2020-04-28 13:16:28 +00:00
} catch ( error_ ) {
error = error_ ;
2018-11-21 02:57:28 +00:00
}
expect ( error ) . toBeTruthy ( ) ;
expect ( error . message ) . toContain ( 'Cannot poll with non-positive interval' ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should return the success value as a JSHandle' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
expect ( await ( await page . waitForFunction ( ( ) = > 5 ) ) . jsonValue ( ) ) . toBe ( 5 ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should return the window as a success value' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
expect ( await page . waitForFunction ( ( ) = > window ) ) . toBeTruthy ( ) ;
} ) ;
2020-06-12 13:55:51 +00:00
it ( 'should accept ElementHandle arguments' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( '<div></div>' ) ;
const div = await page . $ ( 'div' ) ;
let resolved = false ;
2020-05-07 10:54:55 +00:00
const waitForFunction = page
. waitForFunction ( ( element ) = > ! element . parentElement , { } , div )
. then ( ( ) = > ( resolved = true ) ) ;
2018-11-21 02:57:28 +00:00
expect ( resolved ) . toBe ( false ) ;
2020-07-10 10:52:13 +00:00
await page . evaluate ( ( element : HTMLElement ) = > element . remove ( ) , div ) ;
2018-11-21 02:57:28 +00:00
await waitForFunction ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should respect timeout' , async ( ) = > {
const { page , puppeteer } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
2020-05-07 10:54:55 +00:00
await page
. waitForFunction ( 'false' , { timeout : 10 } )
. catch ( ( error_ ) = > ( error = error_ ) ) ;
2018-11-21 02:57:28 +00:00
expect ( error ) . toBeTruthy ( ) ;
expect ( error . message ) . toContain ( 'waiting for function failed: timeout' ) ;
2019-04-19 22:33:06 +00:00
expect ( error ) . toBeInstanceOf ( puppeteer . errors . TimeoutError ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should respect default timeout' , async ( ) = > {
const { page , puppeteer } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-01-29 01:16:12 +00:00
page . setDefaultTimeout ( 1 ) ;
let error = null ;
2020-05-07 10:54:55 +00:00
await page . waitForFunction ( 'false' ) . catch ( ( error_ ) = > ( error = error_ ) ) ;
2019-04-19 22:33:06 +00:00
expect ( error ) . toBeInstanceOf ( puppeteer . errors . TimeoutError ) ;
2019-02-09 04:37:14 +00:00
expect ( error . message ) . toContain ( 'waiting for function failed: timeout' ) ;
2019-01-29 01:16:12 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should disable timeout when its set to 0' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2020-05-07 10:54:55 +00:00
const watchdog = page . waitForFunction (
( ) = > {
2020-06-23 05:18:46 +00:00
globalThis . __counter = ( globalThis . __counter || 0 ) + 1 ;
return globalThis . __injected ;
2020-05-07 10:54:55 +00:00
} ,
{ timeout : 0 , polling : 10 }
) ;
2020-06-23 05:18:46 +00:00
await page . waitForFunction ( ( ) = > globalThis . __counter > 10 ) ;
await page . evaluate ( ( ) = > ( globalThis . __injected = true ) ) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should survive cross-process navigation' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-01-22 18:21:18 +00:00
let fooFound = false ;
2020-05-07 10:54:55 +00:00
const waitForFunction = page
2020-06-23 05:18:46 +00:00
. waitForFunction ( 'globalThis.__FOO === 1' )
2020-05-07 10:54:55 +00:00
. then ( ( ) = > ( fooFound = true ) ) ;
2019-01-22 18:21:18 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
expect ( fooFound ) . toBe ( false ) ;
await page . reload ( ) ;
expect ( fooFound ) . toBe ( false ) ;
await page . goto ( server . CROSS_PROCESS_PREFIX + '/grid.html' ) ;
expect ( fooFound ) . toBe ( false ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __FOO = 1 ) ) ;
2019-01-22 18:21:18 +00:00
await waitForFunction ;
expect ( fooFound ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should survive navigations' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2020-06-23 05:18:46 +00:00
const watchdog = page . waitForFunction ( ( ) = > globalThis . __done ) ;
2019-02-03 01:49:12 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
await page . goto ( server . PREFIX + '/consolelog.html' ) ;
2020-06-23 05:18:46 +00:00
await page . evaluate ( ( ) = > ( globalThis . __done = true ) ) ;
2019-02-03 01:49:12 +00:00
await watchdog ;
} ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-07-28 08:37:49 +00:00
describe ( 'Page.waitForTimeout' , ( ) = > {
it ( 'waits for the given timeout before resolving' , async ( ) = > {
const { page , server } = getTestState ( ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const startTime = Date . now ( ) ;
await page . waitForTimeout ( 1000 ) ;
const endTime = Date . now ( ) ;
/ * I n a p e r f e c t w o r l d e n d T i m e - s t a r t T i m e w o u l d b e e x a c t l y 1 0 0 0 b u t w e
* expect some fluctuations and for it to be off by a little bit . So to
* avoid a flaky test we ' ll make sure it waited for roughly 1 second by
* ensuring 900 < endTime - startTime < 1100
* /
expect ( endTime - startTime ) . toBeGreaterThan ( 900 ) ;
expect ( endTime - startTime ) . toBeLessThan ( 1100 ) ;
} ) ;
} ) ;
describe ( 'Frame.waitForTimeout' , ( ) = > {
it ( 'waits for the given timeout before resolving' , async ( ) = > {
const { page , server } = getTestState ( ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const frame = page . mainFrame ( ) ;
const startTime = Date . now ( ) ;
await frame . waitForTimeout ( 1000 ) ;
const endTime = Date . now ( ) ;
/ * I n a p e r f e c t w o r l d e n d T i m e - s t a r t T i m e w o u l d b e e x a c t l y 1 0 0 0 b u t w e
* expect some fluctuations and for it to be off by a little bit . So to
* avoid a flaky test we ' ll make sure it waited for roughly 1 second by
* ensuring 900 < endTime - startTime < 1100
* /
expect ( endTime - startTime ) . toBeGreaterThan ( 900 ) ;
expect ( endTime - startTime ) . toBeLessThan ( 1100 ) ;
} ) ;
} ) ;
2020-06-12 13:55:51 +00:00
describe ( 'Frame.waitForSelector' , function ( ) {
2020-05-07 10:54:55 +00:00
const addElement = ( tag ) = >
document . body . appendChild ( document . createElement ( tag ) ) ;
2018-11-21 02:57:28 +00:00
2020-05-07 10:54:55 +00:00
it ( 'should immediately resolve promise if node exists' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
const frame = page . mainFrame ( ) ;
await frame . waitForSelector ( '*' ) ;
await frame . evaluate ( addElement , 'div' ) ;
await frame . waitForSelector ( 'div' ) ;
} ) ;
2020-06-12 13:55:51 +00:00
itFailsFirefox ( 'should work with removed MutationObserver' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-01-28 20:24:27 +00:00
await page . evaluate ( ( ) = > delete window . MutationObserver ) ;
2019-02-06 03:49:47 +00:00
const [ handle ] = await Promise . all ( [
page . waitForSelector ( '.zombo' ) ,
page . setContent ( ` <div class='zombo'>anything</div> ` ) ,
] ) ;
2020-07-10 10:52:13 +00:00
expect (
await page . evaluate ( ( x : HTMLElement ) = > x . textContent , handle )
) . toBe ( 'anything' ) ;
2019-01-28 20:24:27 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should resolve promise when node is added' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
const frame = page . mainFrame ( ) ;
const watchdog = frame . waitForSelector ( 'div' ) ;
await frame . evaluate ( addElement , 'br' ) ;
await frame . evaluate ( addElement , 'div' ) ;
const eHandle = await watchdog ;
2020-05-07 10:54:55 +00:00
const tagName = await eHandle
. getProperty ( 'tagName' )
. then ( ( e ) = > e . jsonValue ( ) ) ;
2018-11-21 02:57:28 +00:00
expect ( tagName ) . toBe ( 'DIV' ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should work when node is added through innerHTML' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
const watchdog = page . waitForSelector ( 'h3 div' ) ;
await page . evaluate ( addElement , 'span' ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate (
( ) = >
( document . querySelector ( 'span' ) . innerHTML = '<h3><div></div></h3>' )
) ;
2018-11-21 02:57:28 +00:00
await watchdog ;
} ) ;
2020-06-12 13:55:51 +00:00
itFailsFirefox (
'Page.waitForSelector is shortcut for main frame' ,
async ( ) = > {
const { page , server } = getTestState ( ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await utils . attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
const otherFrame = page . frames ( ) [ 1 ] ;
const watchdog = page . waitForSelector ( 'div' ) ;
await otherFrame . evaluate ( addElement , 'div' ) ;
await page . evaluate ( addElement , 'div' ) ;
const eHandle = await watchdog ;
expect ( eHandle . executionContext ( ) . frame ( ) ) . toBe ( page . mainFrame ( ) ) ;
}
) ;
2018-11-21 02:57:28 +00:00
2020-06-12 13:55:51 +00:00
itFailsFirefox ( 'should run in specified frame' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2019-02-09 03:24:40 +00:00
await utils . attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
await utils . attachFrame ( page , 'frame2' , server . EMPTY_PAGE ) ;
2018-11-21 02:57:28 +00:00
const frame1 = page . frames ( ) [ 1 ] ;
const frame2 = page . frames ( ) [ 2 ] ;
const waitForSelectorPromise = frame2 . waitForSelector ( 'div' ) ;
await frame1 . evaluate ( addElement , 'div' ) ;
await frame2 . evaluate ( addElement , 'div' ) ;
const eHandle = await waitForSelectorPromise ;
expect ( eHandle . executionContext ( ) . frame ( ) ) . toBe ( frame2 ) ;
} ) ;
2020-06-12 13:55:51 +00:00
itFailsFirefox ( 'should throw when frame is detached' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await utils . attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
const frame = page . frames ( ) [ 1 ] ;
let waitError = null ;
2020-05-07 10:54:55 +00:00
const waitPromise = frame
. waitForSelector ( '.box' )
. catch ( ( error ) = > ( waitError = error ) ) ;
2018-11-21 02:57:28 +00:00
await utils . detachFrame ( page , 'frame1' ) ;
await waitPromise ;
expect ( waitError ) . toBeTruthy ( ) ;
2020-05-07 10:54:55 +00:00
expect ( waitError . message ) . toContain (
'waitForFunction failed: frame got detached.'
) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should survive cross-process navigation' , async ( ) = > {
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let boxFound = false ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( '.box' )
. then ( ( ) = > ( boxFound = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . goto ( server . EMPTY_PAGE ) ;
expect ( boxFound ) . toBe ( false ) ;
await page . reload ( ) ;
expect ( boxFound ) . toBe ( false ) ;
await page . goto ( server . CROSS_PROCESS_PREFIX + '/grid.html' ) ;
await waitForSelector ;
expect ( boxFound ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should wait for visible' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divFound = false ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( 'div' , { visible : true } )
. then ( ( ) = > ( divFound = true ) ) ;
await page . setContent (
` <div style='display: none; visibility: hidden;'>1</div> `
) ;
2018-11-21 02:57:28 +00:00
expect ( divFound ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . removeProperty ( 'display' )
) ;
2018-11-21 02:57:28 +00:00
expect ( divFound ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . removeProperty ( 'visibility' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForSelector ) . toBe ( true ) ;
expect ( divFound ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should wait for visible recursively' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divVisible = false ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( 'div#inner' , { visible : true } )
. then ( ( ) = > ( divVisible = true ) ) ;
await page . setContent (
` <div style='display: none; visibility: hidden;'><div id="inner">hi</div></div> `
) ;
2018-11-21 02:57:28 +00:00
expect ( divVisible ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . removeProperty ( 'display' )
) ;
2018-11-21 02:57:28 +00:00
expect ( divVisible ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . removeProperty ( 'visibility' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForSelector ) . toBe ( true ) ;
expect ( divVisible ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'hidden should wait for visibility: hidden' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divHidden = false ;
await page . setContent ( ` <div style='display: block;'></div> ` ) ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( 'div' , { hidden : true } )
. then ( ( ) = > ( divHidden = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . waitForSelector ( 'div' ) ; // do a round trip
expect ( divHidden ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . setProperty ( 'visibility' , 'hidden' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForSelector ) . toBe ( true ) ;
expect ( divHidden ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'hidden should wait for display: none' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divHidden = false ;
await page . setContent ( ` <div style='display: block;'></div> ` ) ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( 'div' , { hidden : true } )
. then ( ( ) = > ( divHidden = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . waitForSelector ( 'div' ) ; // do a round trip
expect ( divHidden ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . setProperty ( 'display' , 'none' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForSelector ) . toBe ( true ) ;
expect ( divHidden ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'hidden should wait for removal' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <div></div> ` ) ;
let divRemoved = false ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( 'div' , { hidden : true } )
. then ( ( ) = > ( divRemoved = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . waitForSelector ( 'div' ) ; // do a round trip
expect ( divRemoved ) . toBe ( false ) ;
await page . evaluate ( ( ) = > document . querySelector ( 'div' ) . remove ( ) ) ;
expect ( await waitForSelector ) . toBe ( true ) ;
expect ( divRemoved ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should return null if waiting to hide non-existing element' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2020-05-07 10:54:55 +00:00
const handle = await page . waitForSelector ( 'non-existing' , {
hidden : true ,
} ) ;
2019-01-28 19:24:53 +00:00
expect ( handle ) . toBe ( null ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should respect timeout' , async ( ) = > {
const { page , puppeteer } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
2020-05-07 10:54:55 +00:00
await page
. waitForSelector ( 'div' , { timeout : 10 } )
. catch ( ( error_ ) = > ( error = error_ ) ) ;
2018-11-21 02:57:28 +00:00
expect ( error ) . toBeTruthy ( ) ;
2020-05-07 10:54:55 +00:00
expect ( error . message ) . toContain (
2020-09-21 13:47:33 +00:00
'waiting for selector `div` failed: timeout'
2020-05-07 10:54:55 +00:00
) ;
2019-04-19 22:33:06 +00:00
expect ( error ) . toBeInstanceOf ( puppeteer . errors . TimeoutError ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should have an error message specifically for awaiting an element to be hidden' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <div></div> ` ) ;
let error = null ;
2020-05-07 10:54:55 +00:00
await page
. waitForSelector ( 'div' , { hidden : true , timeout : 10 } )
. catch ( ( error_ ) = > ( error = error_ ) ) ;
2018-11-21 02:57:28 +00:00
expect ( error ) . toBeTruthy ( ) ;
2020-05-07 10:54:55 +00:00
expect ( error . message ) . toContain (
2020-09-21 13:47:33 +00:00
'waiting for selector `div` to be hidden failed: timeout'
2020-05-07 10:54:55 +00:00
) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should respond to node attribute mutation' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divFound = false ;
2020-05-07 10:54:55 +00:00
const waitForSelector = page
. waitForSelector ( '.zombo' )
. then ( ( ) = > ( divFound = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <div class='notZombo'></div> ` ) ;
expect ( divFound ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate (
( ) = > ( document . querySelector ( 'div' ) . className = 'zombo' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForSelector ) . toBe ( true ) ;
} ) ;
2020-06-12 13:55:51 +00:00
it ( 'should return the element handle' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
const waitForSelector = page . waitForSelector ( '.zombo' ) ;
await page . setContent ( ` <div class='zombo'>anything</div> ` ) ;
2020-05-07 10:54:55 +00:00
expect (
2020-07-10 10:52:13 +00:00
await page . evaluate (
( x : HTMLElement ) = > x . textContent ,
await waitForSelector
)
2020-05-07 10:54:55 +00:00
) . toBe ( 'anything' ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should have correct stack trace for timeout' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error ;
2020-05-07 10:54:55 +00:00
await page
. waitForSelector ( '.zombo' , { timeout : 10 } )
. catch ( ( error_ ) = > ( error = error_ ) ) ;
2020-09-21 13:47:33 +00:00
expect ( error . stack ) . toContain ( 'waiting for selector `.zombo` failed' ) ;
2020-06-23 05:18:46 +00:00
// The extension is ts here as Mocha maps back via sourcemaps.
expect ( error . stack ) . toContain ( 'waittask.spec.ts' ) ;
2018-11-21 02:57:28 +00:00
} ) ;
} ) ;
2020-06-12 13:55:51 +00:00
describe ( 'Frame.waitForXPath' , function ( ) {
2020-05-07 10:54:55 +00:00
const addElement = ( tag ) = >
document . body . appendChild ( document . createElement ( tag ) ) ;
2018-11-21 02:57:28 +00:00
2020-05-07 10:54:55 +00:00
it ( 'should support some fancy xpath' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <p>red herring</p><p>hello world </p> ` ) ;
2020-05-07 10:54:55 +00:00
const waitForXPath = page . waitForXPath (
'//p[normalize-space(.)="hello world"]'
) ;
expect (
2020-07-10 10:52:13 +00:00
await page . evaluate (
( x : HTMLElement ) = > x . textContent ,
await waitForXPath
)
2020-05-07 10:54:55 +00:00
) . toBe ( 'hello world ' ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should respect timeout' , async ( ) = > {
const { page , puppeteer } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let error = null ;
2020-05-07 10:54:55 +00:00
await page
. waitForXPath ( '//div' , { timeout : 10 } )
. catch ( ( error_ ) = > ( error = error_ ) ) ;
2018-11-21 02:57:28 +00:00
expect ( error ) . toBeTruthy ( ) ;
2020-05-07 10:54:55 +00:00
expect ( error . message ) . toContain (
2020-09-21 13:47:33 +00:00
'waiting for XPath `//div` failed: timeout'
2020-05-07 10:54:55 +00:00
) ;
2019-04-19 22:33:06 +00:00
expect ( error ) . toBeInstanceOf ( puppeteer . errors . TimeoutError ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-06-12 13:55:51 +00:00
itFailsFirefox ( 'should run in specified frame' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await utils . attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
await utils . attachFrame ( page , 'frame2' , server . EMPTY_PAGE ) ;
const frame1 = page . frames ( ) [ 1 ] ;
const frame2 = page . frames ( ) [ 2 ] ;
const waitForXPathPromise = frame2 . waitForXPath ( '//div' ) ;
await frame1 . evaluate ( addElement , 'div' ) ;
await frame2 . evaluate ( addElement , 'div' ) ;
const eHandle = await waitForXPathPromise ;
expect ( eHandle . executionContext ( ) . frame ( ) ) . toBe ( frame2 ) ;
} ) ;
2020-06-12 13:55:51 +00:00
itFailsFirefox ( 'should throw when frame is detached' , async ( ) = > {
2020-05-07 10:54:55 +00:00
const { page , server } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await utils . attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
const frame = page . frames ( ) [ 1 ] ;
let waitError = null ;
2020-05-07 10:54:55 +00:00
const waitPromise = frame
. waitForXPath ( '//*[@class="box"]' )
. catch ( ( error ) = > ( waitError = error ) ) ;
2018-11-21 02:57:28 +00:00
await utils . detachFrame ( page , 'frame1' ) ;
await waitPromise ;
expect ( waitError ) . toBeTruthy ( ) ;
2020-05-07 10:54:55 +00:00
expect ( waitError . message ) . toContain (
'waitForFunction failed: frame got detached.'
) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'hidden should wait for display: none' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
let divHidden = false ;
await page . setContent ( ` <div style='display: block;'></div> ` ) ;
2020-05-07 10:54:55 +00:00
const waitForXPath = page
. waitForXPath ( '//div' , { hidden : true } )
. then ( ( ) = > ( divHidden = true ) ) ;
2018-11-21 02:57:28 +00:00
await page . waitForXPath ( '//div' ) ; // do a round trip
expect ( divHidden ) . toBe ( false ) ;
2020-05-07 10:54:55 +00:00
await page . evaluate ( ( ) = >
document . querySelector ( 'div' ) . style . setProperty ( 'display' , 'none' )
) ;
2018-11-21 02:57:28 +00:00
expect ( await waitForXPath ) . toBe ( true ) ;
expect ( divHidden ) . toBe ( true ) ;
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should return the element handle' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
const waitForXPath = page . waitForXPath ( '//*[@class="zombo"]' ) ;
await page . setContent ( ` <div class='zombo'>anything</div> ` ) ;
2020-05-07 10:54:55 +00:00
expect (
2020-07-10 10:52:13 +00:00
await page . evaluate (
( x : HTMLElement ) = > x . textContent ,
await waitForXPath
)
2020-05-07 10:54:55 +00:00
) . toBe ( 'anything' ) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should allow you to select a text node' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <div>some text</div> ` ) ;
const text = await page . waitForXPath ( '//div/text()' ) ;
2020-05-07 10:54:55 +00:00
expect ( await ( await text . getProperty ( 'nodeType' ) ) . jsonValue ( ) ) . toBe (
3 /* Node.TEXT_NODE */
) ;
2018-11-21 02:57:28 +00:00
} ) ;
2020-05-07 10:54:55 +00:00
it ( 'should allow you to select an element with single slash' , async ( ) = > {
const { page } = getTestState ( ) ;
2020-04-09 05:56:25 +00:00
2018-11-21 02:57:28 +00:00
await page . setContent ( ` <div>some text</div> ` ) ;
const waitForXPath = page . waitForXPath ( '/html/body/div' ) ;
2020-05-07 10:54:55 +00:00
expect (
2020-07-10 10:52:13 +00:00
await page . evaluate (
( x : HTMLElement ) = > x . textContent ,
await waitForXPath
)
2020-05-07 10:54:55 +00:00
) . toBe ( 'some text' ) ;
2018-11-21 02:57:28 +00:00
} ) ;
} ) ;
2020-04-09 05:56:25 +00:00
} ) ;