| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Copyright 2018 Google Inc. All rights reserved. | 
					
						
							|  |  |  |  * Modifications 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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-08-19 21:32:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | import { it, expect } from './fixtures'; | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should timeout', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const startTime = Date.now(); | 
					
						
							|  |  |  |   const timeout = 42; | 
					
						
							|  |  |  |   await page.waitForTimeout(timeout); | 
					
						
							|  |  |  |   expect(Date.now() - startTime).not.toBeLessThan(timeout / 2); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should accept a string', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const watchdog = page.waitForFunction('window.__FOO === 1'); | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   await page.evaluate(() => window['__FOO'] = 1); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await watchdog; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should work when resolved right before execution context disposal', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   await page.addInitScript(() => window['__RELOADED'] = true); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await page.waitForFunction(() => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     if (!window['__RELOADED']) | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |       window.location.reload(); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should poll on interval', async ({page, server}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const polling = 100; | 
					
						
							|  |  |  |   const timeDelta = await page.waitForFunction(() => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     if (!window['__startTime']) { | 
					
						
							|  |  |  |       window['__startTime'] = Date.now(); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     return Date.now() - window['__startTime']; | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   }, {}, {polling}); | 
					
						
							|  |  |  |   expect(await timeDelta.jsonValue()).not.toBeLessThan(polling); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should avoid side effects after timeout', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   let counter = 0; | 
					
						
							|  |  |  |   page.on('console', () => ++counter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const error = await page.waitForFunction(() => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     window['counter'] = (window['counter'] || 0) + 1; | 
					
						
							|  |  |  |     console.log(window['counter']); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   }, {}, { polling: 1, timeout: 1000 }).catch(e => e); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const savedCounter = counter; | 
					
						
							|  |  |  |   await page.waitForTimeout(2000); // Give it some time to produce more logs.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(error.message).toContain('page.waitForFunction: Timeout 1000ms exceeded'); | 
					
						
							|  |  |  |   expect(counter).toBe(savedCounter); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should throw on polling:mutation', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-09-09 03:06:52 -07:00
										 |  |  |   // @ts-expect-error mutation is not a valid polling strategy
 | 
					
						
							|  |  |  |   const error = await page.waitForFunction(() => true, {}, {polling: 'mutation'}).catch(e => e); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   expect(error.message).toContain('Unknown polling option: mutation'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should poll on raf', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   const watchdog = page.waitForFunction(() => window['__FOO'] === 'hit', {}, {polling: 'raf'}); | 
					
						
							|  |  |  |   await page.evaluate(() => window['__FOO'] = 'hit'); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await watchdog; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should fail with predicate throwing on first call', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const error = await page.waitForFunction(() => { throw new Error('oh my'); }).catch(e => e); | 
					
						
							|  |  |  |   expect(error.message).toContain('oh my'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should fail with predicate throwing sometimes', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const error = await page.waitForFunction(() => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     window['counter'] = (window['counter'] || 0) + 1; | 
					
						
							|  |  |  |     if (window['counter'] === 3) | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |       throw new Error('Bad counter!'); | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     return window['counter'] === 5 ? 'result' : false; | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   }).catch(e => e); | 
					
						
							|  |  |  |   expect(error.message).toContain('Bad counter!'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should fail with ReferenceError on wrong page', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   // @ts-ignore
 | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const error = await page.waitForFunction(() => globalVar === 123).catch(e => e); | 
					
						
							|  |  |  |   expect(error.message).toContain('globalVar'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should work with strict CSP policy', async ({page, server}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   server.setCSP('/empty.html', 'script-src ' + server.PREFIX); | 
					
						
							|  |  |  |   await page.goto(server.EMPTY_PAGE); | 
					
						
							|  |  |  |   let error = null; | 
					
						
							|  |  |  |   await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     page.waitForFunction(() => window['__FOO'] === 'hit', {}, {polling: 'raf'}).catch(e => error = e), | 
					
						
							|  |  |  |     page.evaluate(() => window['__FOO'] = 'hit') | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   ]); | 
					
						
							|  |  |  |   expect(error).toBe(null); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should throw on bad polling value', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   let error = null; | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-09-09 03:06:52 -07:00
										 |  |  |     // @ts-expect-error 'unknown' is not a valid polling strategy
 | 
					
						
							|  |  |  |     await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'}); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   } catch (e) { | 
					
						
							|  |  |  |     error = e; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   expect(error).toBeTruthy(); | 
					
						
							|  |  |  |   expect(error.message).toContain('polling'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should throw negative polling interval', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   let error = null; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await page.waitForFunction(() => !!document.body, {}, {polling: -10}); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     error = e; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   expect(error).toBeTruthy(); | 
					
						
							|  |  |  |   expect(error.message).toContain('Cannot poll with non-positive interval'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should return the success value as a JSHandle', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should return the window as a success value', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   expect(await page.waitForFunction(() => window)).toBeTruthy(); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should accept ElementHandle arguments', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await page.setContent('<div></div>'); | 
					
						
							|  |  |  |   const div = await page.$('div'); | 
					
						
							|  |  |  |   let resolved = false; | 
					
						
							|  |  |  |   const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true); | 
					
						
							|  |  |  |   expect(resolved).toBe(false); | 
					
						
							|  |  |  |   await page.evaluate(element => element.remove(), div); | 
					
						
							|  |  |  |   await waitForFunction; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should respect timeout', async ({page, playwright}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   let error = null; | 
					
						
							|  |  |  |   await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e); | 
					
						
							|  |  |  |   expect(error).toBeTruthy(); | 
					
						
							|  |  |  |   expect(error.message).toContain('page.waitForFunction: Timeout 10ms exceeded'); | 
					
						
							|  |  |  |   expect(error).toBeInstanceOf(playwright.errors.TimeoutError); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should respect default timeout', async ({page, playwright}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   page.setDefaultTimeout(1); | 
					
						
							|  |  |  |   let error = null; | 
					
						
							|  |  |  |   await page.waitForFunction('false').catch(e => error = e); | 
					
						
							|  |  |  |   expect(error).toBeInstanceOf(playwright.errors.TimeoutError); | 
					
						
							|  |  |  |   expect(error.message).toContain('page.waitForFunction: Timeout 1ms exceeded'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should disable timeout when its set to 0', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const watchdog = page.waitForFunction(() => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |     window['__counter'] = (window['__counter'] || 0) + 1; | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  |     return window['__injected']; | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   }, {}, {timeout: 0, polling: 10}); | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   await page.waitForFunction(() => window['__counter'] > 10); | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  |   await page.evaluate(() => window['__injected'] = true); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await watchdog; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should survive cross-process navigation', async ({page, server}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   let fooFound = false; | 
					
						
							|  |  |  |   const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true); | 
					
						
							|  |  |  |   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-08-11 15:50:53 -07:00
										 |  |  |   await page.evaluate(() => window['__FOO'] = 1); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await waitForFunction; | 
					
						
							|  |  |  |   expect(fooFound).toBe(true); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should survive navigations', async ({page, server}) => { | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   const watchdog = page.waitForFunction(() => window['__done']); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await page.goto(server.EMPTY_PAGE); | 
					
						
							|  |  |  |   await page.goto(server.PREFIX + '/consolelog.html'); | 
					
						
							| 
									
										
										
										
											2020-08-11 15:50:53 -07:00
										 |  |  |   await page.evaluate(() => window['__done'] = true); | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await watchdog; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should work with multiline body', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   const result = await page.waitForFunction(`
 | 
					
						
							|  |  |  |     (() => true)() | 
					
						
							|  |  |  |   `);
 | 
					
						
							|  |  |  |   expect(await result.jsonValue()).toBe(true); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 04:20:29 -07:00
										 |  |  | it('should wait for predicate with arguments', async ({page}) => { | 
					
						
							| 
									
										
										
										
											2020-08-03 16:30:37 -07:00
										 |  |  |   await page.waitForFunction(({arg1, arg2}) => arg1 + arg2 === 3, { arg1: 1, arg2: 2}); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2020-09-14 14:55:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | it('should not be called after finishing successfully', async ({page, server}) => { | 
					
						
							|  |  |  |   await page.goto(server.EMPTY_PAGE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const messages = []; | 
					
						
							|  |  |  |   page.on('console', msg => { | 
					
						
							|  |  |  |     if (msg.text().startsWith('waitForFunction')) | 
					
						
							|  |  |  |       messages.push(msg.text()); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction1'); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   await page.reload(); | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction2'); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   await page.reload(); | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction3'); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(messages.join('|')).toBe('waitForFunction1|waitForFunction2|waitForFunction3'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | it('should not be called after finishing unsuccessfully', async ({page, server}) => { | 
					
						
							|  |  |  |   await page.goto(server.EMPTY_PAGE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const messages = []; | 
					
						
							|  |  |  |   page.on('console', msg => { | 
					
						
							|  |  |  |     if (msg.text().startsWith('waitForFunction')) | 
					
						
							|  |  |  |       messages.push(msg.text()); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction1'); | 
					
						
							|  |  |  |     throw new Error('waitForFunction1'); | 
					
						
							|  |  |  |   }).catch(e => null); | 
					
						
							|  |  |  |   await page.reload(); | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction2'); | 
					
						
							|  |  |  |     throw new Error('waitForFunction2'); | 
					
						
							|  |  |  |   }).catch(e => null); | 
					
						
							|  |  |  |   await page.reload(); | 
					
						
							|  |  |  |   await page.waitForFunction(() => { | 
					
						
							|  |  |  |     console.log('waitForFunction3'); | 
					
						
							|  |  |  |     throw new Error('waitForFunction3'); | 
					
						
							|  |  |  |   }).catch(e => null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(messages.join('|')).toBe('waitForFunction1|waitForFunction2|waitForFunction3'); | 
					
						
							|  |  |  | }); |