diff --git a/docs/api.md b/docs/api.md index 463c26dd11f25..d3f7e1cf2f964 100644 --- a/docs/api.md +++ b/docs/api.md @@ -710,7 +710,6 @@ page.removeListener('request', logRequest); - [page.unroute(url[, handler])](#pageunrouteurl-handler) - [page.url()](#pageurl) - [page.viewportSize()](#pageviewportsize) -- [page.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#pagewaitforselectororfunctionortimeout-options-arg) - [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate) - [page.waitForFunction(pageFunction[, arg, options])](#pagewaitforfunctionpagefunction-arg-options) - [page.waitForLoadState([state[, options]])](#pagewaitforloadstatestate-options) @@ -718,6 +717,7 @@ page.removeListener('request', logRequest); - [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options) - [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options) - [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options) +- [page.waitForTimeout(timeout)](#pagewaitfortimeouttimeout) - [page.workers()](#pageworkers) @@ -1688,41 +1688,6 @@ This is a shortcut for [page.mainFrame().url()](#frameurl) - `width` <[number]> page width in pixels. - `height` <[number]> page height in pixels. -#### page.waitFor(selectorOrFunctionOrTimeout[, options[, arg]]) -- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for -- `options` <[Object]> Optional waiting parameters - - `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`. - - `polling` <[number]|"raf"|"mutation"> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values: - - `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes. - - `'mutation'` - to execute `pageFunction` on every DOM mutation. - - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods. -- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` -- returns: <[Promise]> Promise which resolves to a JSHandle of the success value - -This method behaves differently with respect to the type of the first parameter: -- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] and the method is a shortcut for [page.waitForSelector](#pagewaitforselectorselector-options) -- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-arg-options). -- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout -- otherwise, an exception is thrown - -```js -// wait for selector -await page.waitFor('.foo'); -// wait for 1 second -await page.waitFor(1000); -// wait for predicate -await page.waitFor(() => !!document.querySelector('.foo')); -``` - -To pass an argument from node.js to the predicate of `page.waitFor` function: - -```js -const selector = '.foo'; -await page.waitFor(selector => !!document.querySelector(selector), {}, selector); -``` - -Shortcut for [page.mainFrame().waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-arg). - #### page.waitForEvent(event[, optionsOrPredicate]) - `event` <[string]> Event name, same one would pass into `page.on(event)`. - `optionsOrPredicate` <[Function]|[Object]> Either a predicate that receives an event or an options object. @@ -1871,7 +1836,22 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. await browser.close(); })(); ``` -Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectororfunctionortimeout-options-arg). +Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options). + +#### page.waitForTimeout(timeout) +- `timeout` <[number]> A timeout to wait for +- returns: <[Promise]> + +Returns a promise that resolves after the timeout. + +Note that `page.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going to be flaky. Use signals such as network events, selectors becoming visible and others instead. + +```js +// wait for 1 second +await page.waitForTimeout(1000); +``` + +Shortcut for [page.mainFrame().waitForTimeout(timeout)](#pagewaitfortimeouttimeout). #### page.workers() - returns: <[Array]<[Worker]>> @@ -1948,11 +1928,11 @@ An example of getting text from an iframe element: - [frame.type(selector, text[, options])](#frametypeselector-text-options) - [frame.uncheck(selector, [options])](#frameuncheckselector-options) - [frame.url()](#frameurl) -- [frame.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#framewaitforselectororfunctionortimeout-options-arg) - [frame.waitForFunction(pageFunction[, arg, options])](#framewaitforfunctionpagefunction-arg-options) - [frame.waitForLoadState([state[, options]])](#framewaitforloadstatestate-options) - [frame.waitForNavigation([options])](#framewaitfornavigationoptions) - [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options) +- [frame.waitForTimeout(timeout)](#framewaitfortimeouttimeout) #### frame.$(selector) @@ -2385,39 +2365,6 @@ If there's no element matching `selector`, the method throws an error. Returns frame's url. -#### frame.waitFor(selectorOrFunctionOrTimeout[, options[, arg]]) -- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for -- `options` <[Object]> Optional waiting parameters - - `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`. - - `polling` <[number]|"raf"|"mutation"> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values: - - `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes. - - `'mutation'` - to execute `pageFunction` on every DOM mutation. - - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods. -- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` -- returns: <[Promise]> Promise which resolves to a JSHandle of the success value - -This method behaves differently with respect to the type of the first parameter: -- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] and the method is a shortcut for [frame.waitForSelector](#framewaitforselectororfunctionortimeout-options-arg) -- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-arg-options). -- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout -- otherwise, an exception is thrown - -```js -// wait for selector -await frame.waitFor('.foo'); -// wait for 1 second -await frame.waitFor(1000); -// wait for predicate -await frame.waitFor(() => !!document.querySelector('.foo')); -``` - -To pass an argument from node.js to the predicate of `frame.waitFor` function: - -```js -const selector = '.foo'; -await frame.waitFor(selector => !!document.querySelector(selector), {}, selector); -``` - #### frame.waitForFunction(pageFunction[, arg, options]) - `pageFunction` <[function]|[string]> Function to be evaluated in browser context - `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` @@ -2514,6 +2461,14 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'. })(); ``` +#### frame.waitForTimeout(timeout) +- `timeout` <[number]> A timeout to wait for +- returns: <[Promise]> + +Returns a promise that resolves after the timeout. + +Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going to be flaky. Use signals such as network events, selectors becoming visible and others instead. + ### class: ElementHandle * extends: [JSHandle] diff --git a/docs/intro.md b/docs/intro.md index fffb2c4688583..3258984f0e341 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -5,7 +5,7 @@ - [Usage](#usage) - [Writing your first script](#writing-your-first-script) - [Debugging scripts](#debugging-scripts) -- [Deploying to CI](#deploying-to-ci) +- [Continuous Integration](#continuous-integration) ## Installation diff --git a/package-lock.json b/package-lock.json index 7163d1bc010ea..d46a31549c5fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.0.0-post", + "version": "0.15.0-post", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/frames.ts b/src/frames.ts index c10d7d476400f..333303a16c9a3 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -27,7 +27,7 @@ import * as network from './network'; import { Page } from './page'; import { selectors } from './selectors'; import * as types from './types'; -import { waitForTimeWasUsed } from './hints'; +import { waitForTimeoutWasUsed } from './hints'; type ContextType = 'main' | 'utility'; type ContextData = { @@ -755,16 +755,9 @@ export class Frame { (handle, deadline) => handle.uncheck(helper.optionsWithUpdatedTimeout(options, deadline))); } - async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: types.WaitForFunctionOptions & types.WaitForElementOptions = {}, arg?: any): Promise { - if (helper.isString(selectorOrFunctionOrTimeout)) - return this.waitForSelector(selectorOrFunctionOrTimeout, options) as any; - if (helper.isNumber(selectorOrFunctionOrTimeout)) { - waitForTimeWasUsed(this._page); - return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout)); - } - if (typeof selectorOrFunctionOrTimeout === 'function') - return this.waitForFunction(selectorOrFunctionOrTimeout as any, arg, options); - return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout))); + async waitForTimeout(timeout: number) { + waitForTimeoutWasUsed(this._page); + await new Promise(fulfill => setTimeout(fulfill, timeout)); } async waitForFunction(pageFunction: types.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise>; diff --git a/src/hints.ts b/src/hints.ts index 0a51840278089..b0b72c7f8dab2 100644 --- a/src/hints.ts +++ b/src/hints.ts @@ -22,12 +22,12 @@ const hintsLog: Log = { severity: 'warning' }; -let waitForTimeWasUsedReported = false; -export function waitForTimeWasUsed(page: Page) { - if (waitForTimeWasUsedReported) +let waitForTimeoutWasUsedReported = false; +export function waitForTimeoutWasUsed(page: Page) { + if (waitForTimeoutWasUsedReported) return; - waitForTimeWasUsedReported = true; - page._log(hintsLog, `WARNING: page.waitFor(timeout) should only be used for debugging. -It is likely that the tests using timer in production are going to be flaky. + waitForTimeoutWasUsedReported = true; + page._log(hintsLog, `WARNING: page.waitForTimeout(timeout) should only be used for debugging. +Tests using the timer in production are going to be flaky. Use signals such as network events, selectors becoming visible, etc. instead.`); } diff --git a/src/page.ts b/src/page.ts index ea3d12142a9d7..c50ee27d23b44 100644 --- a/src/page.ts +++ b/src/page.ts @@ -481,8 +481,8 @@ export class Page extends ExtendedEventEmitter implements InnerLogger { return this.mainFrame().uncheck(selector, options); } - async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & types.WaitForElementOptions, arg?: any): Promise { - return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, arg); + async waitForTimeout(timeout: number) { + await this.mainFrame().waitForTimeout(timeout); } async waitForFunction(pageFunction: types.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise>; diff --git a/test/waittask.spec.js b/test/waittask.spec.js index 0bc0a76b4336a..f2717155c7afc 100644 --- a/test/waittask.spec.js +++ b/test/waittask.spec.js @@ -18,57 +18,13 @@ const utils = require('./utils'); const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType); -describe('Page.waitFor', function() { - it('should wait for selector', async({page, server}) => { - let found = false; - const waitFor = page.waitFor('div').then(() => found = true); - await page.goto(server.EMPTY_PAGE); - expect(found).toBe(false); - await page.goto(server.PREFIX + '/grid.html'); - await waitFor; - expect(found).toBe(true); - }); - it('should wait for an xpath', async({page, server}) => { - let found = false; - const waitFor = page.waitFor('//div').then(() => found = true); - await page.goto(server.EMPTY_PAGE); - expect(found).toBe(false); - await page.goto(server.PREFIX + '/grid.html'); - await waitFor; - expect(found).toBe(true); - }); - it('should not allow you to select an element with single slash xpath', async({page, server}) => { - await page.setContent(`
some text
`); - let error = null; - await page.waitFor('/html/body/div').catch(e => error = e); - expect(error).toBeTruthy(); - }); +describe('Page.waitForTimeout', function() { it('should timeout', async({page, server}) => { const startTime = Date.now(); const timeout = 42; - await page.waitFor(timeout); + await page.waitForTimeout(timeout); expect(Date.now() - startTime).not.toBeLessThan(timeout / 2); }); - it('should work with multiline body', async({page, server}) => { - const result = await page.waitForFunction(` - (() => true)() - `); - expect(await result.jsonValue()).toBe(true); - }); - it('should wait for predicate', async({page, server}) => { - await Promise.all([ - page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide - page.setViewportSize({width: 10, height: 10}), - ]); - }); - it('should throw when unknown type', async({page, server}) => { - let error = null; - await page.waitFor({foo: 'bar'}).catch(e => error = e); - expect(error.message).toContain('Unsupported target type'); - }); - it('should wait for predicate with arguments', async({page, server}) => { - await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 2}); - }); }); describe('Frame.waitForFunction', function() { @@ -198,6 +154,15 @@ describe('Frame.waitForFunction', function() { await page.evaluate(() => window.__done = true); await watchdog; }); + it('should work with multiline body', async({page, server}) => { + const result = await page.waitForFunction(` + (() => true)() + `); + expect(await result.jsonValue()).toBe(true); + }); + it('should wait for predicate with arguments', async({page, server}) => { + await page.waitForFunction(({arg1, arg2}) => arg1 + arg2 === 3, { arg1: 1, arg2: 2}); + }); }); describe('Frame.waitForSelector', function() {