diff --git a/docs/api.md b/docs/api.md index accabd15ba3fc..df0c2ac13c594 100644 --- a/docs/api.md +++ b/docs/api.md @@ -284,7 +284,7 @@ await context.close(); - [event: 'close'](#event-close) - [event: 'page'](#event-page) - [browserContext.addCookies(cookies)](#browsercontextaddcookiescookies) -- [browserContext.addInitScript(script[, ...args])](#browsercontextaddinitscriptscript-args) +- [browserContext.addInitScript(script[, arg])](#browsercontextaddinitscriptscript-arg) - [browserContext.clearCookies()](#browsercontextclearcookies) - [browserContext.clearPermissions()](#browsercontextclearpermissions) - [browserContext.close()](#browsercontextclose) @@ -344,11 +344,11 @@ console.log(await page.evaluate('location.href')); await browserContext.addCookies([cookieObject1, cookieObject2]); ``` -#### browserContext.addInitScript(script[, ...args]) +#### browserContext.addInitScript(script[, arg]) - `script` <[function]|[string]|[Object]> Script to be evaluated in all pages in the browser context. - `path` <[string]> Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). - `content` <[string]> Raw script content. -- `...args` <...[Serializable]> Arguments to pass to `script` (only supported when passing a function). +- `arg` <[Serializable]> Optional argument to pass to `script` (only supported when passing a function). - returns: <[Promise]> Adds a script which would be evaluated in one of the following scenarios: @@ -371,7 +371,7 @@ await browserContext.addInitScript({ }); ``` -> **NOTE** The order of evaluation of multiple scripts installed via [browserContext.addInitScript(script[, ...args])](#browsercontextaddinitscriptscript-args) and [page.addInitScript(script[, ...args])](#pageaddinitscriptscript-args) is not defined. +> **NOTE** The order of evaluation of multiple scripts installed via [browserContext.addInitScript(script[, arg])](#browsercontextaddinitscriptscript-arg) and [page.addInitScript(script[, arg])](#pageaddinitscriptscript-arg) is not defined. #### browserContext.clearCookies() - returns: <[Promise]> @@ -643,10 +643,10 @@ page.removeListener('request', logRequest); - [event: 'worker'](#event-worker) - [page.$(selector)](#pageselector) - [page.$$(selector)](#pageselector-1) -- [page.$$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args) -- [page.$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args-1) +- [page.$$eval(selector, pageFunction, arg)](#pageevalselector-pagefunction-arg) +- [page.$eval(selector, pageFunction, arg)](#pageevalselector-pagefunction-arg-1) - [page.accessibility](#pageaccessibility) -- [page.addInitScript(script[, ...args])](#pageaddinitscriptscript-args) +- [page.addInitScript(script[, arg])](#pageaddinitscriptscript-arg) - [page.addScriptTag(options)](#pageaddscripttagoptions) - [page.addStyleTag(options)](#pageaddstyletagoptions) - [page.check(selector, [options])](#pagecheckselector-options) @@ -657,8 +657,8 @@ page.removeListener('request', logRequest); - [page.coverage](#pagecoverage) - [page.dblclick(selector[, options])](#pagedblclickselector-options) - [page.emulateMedia(options)](#pageemulatemediaoptions) -- [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args) -- [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args) +- [page.evaluate(pageFunction, arg)](#pageevaluatepagefunction-arg) +- [page.evaluateHandle(pageFunction, arg)](#pageevaluatehandlepagefunction-arg) - [page.exposeFunction(name, playwrightFunction)](#pageexposefunctionname-playwrightfunction) - [page.fill(selector, value[, options])](#pagefillselector-value-options) - [page.focus(selector[, options])](#pagefocusselector-options) @@ -689,9 +689,9 @@ page.removeListener('request', logRequest); - [page.uncheck(selector, [options])](#pageuncheckselector-options) - [page.url()](#pageurl) - [page.viewportSize()](#pageviewportsize) -- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args) +- [page.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#pagewaitforselectororfunctionortimeout-options-arg) - [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate) -- [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args) +- [page.waitForFunction(pageFunction, arg[, options])](#pagewaitforfunctionpagefunction-arg-options) - [page.waitForLoadState([options])](#pagewaitforloadstateoptions) - [page.waitForNavigation([options])](#pagewaitfornavigationoptions) - [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options) @@ -826,10 +826,10 @@ The method runs `document.querySelectorAll` within the page. If no elements matc Shortcut for [page.mainFrame().$$(selector)](#frameselector-1). -#### page.$$eval(selector, pageFunction[, ...args]) +#### page.$$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query page for - `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `Array.from(document.querySelectorAll(selector))` within the page and passes it as the first argument to `pageFunction`. @@ -838,13 +838,13 @@ If `pageFunction` returns a [Promise], then `page.$$eval` would wait for the pro Examples: ```js -const divsCounts = await page.$$eval('div', divs => divs.length); +const divsCounts = await page.$$eval('div', (divs, min) => divs.length >= min, 10); ``` -#### page.$eval(selector, pageFunction[, ...args]) +#### page.$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query page for - `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `document.querySelector` within the page and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error. @@ -855,19 +855,19 @@ Examples: ```js const searchValue = await page.$eval('#search', el => el.value); const preloadHref = await page.$eval('link[rel=preload]', el => el.href); -const html = await page.$eval('.main-container', e => e.outerHTML); +const html = await page.$eval('.main-container', (e, suffix) => e.outerHTML + suffix, 'hello'); ``` -Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-args). +Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-arg). #### page.accessibility - returns: <[Accessibility]> -#### page.addInitScript(script[, ...args]) +#### page.addInitScript(script[, arg]) - `script` <[function]|[string]|[Object]> Script to be evaluated in the page. - `path` <[string]> Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). - `content` <[string]> Raw script content. -- `...args` <...[Serializable]> Arguments to pass to `script` (only supported when passing a function). +- `arg` <[Serializable]> Optional argument to pass to `script` (only supported when passing a function). - returns: <[Promise]> Adds a script which would be evaluated in one of the following scenarios: @@ -887,7 +887,7 @@ const preloadFile = fs.readFileSync('./preload.js', 'utf8'); await page.addInitScript(preloadFile); ``` -> **NOTE** The order of evaluation of multiple scripts installed via [browserContext.addInitScript(script[, ...args])](#browsercontextaddinitscriptscript-args) and [page.addInitScript(script[, ...args])](#pageaddinitscriptscript-args) is not defined. +> **NOTE** The order of evaluation of multiple scripts installed via [browserContext.addInitScript(script[, arg])](#browsercontextaddinitscriptscript-arg) and [page.addInitScript(script[, arg])](#pageaddinitscriptscript-arg) is not defined. #### page.addScriptTag(options) - `options` <[Object]> @@ -1060,20 +1060,20 @@ await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').ma // → false ``` -#### page.evaluate(pageFunction[, ...args]) +#### page.evaluate(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in the page context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` If the function passed to the `page.evaluate` returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return its value. If the function passed to the `page.evaluate` returns a non-[Serializable] value, then `page.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. -Passing arguments to `pageFunction`: +Passing argument to `pageFunction`: ```js -const result = await page.evaluate(x => { - return Promise.resolve(8 * x); -}, 7); +const result = await page.evaluate(([x, y]) => { + return Promise.resolve(x * y); +}, [7, 8]); console.log(result); // prints "56" ``` @@ -1084,18 +1084,18 @@ const x = 10; console.log(await page.evaluate(`1 + ${x}`)); // prints "11" ``` -[ElementHandle] instances can be passed as arguments to the `page.evaluate`: +[ElementHandle] instances can be passed as an argument to the `page.evaluate`: ```js const bodyHandle = await page.$('body'); -const html = await page.evaluate(body => body.innerHTML, bodyHandle); +const html = await page.evaluate(([body, suffix]) => body.innerHTML + suffix, [bodyHandle, 'hello']); await bodyHandle.dispose(); ``` -Shortcut for [page.mainFrame().evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args). +Shortcut for [page.mainFrame().evaluate(pageFunction, arg)](#frameevaluatepagefunction-arg). -#### page.evaluateHandle(pageFunction[, ...args]) +#### page.evaluateHandle(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in the page context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle) The only difference between `page.evaluate` and `page.evaluateHandle` is that `page.evaluateHandle` returns in-page object (JSHandle). @@ -1107,7 +1107,7 @@ A string can also be passed in instead of a function: const aHandle = await page.evaluateHandle('document'); // Handle for the 'document' ``` -[JSHandle] instances can be passed as arguments to the `page.evaluateHandle`: +[JSHandle] instances can be passed as an argument to the `page.evaluateHandle`: ```js const aHandle = await page.evaluateHandle(() => document.body); const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle); @@ -1473,7 +1473,7 @@ Page routes take precedence over browser context routes (set up with [browserCon #### page.selectOption(selector, values[, options]) - `selector` <[string]> A selector to query frame for. -- `values` <[string]|[ElementHandle]|[Object]|[Array]<[string]>|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - `index` <[number]> Matches by the index. @@ -1627,7 +1627,7 @@ 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[, ...args]]) +#### 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`. @@ -1635,12 +1635,12 @@ This is a shortcut for [page.mainFrame().url()](#frameurl) - `'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. -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `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-options-args). +- 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 @@ -1653,14 +1653,14 @@ await page.waitFor(1000); await page.waitFor(() => !!document.querySelector('.foo')); ``` -To pass arguments from node.js to the predicate of `page.waitFor` function: +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-args). +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)`. @@ -1672,14 +1672,14 @@ Shortcut for [page.mainFrame().waitFor(selectorOrFunctionOrTimeout[, options[, . Waits for event to fire and passes its value into the predicate function. Resolves when the predicate returns truthy value. Will throw an error if the page is closed before the event is fired. -#### page.waitForFunction(pageFunction[, options[, ...args]]) +#### page.waitForFunction(pageFunction, arg[, options]) - `pageFunction` <[function]|[string]> Function to be evaluated in browser context +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - `options` <[Object]> Optional waiting parameters - `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 [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method. -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value. The `waitForFunction` can be used to observe viewport size change: @@ -1696,14 +1696,14 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'. })(); ``` -To pass arguments from node.js to the predicate of `page.waitForFunction` function: +To pass an argument from node.js to the predicate of `page.waitForFunction` function: ```js const selector = '.foo'; -await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector); +await page.waitForFunction(selector => !!document.querySelector(selector), selector); ``` -Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args). +Shortcut for [page.mainFrame().waitForFunction(pageFunction, arg, options]])](#framewaitforfunctionpagefunction-arg-options). #### page.waitForLoadState([options]) - `options` <[Object]> Navigation parameters which might have the following properties: @@ -1804,7 +1804,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. await browser.close(); })(); ``` -Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectororfunctionortimeout-options-args). +Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectororfunctionortimeout-options-arg). #### page.workers() - returns: <[Array]<[Worker]>> @@ -1853,8 +1853,8 @@ An example of getting text from an iframe element: - [frame.$(selector)](#frameselector) - [frame.$$(selector)](#frameselector-1) -- [frame.$$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args) -- [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args-1) +- [frame.$$eval(selector, pageFunction, arg)](#frameevalselector-pagefunction-arg) +- [frame.$eval(selector, pageFunction, arg)](#frameevalselector-pagefunction-arg-1) - [frame.addScriptTag(options)](#frameaddscripttagoptions) - [frame.addStyleTag(options)](#frameaddstyletagoptions) - [frame.check(selector, [options])](#framecheckselector-options) @@ -1862,8 +1862,8 @@ An example of getting text from an iframe element: - [frame.click(selector[, options])](#frameclickselector-options) - [frame.content()](#framecontent) - [frame.dblclick(selector[, options])](#framedblclickselector-options) -- [frame.evaluate(pageFunction[, ...args])](#frameevaluatepagefunction-args) -- [frame.evaluateHandle(pageFunction[, ...args])](#frameevaluatehandlepagefunction-args) +- [frame.evaluate(pageFunction, arg)](#frameevaluatepagefunction-arg) +- [frame.evaluateHandle(pageFunction, arg)](#frameevaluatehandlepagefunction-arg) - [frame.fill(selector, value[, options])](#framefillselector-value-options) - [frame.focus(selector[, options])](#framefocusselector-options) - [frame.frameElement()](#frameframeelement) @@ -1879,8 +1879,8 @@ 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[, ...args]])](#framewaitforselectororfunctionortimeout-options-args) -- [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args) +- [frame.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#framewaitforselectororfunctionortimeout-options-arg) +- [frame.waitForFunction(pageFunction, arg[, options])](#framewaitforfunctionpagefunction-arg-options) - [frame.waitForLoadState([options])](#framewaitforloadstateoptions) - [frame.waitForNavigation([options])](#framewaitfornavigationoptions) - [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options) @@ -1898,10 +1898,10 @@ The method queries frame for the selector. If there's no such element within the The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolves to `[]`. -#### frame.$$eval(selector, pageFunction[, ...args]) +#### frame.$$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query frame for - `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `Array.from(document.querySelectorAll(selector))` within the frame and passes it as the first argument to `pageFunction`. @@ -1910,13 +1910,13 @@ If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the pr Examples: ```js -const divsCounts = await frame.$$eval('div', divs => divs.length); +const divsCounts = await frame.$$eval('div', (divs, min) => divs.length >= min, 10); ``` -#### frame.$eval(selector, pageFunction[, ...args]) +#### frame.$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query frame for - `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `document.querySelector` within the frame and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error. @@ -1927,7 +1927,7 @@ Examples: ```js const searchValue = await frame.$eval('#search', el => el.value); const preloadHref = await frame.$eval('link[rel=preload]', el => el.href); -const html = await frame.$eval('.main-container', e => e.outerHTML); +const html = await frame.$eval('.main-container', (e, suffix) => e.outerHTML + suffix, 'hello'); ``` #### frame.addScriptTag(options) @@ -2034,9 +2034,9 @@ Bear in mind that if the first click of the `dblclick()` triggers a navigation e > **NOTE** `frame.dblclick()` dispatches two `click` events and a single `dblclick` event. -#### frame.evaluate(pageFunction[, ...args]) +#### frame.evaluate(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` If the function passed to the `frame.evaluate` returns a [Promise], then `frame.evaluate` would wait for the promise to resolve and return its value. @@ -2044,9 +2044,9 @@ If the function passed to the `frame.evaluate` returns a [Promise], then `frame. If the function passed to the `frame.evaluate` returns a non-[Serializable] value, then `frame.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. ```js -const result = await frame.evaluate(() => { - return Promise.resolve(8 * 7); -}); +const result = await frame.evaluate(([x, y]) => { + return Promise.resolve(x * y); +}, [7, 8]); console.log(result); // prints "56" ``` @@ -2056,16 +2056,16 @@ A string can also be passed in instead of a function. console.log(await frame.evaluate('1 + 2')); // prints "3" ``` -[ElementHandle] instances can be passed as arguments to the `frame.evaluate`: +[ElementHandle] instances can be passed as an argument to the `frame.evaluate`: ```js const bodyHandle = await frame.$('body'); -const html = await frame.evaluate(body => body.innerHTML, bodyHandle); +const html = await frame.evaluate(([body, suffix]) => body.innerHTML + suffix, [bodyHandle, 'hello']); await bodyHandle.dispose(); ``` -#### frame.evaluateHandle(pageFunction[, ...args]) +#### frame.evaluateHandle(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in the page context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle) The only difference between `frame.evaluate` and `frame.evaluateHandle` is that `frame.evaluateHandle` returns in-page object (JSHandle). @@ -2083,10 +2083,10 @@ A string can also be passed in instead of a function. const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'. ``` -[JSHandle] instances can be passed as arguments to the `frame.evaluateHandle`: +[JSHandle] instances can be passed as an argument to the `frame.evaluateHandle`: ```js const aHandle = await frame.evaluateHandle(() => document.body); -const resultHandle = await frame.evaluateHandle(body => body.innerHTML, aHandle); +const resultHandle = await frame.evaluateHandle(([body, suffix]) => body.innerHTML + suffix, [aHandle, 'hello']); console.log(await resultHandle.jsonValue()); await resultHandle.dispose(); ``` @@ -2212,7 +2212,7 @@ If `key` is a single character and no modifier keys besides `Shift` are being he #### frame.selectOption(selector, values[, options]) - `selector` <[string]> A selector to query frame for. -- `values` <[string]|[ElementHandle]|[Object]|[Array]<[string]>|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - `index` <[number]> Matches by the index. @@ -2305,7 +2305,7 @@ If there's no element matching `selector`, the method throws an error. Returns frame's url. -#### frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]]) +#### 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`. @@ -2313,39 +2313,39 @@ Returns frame's url. - `'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. -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `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-args) -- 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-options-args). +- 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 page.waitFor('.foo'); +await frame.waitFor('.foo'); // wait for 1 second -await page.waitFor(1000); +await frame.waitFor(1000); // wait for predicate -await page.waitFor(() => !!document.querySelector('.foo')); +await frame.waitFor(() => !!document.querySelector('.foo')); ``` -To pass arguments from node.js to the predicate of `page.waitFor` function: +To pass an argument from node.js to the predicate of `frame.waitFor` function: ```js const selector = '.foo'; -await page.waitFor(selector => !!document.querySelector(selector), {}, selector); +await frame.waitFor(selector => !!document.querySelector(selector), {}, selector); ``` -#### frame.waitForFunction(pageFunction[, options[, ...args]]) +#### frame.waitForFunction(pageFunction, arg[, options]) - `pageFunction` <[function]|[string]> Function to be evaluated in browser context +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - `options` <[Object]> Optional waiting parameters - `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. -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value. The `waitForFunction` can be used to observe viewport size change: @@ -2362,11 +2362,11 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'. })(); ``` -To pass arguments from node.js to the predicate of `page.waitForFunction` function: +To pass an argument from node.js to the predicate of `frame.waitForFunction` function: ```js const selector = '.foo'; -await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector); +await frame.waitForFunction(selector => !!document.querySelector(selector), selector); ``` #### frame.waitForLoadState([options]) @@ -2457,13 +2457,13 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. ElementHandle prevents DOM element from garbage collection unless the handle is [disposed](#jshandledispose). ElementHandles are auto-disposed when their origin frame gets navigated. -ElementHandle instances can be used as arguments in [`page.$eval()`](#pageevalselector-pagefunction-args) and [`page.evaluate()`](#pageevaluatepagefunction-args) methods. +ElementHandle instances can be used as an argument in [`page.$eval()`](#pageevalselector-pagefunction-arg) and [`page.evaluate()`](#pageevaluatepagefunction-arg) methods. - [elementHandle.$(selector)](#elementhandleselector) - [elementHandle.$$(selector)](#elementhandleselector-1) -- [elementHandle.$$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args) -- [elementHandle.$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args-1) +- [elementHandle.$$eval(selector, pageFunction, arg)](#elementhandleevalselector-pagefunction-arg) +- [elementHandle.$eval(selector, pageFunction, arg)](#elementhandleevalselector-pagefunction-arg-1) - [elementHandle.boundingBox()](#elementhandleboundingbox) - [elementHandle.check([options])](#elementhandlecheckoptions) - [elementHandle.click([options])](#elementhandleclickoptions) @@ -2485,8 +2485,8 @@ ElementHandle instances can be used as arguments in [`page.$eval()`](#pageevalse - [jsHandle.asElement()](#jshandleaselement) - [jsHandle.dispose()](#jshandledispose) -- [jsHandle.evaluate(pageFunction[, ...args])](#jshandleevaluatepagefunction-args) -- [jsHandle.evaluateHandle(pageFunction[, ...args])](#jshandleevaluatehandlepagefunction-args) +- [jsHandle.evaluate(pageFunction, arg)](#jshandleevaluatepagefunction-arg) +- [jsHandle.evaluateHandle(pageFunction, arg)](#jshandleevaluatehandlepagefunction-arg) - [jsHandle.getProperties()](#jshandlegetproperties) - [jsHandle.getProperty(propertyName)](#jshandlegetpropertypropertyname) - [jsHandle.jsonValue()](#jshandlejsonvalue) @@ -2504,10 +2504,10 @@ The method runs `element.querySelector` within the page. If no element matches t The method runs `element.querySelectorAll` within the page. If no elements match the selector, the return value resolves to `[]`. -#### elementHandle.$$eval(selector, pageFunction[, ...args]) +#### elementHandle.$$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query page for - `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `document.querySelectorAll` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error. @@ -2526,10 +2526,10 @@ const feedHandle = await page.$('.feed'); expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']); ``` -#### elementHandle.$eval(selector, pageFunction[, ...args]) +#### elementHandle.$eval(selector, pageFunction, arg) - `selector` <[string]> A selector to query page for - `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method runs `document.querySelector` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error. @@ -2707,7 +2707,7 @@ Throws when ```elementHandle``` does not point to an element [connected](https:/ > **NOTE** If javascript is disabled, element is scrolled into view even when already completely visible. #### elementHandle.selectOption(values[, options]) -- `values` <[string]|[ElementHandle]|[Object]|[Array]<[string]>|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - `index` <[number]> Matches by the index. @@ -2801,7 +2801,7 @@ If element is not already unchecked, it scrolls it into view if needed, and then ### class: JSHandle -JSHandle represents an in-page JavaScript object. JSHandles can be created with the [page.evaluateHandle](#pageevaluatehandlepagefunction-args) method. +JSHandle represents an in-page JavaScript object. JSHandles can be created with the [page.evaluateHandle](#pageevaluatehandlepagefunction-arg) method. ```js const windowHandle = await page.evaluateHandle(() => window); @@ -2810,13 +2810,13 @@ const windowHandle = await page.evaluateHandle(() => window); JSHandle prevents the referenced JavaScript object being garbage collected unless the handle is [disposed](#jshandledispose). JSHandles are auto-disposed when their origin frame gets navigated or the parent context gets destroyed. -JSHandle instances can be used as arguments in [`page.$eval()`](#pageevalselector-pagefunction-args), [`page.evaluate()`](#pageevaluatepagefunction-args) and [`page.evaluateHandle`](#pageevaluatehandlepagefunction-args) methods. +JSHandle instances can be used as an argument in [`page.$eval()`](#pageevalselector-pagefunction-arg), [`page.evaluate()`](#pageevaluatepagefunction-arg) and [`page.evaluateHandle()`](#pageevaluatehandlepagefunction-arg) methods. - [jsHandle.asElement()](#jshandleaselement) - [jsHandle.dispose()](#jshandledispose) -- [jsHandle.evaluate(pageFunction[, ...args])](#jshandleevaluatepagefunction-args) -- [jsHandle.evaluateHandle(pageFunction[, ...args])](#jshandleevaluatehandlepagefunction-args) +- [jsHandle.evaluate(pageFunction, arg)](#jshandleevaluatepagefunction-arg) +- [jsHandle.evaluateHandle(pageFunction, arg)](#jshandleevaluatehandlepagefunction-arg) - [jsHandle.getProperties()](#jshandlegetproperties) - [jsHandle.getProperty(propertyName)](#jshandlegetpropertypropertyname) - [jsHandle.jsonValue()](#jshandlejsonvalue) @@ -2832,9 +2832,9 @@ Returns either `null` or the object handle itself, if the object handle is an in The `jsHandle.dispose` method stops referencing the element handle. -#### jsHandle.evaluate(pageFunction[, ...args]) +#### jsHandle.evaluate(pageFunction, arg) - `pageFunction` <[function]\([Object]\)> Function to be evaluated in browser context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` This method passes this handle as the first argument to `pageFunction`. @@ -2844,12 +2844,12 @@ If `pageFunction` returns a [Promise], then `handle.evaluate` would wait for the Examples: ```js const tweetHandle = await page.$('.tweet .retweets'); -expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10'); +expect(await tweetHandle.evaluate((node, suffix) => node.innerText, ' retweets')).toBe('10 retweets'); ``` -#### jsHandle.evaluateHandle(pageFunction[, ...args]) +#### jsHandle.evaluateHandle(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle) This method passes this handle as the first argument to `pageFunction`. @@ -2858,7 +2858,7 @@ The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait for the promise to resolve and return its value. -See [Page.evaluateHandle](#pageevaluatehandlepagefunction-args) for more details. +See [page.evaluateHandle()](#pageevaluatehandlepagefunction-arg) for more details. #### jsHandle.getProperties() - returns: <[Promise]<[Map]<[string], [JSHandle]>>> @@ -3533,8 +3533,8 @@ for (const worker of page.workers()) - [event: 'close'](#event-close-2) -- [worker.evaluate(pageFunction[, ...args])](#workerevaluatepagefunction-args) -- [worker.evaluateHandle(pageFunction[, ...args])](#workerevaluatehandlepagefunction-args) +- [worker.evaluate(pageFunction, arg)](#workerevaluatepagefunction-arg) +- [worker.evaluateHandle(pageFunction, arg)](#workerevaluatehandlepagefunction-arg) - [worker.url()](#workerurl) @@ -3543,18 +3543,18 @@ for (const worker of page.workers()) Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated. -#### worker.evaluate(pageFunction[, ...args]) +#### worker.evaluate(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in the worker context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction` If the function passed to the `worker.evaluate` returns a [Promise], then `worker.evaluate` would wait for the promise to resolve and return its value. If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. -#### worker.evaluateHandle(pageFunction[, ...args]) +#### worker.evaluateHandle(pageFunction, arg) - `pageFunction` <[function]|[string]> Function to be evaluated in the page context -- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` +- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction` - returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle) The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns in-page object (JSHandle). @@ -3784,7 +3784,7 @@ const backgroundPage = await context.waitForEvent('backgroundpage'); - [event: 'close'](#event-close) - [event: 'page'](#event-page) - [browserContext.addCookies(cookies)](#browsercontextaddcookiescookies) -- [browserContext.addInitScript(script[, ...args])](#browsercontextaddinitscriptscript-args) +- [browserContext.addInitScript(script[, arg])](#browsercontextaddinitscriptscript-arg) - [browserContext.clearCookies()](#browsercontextclearcookies) - [browserContext.clearPermissions()](#browsercontextclearpermissions) - [browserContext.close()](#browsercontextclose) diff --git a/package.json b/package.json index 22bb7f5833654..22d6bcd97c853 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "build": "node utils/runWebpack.js --mode='development' && tsc -p .", "watch": "node utils/runWebpack.js --mode='development' --watch --silent | tsc -w -p .", "version": "node utils/sync_package_versions.js && npm run doc", - "test-types": "npm run generate-types && npx -p typescript@3.2 tsc -p utils/generate_types/test/tsconfig.json", + "test-types": "npm run generate-types && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json", "generate-types": "node utils/generate_types/" }, "author": { diff --git a/src/browserContext.ts b/src/browserContext.ts index 4a553a5e714a1..39ba1da6727dd 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -55,7 +55,7 @@ export interface BrowserContext { setExtraHTTPHeaders(headers: network.Headers): Promise; setOffline(offline: boolean): Promise; setHTTPCredentials(httpCredentials: types.Credentials | null): Promise; - addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]): Promise; + addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise; exposeFunction(name: string, playwrightFunction: Function): Promise; route(url: types.URLMatch, handler: network.RouteHandler): Promise; waitForEvent(event: string, optionsOrPredicate?: Function | (types.TimeoutOptions & { predicate?: Function })): Promise; @@ -102,7 +102,7 @@ export abstract class BrowserContextBase extends platform.EventEmitter implement abstract setHTTPCredentials(httpCredentials: types.Credentials | null): Promise; abstract setExtraHTTPHeaders(headers: network.Headers): Promise; abstract setOffline(offline: boolean): Promise; - abstract addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, ...args: any[]): Promise; + abstract addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, arg?: any): Promise; abstract exposeFunction(name: string, playwrightFunction: Function): Promise; abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise; abstract close(): Promise; diff --git a/src/chromium/crBrowser.ts b/src/chromium/crBrowser.ts index b814075da4f7d..041fa6a77717a 100644 --- a/src/chromium/crBrowser.ts +++ b/src/chromium/crBrowser.ts @@ -374,8 +374,8 @@ export class CRBrowserContext extends BrowserContextBase { await (page._delegate as CRPage)._networkManager.authenticate(httpCredentials); } - async addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]) { - const source = await helper.evaluationScript(script, args); + async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { + const source = await helper.evaluationScript(script, arg); this._evaluateOnNewDocumentSources.push(source); for (const page of this.pages()) await (page._delegate as CRPage).evaluateOnNewDocument(source); diff --git a/src/dom.ts b/src/dom.ts index a3d9b379b7a8a..79df175e1f870 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -51,7 +51,7 @@ export class FrameExecutionContext extends js.ExecutionContext { return null; } - async _evaluate(returnByValue: boolean, waitForNavigations: boolean, pageFunction: string | Function, ...args: any[]): Promise { + async _doEvaluateInternal(returnByValue: boolean, waitForNavigations: boolean, pageFunction: string | Function, ...args: any[]): Promise { return await this.frame._page._frameManager.waitForNavigationsCreatedBy(async () => { return this._delegate.evaluate(this, returnByValue, pageFunction, ...args); }, waitForNavigations ? undefined : { waitUntil: 'nowait' }); @@ -78,16 +78,16 @@ export class FrameExecutionContext extends js.ExecutionContext { ${custom.join(',\n')} ]) `; - this._injectedPromise = this._evaluate(false /* returnByValue */, false /* waitForNavigations */, source); + this._injectedPromise = this._doEvaluateInternal(false /* returnByValue */, false /* waitForNavigations */, source); this._injectedGeneration = selectors._generation; } return this._injectedPromise; } async _$(selector: string, scope?: ElementHandle): Promise | null> { - const handle = await this.evaluateHandle( - (injected: Injected, selector: string, scope?: Node) => injected.querySelector(selector, scope || document), - await this._injected(), selector, scope + const handle = await this.evaluateHandleInternal( + ({ injected, selector, scope }) => injected.querySelector(selector, scope || document), + { injected: await this._injected(), selector, scope } ); if (!handle.asElement()) handle.dispose(); @@ -95,9 +95,9 @@ export class FrameExecutionContext extends js.ExecutionContext { } async _$array(selector: string, scope?: ElementHandle): Promise> { - const arrayHandle = await this.evaluateHandle( - (injected: Injected, selector: string, scope?: Node) => injected.querySelectorAll(selector, scope || document), - await this._injected(), selector, scope + const arrayHandle = await this.evaluateHandleInternal( + ({ injected, selector, scope }) => injected.querySelectorAll(selector, scope || document), + { injected: await this._injected(), selector, scope } ); return arrayHandle; } @@ -132,9 +132,9 @@ export class ElementHandle extends js.JSHandle { return this; } - _evaluateInUtility: types.EvaluateWithInjected = async (pageFunction, ...args) => { + async _evaluateInUtility(pageFunction: types.FuncOn<{ injected: Injected, node: T }, Arg, R>, arg: Arg): Promise { const utility = await this._context.frame._utilityContext(); - return utility.evaluate(pageFunction as any, await utility._injected(), this, ...args); + return utility._doEvaluateInternal(true /* returnByValue */, true /* waitForNavigations */, pageFunction, { injected: await utility._injected(), node: this }, arg); } async ownerFrame(): Promise { @@ -150,7 +150,7 @@ export class ElementHandle extends js.JSHandle { } async contentFrame(): Promise { - const isFrameElement = await this._evaluateInUtility((injected, node) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME')); + const isFrameElement = await this._evaluateInUtility(({node}) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {}); if (!isFrameElement) return null; return this._page._delegate.getContentFrame(this); @@ -206,7 +206,7 @@ export class ElementHandle extends js.JSHandle { private async _offsetPoint(offset: types.Point): Promise { const [box, border] = await Promise.all([ this.boundingBox(), - this._evaluateInUtility((injected, node) => injected.getElementBorderWidth(node)).catch(debugError), + this._evaluateInUtility(({ injected, node }) => injected.getElementBorderWidth(node), {}).catch(debugError), ]); const point = { x: offset.x, y: offset.y }; if (box) { @@ -271,14 +271,14 @@ export class ElementHandle extends js.JSHandle { assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"'); } return await this._page._frameManager.waitForNavigationsCreatedBy(async () => { - return this._evaluateInUtility((injected, node, ...optionsToSelect) => injected.selectOptions(node, optionsToSelect), ...selectOptions); + return this._evaluateInUtility(({ injected, node }, selectOptions) => injected.selectOptions(node, selectOptions), selectOptions); }, options); } async fill(value: string, options?: types.NavigatingActionWaitOptions): Promise { assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"'); await this._page._frameManager.waitForNavigationsCreatedBy(async () => { - const error = await this._evaluateInUtility((injected, node, value) => injected.fill(node, value), value); + const error = await this._evaluateInUtility(({ injected, node }, value) => injected.fill(node, value), value); if (error) throw new Error(error); if (value) @@ -289,12 +289,12 @@ export class ElementHandle extends js.JSHandle { } async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[]) { - const multiple = await this._evaluateInUtility((injected: Injected, node: Node) => { - if (node.nodeType !== Node.ELEMENT_NODE || (node as Element).tagName !== 'INPUT') + const multiple = await this._evaluateInUtility(({ node }) => { + if (node.nodeType !== Node.ELEMENT_NODE || (node as Node as Element).tagName !== 'INPUT') throw new Error('Node is not an HTMLInputElement'); - const input = node as HTMLInputElement; + const input = node as Node as HTMLInputElement; return input.multiple; - }); + }, {}); let ff: string[] | types.FilePayload[]; if (!Array.isArray(files)) ff = [ files ] as string[] | types.FilePayload[]; @@ -320,12 +320,12 @@ export class ElementHandle extends js.JSHandle { } async focus() { - const errorMessage = await this._evaluateInUtility((injected: Injected, element: Node) => { - if (!(element as any)['focus']) + const errorMessage = await this._evaluateInUtility(({ node }) => { + if (!(node as any)['focus']) return 'Node is not an HTML or SVG element.'; - (element as HTMLElement|SVGElement).focus(); + (node as Node as HTMLElement | SVGElement).focus(); return false; - }); + }, {}); if (errorMessage) throw new Error(errorMessage); } @@ -353,10 +353,10 @@ export class ElementHandle extends js.JSHandle { } private async _setChecked(state: boolean, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { - if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) === state) + if (await this._evaluateInUtility(({ injected, node }) => injected.isCheckboxChecked(node), {}) === state) return; await this.click(options); - if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) !== state) + if (await this._evaluateInUtility(({ injected, node }) => injected.isCheckboxChecked(node), {}) !== state) throw new Error('Unable to click checkbox'); } @@ -376,24 +376,28 @@ export class ElementHandle extends js.JSHandle { return this._context._$$(selector, this); } - $eval: types.$Eval = async (selector, pageFunction, ...args) => { + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { const elementHandle = await this._context._$(selector, this); if (!elementHandle) throw new Error(`Error: failed to find element matching selector "${selector}"`); - const result = await elementHandle.evaluate(pageFunction, ...args as any); + const result = await elementHandle.evaluate(pageFunction, arg); elementHandle.dispose(); return result; } - $$eval: types.$$Eval = async (selector, pageFunction, ...args) => { + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { const arrayHandle = await this._context._$array(selector, this); - const result = await arrayHandle.evaluate(pageFunction, ...args as any); + const result = await arrayHandle.evaluate(pageFunction, arg); arrayHandle.dispose(); return result; } async _waitForDisplayedAtStablePosition(options: types.TimeoutOptions = {}): Promise { - const stablePromise = this._evaluateInUtility((injected, node, timeout) => { + const stablePromise = this._evaluateInUtility(({ injected, node }, timeout) => { return injected.waitForDisplayedAtStablePosition(node, timeout); }, options.timeout || 0); await helper.waitWithTimeout(stablePromise, 'element to be displayed and not moving', options.timeout || 0); @@ -409,57 +413,13 @@ export class ElementHandle extends js.JSHandle { // Translate from viewport coordinates to frame coordinates. point = { x: point.x - box.x, y: point.y - box.y }; } - const hitTargetPromise = this._evaluateInUtility((injected, node, timeout, point) => { + const hitTargetPromise = this._evaluateInUtility(({ injected, node }, { timeout, point }) => { return injected.waitForHitTargetAt(node, timeout, point); - }, options.timeout || 0, point); + }, { timeout: options.timeout || 0, point }); await helper.waitWithTimeout(hitTargetPromise, 'element to receive mouse events', options.timeout || 0); } } -export type Task = (context: FrameExecutionContext) => Promise; - -function assertPolling(polling: types.Polling) { - if (helper.isString(polling)) - assert(polling === 'raf' || polling === 'mutation', 'Unknown polling option: ' + polling); - else if (helper.isNumber(polling)) - assert(polling > 0, 'Cannot poll with non-positive interval: ' + polling); - else - throw new Error('Unknown polling options: ' + polling); -} - -export function waitForFunctionTask(selector: string | undefined, pageFunction: Function | string, options: types.WaitForFunctionOptions, ...args: any[]): Task { - const { polling = 'raf' } = options; - assertPolling(polling); - const predicateBody = helper.isString(pageFunction) ? 'return (' + pageFunction + ')' : 'return (' + pageFunction + ')(...args)'; - - return async (context: FrameExecutionContext) => context.evaluateHandle((injected: Injected, selector: string | undefined, predicateBody: string, polling: types.Polling, timeout: number, ...args) => { - const innerPredicate = new Function('...args', predicateBody); - return injected.poll(polling, selector, timeout, (element: Element | undefined): any => { - if (selector === undefined) - return innerPredicate(...args); - return innerPredicate(element, ...args); - }); - }, await context._injected(), selector, predicateBody, polling, options.timeout || 0, ...args); -} - -export function waitForSelectorTask(selector: string, waitFor: 'attached' | 'detached' | 'visible' | 'hidden', timeout: number): Task { - return async (context: FrameExecutionContext) => context.evaluateHandle((injected, selector, waitFor, timeout) => { - const polling = (waitFor === 'attached' || waitFor === 'detached') ? 'mutation' : 'raf'; - return injected.poll(polling, selector, timeout, (element: Element | undefined): Element | boolean => { - switch (waitFor) { - case 'attached': - return element || false; - case 'detached': - return !element; - case 'visible': - return element && injected.isVisible(element) ? element : false; - case 'hidden': - return !element || !injected.isVisible(element); - } - }); - }, await context._injected(), selector, waitFor, timeout); -} - export const setFileInputFunction = async (element: HTMLInputElement, payloads: types.FilePayload[]) => { const files = await Promise.all(payloads.map(async (file: types.FilePayload) => { const result = await fetch(`data:${file.type};base64,${file.data}`); diff --git a/src/firefox/ffBrowser.ts b/src/firefox/ffBrowser.ts index 2a8a066dae71b..680acbb47df66 100644 --- a/src/firefox/ffBrowser.ts +++ b/src/firefox/ffBrowser.ts @@ -269,8 +269,8 @@ export class FFBrowserContext extends BrowserContextBase { await this._browser._connection.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId || undefined, credentials: httpCredentials }); } - async addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]) { - const source = await helper.evaluationScript(script, args); + async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { + const source = await helper.evaluationScript(script, arg); this._evaluateOnNewDocumentSources.push(source); await this._browser._connection.send('Browser.addScriptToEvaluateOnNewDocument', { browserContextId: this._browserContextId || undefined, script: source }); } diff --git a/src/firefox/ffPage.ts b/src/firefox/ffPage.ts index 215ebac9467ec..f3eaca5ed6fba 100644 --- a/src/firefox/ffPage.ts +++ b/src/firefox/ffPage.ts @@ -355,7 +355,7 @@ export class FFPage implements PageDelegate { async takeScreenshot(format: 'png' | 'jpeg', documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined): Promise { if (!documentRect) { const context = await this._page.mainFrame()._utilityContext(); - const scrollOffset = await context.evaluate(() => ({ x: window.scrollX, y: window.scrollY })); + const scrollOffset = await context.evaluateInternal(() => ({ x: window.scrollX, y: window.scrollY })); documentRect = { x: viewportRect!.x + scrollOffset.x, y: viewportRect!.y + scrollOffset.y, diff --git a/src/frames.ts b/src/frames.ts index 7d6996072a7e7..910a28ae64eb2 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -410,14 +410,18 @@ export class Frame { return this._context('utility'); } - evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => { + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg?: any): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise> { const context = await this._mainContext(); - return context.evaluateHandle(pageFunction, ...args as any); + return context.evaluateHandleInternal(pageFunction, arg); } - evaluate: types.Evaluate = async (pageFunction, ...args) => { + async evaluate(pageFunction: types.Func1, arg: Arg): Promise; + async evaluate(pageFunction: types.Func1, arg?: any): Promise; + async evaluate(pageFunction: types.Func1, arg: Arg): Promise { const context = await this._mainContext(); - return context.evaluate(pageFunction, ...args as any); + return context.evaluateInternal(pageFunction, arg); } async $(selector: string): Promise | null> { @@ -439,7 +443,7 @@ export class Frame { if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor)) throw new Error(`Unsupported waitFor option "${waitFor}"`); - const task = dom.waitForSelectorTask(selector, waitFor, timeout); + const task = waitForSelectorTask(selector, waitFor, timeout); const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, waitFor)}"`); if (!result.asElement()) { result.dispose(); @@ -455,20 +459,24 @@ export class Frame { return handle; } - $eval: types.$Eval = async (selector, pageFunction, ...args) => { + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { const context = await this._mainContext(); const elementHandle = await context._$(selector); if (!elementHandle) throw new Error(`Error: failed to find element matching selector "${selector}"`); - const result = await elementHandle.evaluate(pageFunction, ...args as any); + const result = await elementHandle.evaluate(pageFunction, arg); elementHandle.dispose(); return result; } - $$eval: types.$$Eval = async (selector, pageFunction, ...args) => { + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { const context = await this._mainContext(); const arrayHandle = await context._$array(selector); - const result = await arrayHandle.evaluate(pageFunction, ...args as any); + const result = await arrayHandle.evaluate(pageFunction, arg); arrayHandle.dispose(); return result; } @@ -480,7 +488,7 @@ export class Frame { async content(): Promise { const context = await this._utilityContext(); - return context.evaluate(() => { + return context.evaluateInternal(() => { let retVal = ''; if (document.doctype) retVal = new XMLSerializer().serializeToString(document.doctype); @@ -500,13 +508,13 @@ export class Frame { this.waitForLoadState(options).then(resolve).catch(reject); }); }); - const contentPromise = context.evaluate((html, tag) => { + const contentPromise = context.evaluateInternal(({ html, tag }) => { window.stop(); document.open(); console.debug(tag); // eslint-disable-line no-console document.write(html); document.close(); - }, html, tag); + }, { html, tag }); await Promise.all([contentPromise, lifecyclePromise]); } @@ -547,20 +555,20 @@ export class Frame { const context = await this._mainContext(); return this._raceWithCSPError(async () => { if (url !== null) - return (await context.evaluateHandle(addScriptUrl, url, type)).asElement()!; + return (await context.evaluateHandleInternal(addScriptUrl, { url, type })).asElement()!; if (path !== null) { let contents = await platform.readFileAsync(path, 'utf8'); contents += '//# sourceURL=' + path.replace(/\n/g, ''); - return (await context.evaluateHandle(addScriptContent, contents, type)).asElement()!; + return (await context.evaluateHandleInternal(addScriptContent, { content: contents, type })).asElement()!; } - return (await context.evaluateHandle(addScriptContent, content!, type)).asElement()!; + return (await context.evaluateHandleInternal(addScriptContent, { content: content!, type })).asElement()!; }); - async function addScriptUrl(url: string, type: string): Promise { + async function addScriptUrl(options: { url: string, type: string }): Promise { const script = document.createElement('script'); - script.src = url; - if (type) - script.type = type; + script.src = options.url; + if (options.type) + script.type = options.type; const promise = new Promise((res, rej) => { script.onload = res; script.onerror = rej; @@ -570,10 +578,10 @@ export class Frame { return script; } - function addScriptContent(content: string, type: string = 'text/javascript'): HTMLElement { + function addScriptContent(options: { content: string, type: string }): HTMLElement { const script = document.createElement('script'); - script.type = type; - script.text = content; + script.type = options.type || 'text/javascript'; + script.text = options.content; let error = null; script.onerror = e => error = e; document.head.appendChild(script); @@ -595,15 +603,15 @@ export class Frame { const context = await this._mainContext(); return this._raceWithCSPError(async () => { if (url !== null) - return (await context.evaluateHandle(addStyleUrl, url)).asElement()!; + return (await context.evaluateHandleInternal(addStyleUrl, url)).asElement()!; if (path !== null) { let contents = await platform.readFileAsync(path, 'utf8'); contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; - return (await context.evaluateHandle(addStyleContent, contents)).asElement()!; + return (await context.evaluateHandleInternal(addStyleContent, contents)).asElement()!; } - return (await context.evaluateHandle(addStyleContent, content!)).asElement()!; + return (await context.evaluateHandleInternal(addStyleContent, content!)).asElement()!; }); async function addStyleUrl(url: string): Promise { @@ -724,32 +732,47 @@ export class Frame { handle.dispose(); } - async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: types.WaitForFunctionOptions & types.WaitForElementOptions = {}, ...args: any[]): Promise { + 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)) return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout)); if (typeof selectorOrFunctionOrTimeout === 'function') - return this.waitForFunction(selectorOrFunctionOrTimeout, options, ...args); + return this.waitForFunction(selectorOrFunctionOrTimeout as any, arg, options); return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout))); } private async _waitForSelectorInUtilityContext(selector: string, options?: types.WaitForElementOptions): Promise> { const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'attached' } = (options || {}); - const task = dom.waitForSelectorTask(selector, waitFor, timeout); + const task = waitForSelectorTask(selector, waitFor, timeout); const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, waitFor)}"`); return result.asElement() as dom.ElementHandle; } - async waitForFunction(pageFunction: Function | string, options?: types.WaitForFunctionOptions, ...args: any[]): Promise { - options = { timeout: this._page._timeoutSettings.timeout(), ...(options || {}) }; - const task = dom.waitForFunctionTask(undefined, pageFunction, options, ...args); - return this._scheduleRerunnableTask(task, 'main', options.timeout); + async waitForFunction(pageFunction: types.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise>; + async waitForFunction(pageFunction: types.Func1, arg?: any, options?: types.WaitForFunctionOptions): Promise>; + async waitForFunction(pageFunction: types.Func1, arg: Arg, options: types.WaitForFunctionOptions = {}): Promise> { + const { polling = 'raf', timeout = this._page._timeoutSettings.timeout() } = options; + if (helper.isString(polling)) + assert(polling === 'raf' || polling === 'mutation', 'Unknown polling option: ' + polling); + else if (helper.isNumber(polling)) + assert(polling > 0, 'Cannot poll with non-positive interval: ' + polling); + else + throw new Error('Unknown polling options: ' + polling); + const predicateBody = helper.isString(pageFunction) ? 'return (' + pageFunction + ')' : 'return (' + pageFunction + ')(arg)'; + + const task = async (context: dom.FrameExecutionContext) => context.evaluateHandleInternal(({ injected, predicateBody, polling, timeout, arg }) => { + const innerPredicate = new Function('arg', predicateBody); + return injected.poll(polling, undefined, timeout, (element: Element | undefined): any => { + return innerPredicate(arg); + }); + }, { injected: await context._injected(), predicateBody, polling, timeout, arg }); + return this._scheduleRerunnableTask(task, 'main', timeout) as any as types.SmartHandle; } async title(): Promise { const context = await this._utilityContext(); - return context.evaluate(() => document.title); + return context.evaluateInternal(() => document.title); } _onDetached() { @@ -764,7 +787,7 @@ export class Frame { this._parentFrame = null; } - private _scheduleRerunnableTask(task: dom.Task, contextType: ContextType, timeout?: number, title?: string): Promise { + private _scheduleRerunnableTask(task: Task, contextType: ContextType, timeout?: number, title?: string): Promise { const data = this._contextData.get(contextType)!; const rerunnableTask = new RerunnableTask(data, task, timeout, title); data.rerunnableTasks.add(rerunnableTask); @@ -805,17 +828,37 @@ export class Frame { } } +type Task = (context: dom.FrameExecutionContext) => Promise; + +function waitForSelectorTask(selector: string, waitFor: 'attached' | 'detached' | 'visible' | 'hidden', timeout: number): Task { + return async (context: dom.FrameExecutionContext) => context.evaluateHandleInternal(({ injected, selector, waitFor, timeout }) => { + const polling = (waitFor === 'attached' || waitFor === 'detached') ? 'mutation' : 'raf'; + return injected.poll(polling, selector, timeout, (element: Element | undefined): Element | boolean => { + switch (waitFor) { + case 'attached': + return element || false; + case 'detached': + return !element; + case 'visible': + return element && injected.isVisible(element) ? element : false; + case 'hidden': + return !element || !injected.isVisible(element); + } + }); + }, { injected: await context._injected(), selector, waitFor, timeout }); +} + class RerunnableTask { readonly promise: Promise; private _contextData: ContextData; - private _task: dom.Task; + private _task: Task; private _runCount: number; private _resolve: (result: js.JSHandle) => void = () => {}; private _reject: (reason: Error) => void = () => {}; private _timeoutTimer?: NodeJS.Timer; private _terminated = false; - constructor(data: ContextData, task: dom.Task, timeout?: number, title?: string) { + constructor(data: ContextData, task: Task, timeout?: number, title?: string) { this._contextData = data; this._task = task; this._runCount = 0; @@ -856,7 +899,7 @@ class RerunnableTask { // Ignore timeouts in pageScript - we track timeouts ourselves. // If execution context has been already destroyed, `context.evaluate` will // throw an error - ignore this predicate run altogether. - if (!error && await context.evaluate(s => !s, success).catch(e => true)) { + if (!error && await context.evaluateInternal(s => !s, success).catch(e => true)) { success!.dispose(); return; } diff --git a/src/helper.ts b/src/helper.ts index 8b3a1aac17a1b..a91087a640741 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -30,7 +30,7 @@ export type RegisteredListener = { class Helper { static evaluationString(fun: Function | string, ...args: any[]): string { if (Helper.isString(fun)) { - assert(args.length === 0, 'Cannot evaluate a string with arguments'); + assert(args.length === 0 || (args.length === 1 && args[0] === undefined), 'Cannot evaluate a string with arguments'); return fun; } return `(${fun})(${args.map(serializeArgument).join(',')})`; @@ -42,7 +42,7 @@ class Helper { } } - static async evaluationScript(fun: Function | string | { path?: string, content?: string }, args: any[] = [], addSourceUrl: boolean = true): Promise { + static async evaluationScript(fun: Function | string | { path?: string, content?: string }, arg?: any, addSourceUrl: boolean = true): Promise { if (!helper.isString(fun) && typeof fun !== 'function') { if (fun.content !== undefined) { fun = fun.content; @@ -55,7 +55,7 @@ class Helper { throw new Error('Either path or content property must be present'); } } - return helper.evaluationString(fun, ...args); + return helper.evaluationString(fun, arg); } static installApiHooks(className: string, classType: any) { diff --git a/src/javascript.ts b/src/javascript.ts index 92a17bb950191..5e693091761ef 100644 --- a/src/javascript.ts +++ b/src/javascript.ts @@ -33,20 +33,24 @@ export class ExecutionContext { this._delegate = delegate; } - _adoptIfNeeded(handle: JSHandle): Promise | null { - return null; + _doEvaluateInternal(returnByValue: boolean, waitForNavigations: boolean, pageFunction: string | Function, ...args: any[]): Promise { + return this._delegate.evaluate(this, returnByValue, pageFunction, ...args); } - _evaluate(returnByValue: boolean, waitForNavigations: boolean, pageFunction: string | Function, ...args: any[]): Promise { - return this._delegate.evaluate(this, returnByValue, pageFunction, ...args); + _adoptIfNeeded(handle: JSHandle): Promise | null { + return null; } - evaluate: types.Evaluate = async (pageFunction, ...args) => { - return this._evaluate(true /* returnByValue */, true /* waitForNavigations */, pageFunction, ...args); + async evaluateInternal(pageFunction: types.Func0): Promise; + async evaluateInternal(pageFunction: types.Func1, arg: Arg): Promise; + async evaluateInternal(pageFunction: never, ...args: never[]): Promise { + return this._doEvaluateInternal(true /* returnByValue */, true /* waitForNavigations */, pageFunction, ...args); } - evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => { - return this._evaluate(false /* returnByValue */, true /* waitForNavigations */, pageFunction, ...args); + async evaluateHandleInternal(pageFunction: types.Func0): Promise>; + async evaluateHandleInternal(pageFunction: types.Func1, arg: Arg): Promise>; + async evaluateHandleInternal(pageFunction: never, ...args: never[]): Promise { + return this._doEvaluateInternal(false /* returnByValue */, true /* waitForNavigations */, pageFunction, ...args); } _createHandle(remoteObject: any): JSHandle { @@ -64,12 +68,16 @@ export class JSHandle { this._remoteObject = remoteObject; } - evaluate: types.EvaluateOn = (pageFunction, ...args) => { - return this._context.evaluate(pageFunction as any, this, ...args); + async evaluate(pageFunction: types.FuncOn, arg: Arg): Promise; + async evaluate(pageFunction: types.FuncOn, arg?: any): Promise; + async evaluate(pageFunction: types.FuncOn, arg: Arg): Promise { + return this._context._doEvaluateInternal(true /* returnByValue */, true /* waitForNavigations */, pageFunction, this, arg); } - evaluateHandle: types.EvaluateHandleOn = (pageFunction, ...args) => { - return this._context.evaluateHandle(pageFunction as any, this, ...args); + async evaluateHandle(pageFunction: types.FuncOn, arg: Arg): Promise>; + async evaluateHandle(pageFunction: types.FuncOn, arg?: any): Promise>; + async evaluateHandle(pageFunction: types.FuncOn, arg: Arg): Promise> { + return this._context._doEvaluateInternal(false /* returnByValue */, true /* waitForNavigations */, pageFunction, this, arg); } async getProperty(propertyName: string): Promise { diff --git a/src/page.ts b/src/page.ts index 6ce0d331d93a6..27d15db182e6b 100644 --- a/src/page.ts +++ b/src/page.ts @@ -215,16 +215,22 @@ export class Page extends platform.EventEmitter { return this.mainFrame().waitForSelector(selector, options); } - evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => { - return this.mainFrame().evaluateHandle(pageFunction, ...args as any); + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg?: any): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise> { + return this.mainFrame().evaluateHandle(pageFunction, arg); } - $eval: types.$Eval = async (selector, pageFunction, ...args) => { - return this.mainFrame().$eval(selector, pageFunction, ...args as any); + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { + return this.mainFrame().$eval(selector, pageFunction, arg); } - $$eval: types.$$Eval = async (selector, pageFunction, ...args) => { - return this.mainFrame().$$eval(selector, pageFunction, ...args as any); + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg?: any): Promise; + async $$eval(selector: string, pageFunction: types.FuncOn, arg: Arg): Promise { + return this.mainFrame().$$eval(selector, pageFunction, arg); } async $$(selector: string): Promise[]> { @@ -361,12 +367,14 @@ export class Page extends platform.EventEmitter { return this._state.viewportSize; } - evaluate: types.Evaluate = async (pageFunction, ...args) => { - return this.mainFrame().evaluate(pageFunction, ...args as any); + async evaluate(pageFunction: types.Func1, arg: Arg): Promise; + async evaluate(pageFunction: types.Func1, arg?: any): Promise; + async evaluate(pageFunction: types.Func1, arg: Arg): Promise { + return this.mainFrame().evaluate(pageFunction, arg); } - async addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]) { - await this._delegate.evaluateOnNewDocument(await helper.evaluationScript(script, args)); + async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { + await this._delegate.evaluateOnNewDocument(await helper.evaluationScript(script, arg)); } _needsRequestInterception(): boolean { @@ -462,12 +470,14 @@ export class Page extends platform.EventEmitter { return this.mainFrame().uncheck(selector, options); } - async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & types.WaitForElementOptions, ...args: any[]): Promise { - return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args); + async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & types.WaitForElementOptions, arg?: any): Promise { + return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, arg); } - async waitForFunction(pageFunction: Function | string, options?: types.WaitForFunctionOptions, ...args: any[]): Promise { - return this.mainFrame().waitForFunction(pageFunction, options, ...args); + async waitForFunction(pageFunction: types.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise>; + async waitForFunction(pageFunction: types.Func1, arg?: any, options?: types.WaitForFunctionOptions): Promise>; + async waitForFunction(pageFunction: types.Func1, arg: Arg, options?: types.WaitForFunctionOptions): Promise> { + return this.mainFrame().waitForFunction(pageFunction, arg, options); } workers(): Worker[] { @@ -533,12 +543,16 @@ export class Worker extends platform.EventEmitter { return this._url; } - evaluate: types.Evaluate = async (pageFunction, ...args) => { - return (await this._executionContextPromise).evaluate(pageFunction, ...args as any); + async evaluate(pageFunction: types.Func1, arg: Arg): Promise; + async evaluate(pageFunction: types.Func1, arg?: any): Promise; + async evaluate(pageFunction: types.Func1, arg: Arg): Promise { + return (await this._executionContextPromise).evaluateInternal(pageFunction, arg); } - evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => { - return (await this._executionContextPromise).evaluateHandle(pageFunction, ...args as any); + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg?: any): Promise>; + async evaluateHandle(pageFunction: types.Func1, arg: Arg): Promise> { + return (await this._executionContextPromise).evaluateHandleInternal(pageFunction, arg); } } @@ -568,7 +582,7 @@ export class PageBinding { else expression = helper.evaluationString(deliverErrorValue, name, seq, error); } - context.evaluate(expression).catch(debugError); + context.evaluateInternal(expression).catch(debugError); function deliverResult(name: string, seq: number, result: any) { (window as any)[name]['callbacks'].get(seq).resolve(result); diff --git a/src/screenshotter.ts b/src/screenshotter.ts index dcec2d562611c..488e39cb773ae 100644 --- a/src/screenshotter.ts +++ b/src/screenshotter.ts @@ -41,7 +41,7 @@ export class Screenshotter { let viewportSize = originalViewportSize; if (!viewportSize) { const context = await this._page.mainFrame()._utilityContext(); - viewportSize = await context.evaluate(() => { + viewportSize = await context.evaluateInternal(() => { if (!document.body || !document.documentElement) return null; return { @@ -57,7 +57,7 @@ export class Screenshotter { private async _fullPageSize(): Promise { const context = await this._page.mainFrame()._utilityContext(); - const fullPageSize = await context.evaluate(() => { + const fullPageSize = await context.evaluateInternal(() => { if (!document.body || !document.documentElement) return null; return { @@ -130,7 +130,7 @@ export class Screenshotter { } const context = await this._page.mainFrame()._utilityContext(); - const scrollOffset = await context.evaluate(() => ({ x: window.scrollX, y: window.scrollY })); + const scrollOffset = await context.evaluateInternal(() => ({ x: window.scrollX, y: window.scrollY })); const documentRect = { ...boundingBox }; documentRect.x += scrollOffset.x; documentRect.y += scrollOffset.y; diff --git a/src/selectors.ts b/src/selectors.ts index 92584ae5304ba..eccf8b500bea5 100644 --- a/src/selectors.ts +++ b/src/selectors.ts @@ -15,7 +15,6 @@ */ import * as dom from './dom'; -import Injected from './injected/injected'; import { helper } from './helper'; let selectors: Selectors; @@ -40,7 +39,7 @@ export class Selectors { // Note: keep in sync with Injected class, and also keep 'zs' for future. if (['css', 'xpath', 'text', 'id', 'zs', 'data-testid', 'data-test-id', 'data-test'].includes(name)) throw new Error(`"${name}" is a predefined selector engine`); - const source = await helper.evaluationScript(script, [], false); + const source = await helper.evaluationScript(script, undefined, false); if (this._engines.has(name)) throw new Error(`"${name}" selector engine has been already registered`); this._engines.set(name, source); @@ -49,8 +48,8 @@ export class Selectors { async _createSelector(name: string, handle: dom.ElementHandle): Promise { const mainContext = await handle._page.mainFrame()._mainContext(); - return mainContext.evaluate((injected: Injected, target: Element, name: string) => { + return mainContext.evaluateInternal(({ injected, target, name }) => { return injected.engines.get(name)!.create(document.documentElement, target); - }, await mainContext._injected(), handle, name); + }, { injected: await mainContext._injected(), target: handle, name }); } } diff --git a/src/types.ts b/src/types.ts index a9ad12b55bdce..72eb54e7787db 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,23 +16,19 @@ import * as js from './javascript'; import * as dom from './dom'; -import Injected from './injected/injected'; - -type BoxedArg = js.JSHandle | (Arg extends object ? { [Key in keyof Arg]: BoxedArg } : Arg); -type Boxed = { [Index in keyof Args]: BoxedArg }; -type PageFunction = string | ((...args: Args) => R | Promise); -type PageFunctionOn = string | ((on: On, ...args: Args) => R | Promise); -type PageFunctionWithInjected = string | ((injected: Injected, on: On, ...args: Args) => R | Promise); - -type Handle = T extends Node ? dom.ElementHandle : js.JSHandle; - -export type Evaluate = (pageFunction: PageFunction, ...args: Boxed) => Promise; -export type EvaluateHandle = (pageFunction: PageFunction, ...args: Boxed) => Promise>; -export type $Eval = (selector: string, pageFunction: PageFunctionOn, ...args: Boxed) => Promise; -export type $$Eval = (selector: string, pageFunction: PageFunctionOn, ...args: Boxed) => Promise; -export type EvaluateOn = (pageFunction: PageFunctionOn, ...args: Boxed) => Promise; -export type EvaluateHandleOn = (pageFunction: PageFunctionOn, ...args: Boxed) => Promise>; -export type EvaluateWithInjected = (pageFunction: PageFunctionWithInjected, ...args: Boxed) => Promise; + +type NoHandles = Arg extends js.JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles } : Arg); +type Unboxed = + Arg extends dom.ElementHandle ? T : + Arg extends js.JSHandle ? T : + Arg extends NoHandles ? Arg : + Arg extends Array ? Array> : + Arg extends object ? { [Key in keyof Arg]: Unboxed } : + Arg; +export type Func0 = string | (() => R | Promise); +export type Func1 = string | ((arg: Unboxed) => R | Promise); +export type FuncOn = string | ((on: On, arg2: Unboxed) => R | Promise); +export type SmartHandle = T extends Node ? dom.ElementHandle : js.JSHandle; export type Size = { width: number, height: number }; export type Point = { x: number, y: number }; diff --git a/src/webkit/wkBrowser.ts b/src/webkit/wkBrowser.ts index cf38ffe697ab4..c46240097f92c 100644 --- a/src/webkit/wkBrowser.ts +++ b/src/webkit/wkBrowser.ts @@ -305,8 +305,8 @@ export class WKBrowserContext extends BrowserContextBase { await (page._delegate as WKPage).updateHttpCredentials(); } - async addInitScript(script: Function | string | { path?: string, content?: string }, ...args: any[]) { - const source = await helper.evaluationScript(script, args); + async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) { + const source = await helper.evaluationScript(script, arg); this._evaluateOnNewDocumentSources.push(source); for (const page of this.pages()) await (page._delegate as WKPage)._updateBootstrapScript(); diff --git a/test/evaluation.spec.js b/test/evaluation.spec.js index 53414b1f9605f..dc2c28113e024 100644 --- a/test/evaluation.spec.js +++ b/test/evaluation.spec.js @@ -77,12 +77,11 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT, }); it('should work with function shorthands', async({page, server}) => { const a = { - sum(a, b) { return a + b; }, - - async mult(a, b) { return a * b; } + sum([a, b]) { return a + b; }, + async mult([a, b]) { return a * b; } }; - expect(await page.evaluate(a.sum, 1, 2)).toBe(3); - expect(await page.evaluate(a.mult, 2, 4)).toBe(8); + expect(await page.evaluate(a.sum, [1, 2])).toBe(3); + expect(await page.evaluate(a.mult, [2, 4])).toBe(8); }); it('should work with unicode chars', async({page, server}) => { const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42}); @@ -120,7 +119,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT, it('should work from-inside an exposed function', async({page, server}) => { // Setup inpage callback, which calls Page.evaluate await page.exposeFunction('callController', async function(a, b) { - return await page.evaluate((a, b) => a * b, a, b); + return await page.evaluate(({ a, b }) => a * b, { a, b }); }); const result = await page.evaluate(async function() { return await callController(9, 3); @@ -168,7 +167,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT, expect(Object.is(result, -Infinity)).toBe(true); }); it('should accept "undefined" as one of multiple parameters', async({page, server}) => { - const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo'); + const result = await page.evaluate(({ a, b }) => Object.is(a, undefined) && Object.is(b, 'foo'), { a: undefined, b: 'foo' }); expect(result).toBe(true); }); it('should properly serialize undefined arguments', async({page}) => { @@ -440,8 +439,8 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT, await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const frame = page.frames()[1]; const context = await frame._utilityContext(); - const elementHandle = await context.evaluateHandle(() => window.top.document.querySelector('#frame1')); - const constructorName = await context.evaluate(node => node.constructor.name, elementHandle); + const elementHandle = await context.evaluateHandleInternal(() => window.top.document.querySelector('#frame1')); + const constructorName = await context.evaluateInternal(node => node.constructor.name, elementHandle); expect(constructorName).toBe('HTMLIFrameElement'); }); }); diff --git a/test/jshandle.spec.js b/test/jshandle.spec.js index 3395f398c314c..5d52ba5be197a 100644 --- a/test/jshandle.spec.js +++ b/test/jshandle.spec.js @@ -56,9 +56,9 @@ module.exports.describe = function({testRunner, expect, CHROMIUM, FFOX, WEBKIT}) const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' })); const bar = await page.evaluateHandle(() => 5); const baz = await page.evaluateHandle(() => (['baz'])); - const result = await page.evaluate((a1, a2) => { - return JSON.stringify({ a1, a2 }); - }, { foo }, { bar, arr: [{ baz }] }); + const result = await page.evaluate(x => { + return JSON.stringify(x); + }, { a1: { foo }, a2: { bar, arr: [{ baz }] } }); expect(JSON.parse(result)).toEqual({ a1: { foo: { x: 1, y: 'foo' } }, a2: { bar: 5, arr: [{ baz: ['baz'] }] } diff --git a/test/utils.js b/test/utils.js index 4f15bfb3ba766..52cbe80aca8bd 100644 --- a/test/utils.js +++ b/test/utils.js @@ -118,7 +118,7 @@ const utils = module.exports = { */ attachFrame: async function(page, frameId, url) { const frames = new Set(page.frames()); - const handle = await page.evaluateHandle(attachFrame, frameId, url); + const handle = await page.evaluateHandle(attachFrame, { frameId, url }); try { return await handle.asElement().contentFrame(); } catch(e) { @@ -130,7 +130,7 @@ const utils = module.exports = { } return null; - async function attachFrame(frameId, url) { + async function attachFrame({ frameId, url }) { const frame = document.createElement('iframe'); frame.src = url; frame.id = frameId; @@ -159,9 +159,9 @@ const utils = module.exports = { * @param {string} url */ navigateFrame: async function(page, frameId, url) { - await page.evaluate(navigateFrame, frameId, url); + await page.evaluate(navigateFrame, { frameId, url }); - function navigateFrame(frameId, url) { + function navigateFrame({ frameId, url }) { const frame = document.getElementById(frameId); frame.src = url; return new Promise(x => frame.onload = x); diff --git a/test/waittask.spec.js b/test/waittask.spec.js index 7eb231dcaa1bf..40a0e7beaf899 100644 --- a/test/waittask.spec.js +++ b/test/waittask.spec.js @@ -100,12 +100,12 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO return false; } return Date.now() - window.__startTime; - }, {polling}); + }, {}, {polling}); expect(timeDelta).not.toBeLessThan(polling); }); it('should poll on mutation', async({page, server}) => { let success = false; - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'}) + const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'mutation'}) .then(() => success = true); await page.evaluate(() => window.__FOO = 'hit'); expect(success).toBe(false); @@ -113,7 +113,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO await watchdog; }); it('should poll on raf', async({page, server}) => { - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'}); + const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}); await page.evaluate(() => window.__FOO = 'hit'); await watchdog; }); @@ -122,7 +122,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO await page.goto(server.EMPTY_PAGE); let error = null; await Promise.all([ - page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'}).catch(e => error = e), + page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}).catch(e => error = e), page.evaluate(() => window.__FOO = 'hit') ]); expect(error).toBe(null); @@ -130,7 +130,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO it('should throw on bad polling value', async({page, server}) => { let error = null; try { - await page.waitForFunction(() => !!document.body, {polling: 'unknown'}); + await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'}); } catch (e) { error = e; } @@ -140,7 +140,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO it('should throw negative polling interval', async({page, server}) => { let error = null; try { - await page.waitForFunction(() => !!document.body, {polling: -10}); + await page.waitForFunction(() => !!document.body, {}, {polling: -10}); } catch (e) { error = e; } @@ -157,14 +157,14 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO await page.setContent('
'); const div = await page.$('div'); let resolved = false; - const waitForFunction = page.waitForFunction(element => !element.parentElement, {}, div).then(() => resolved = true); + const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true); expect(resolved).toBe(false); await page.evaluate(element => element.remove(), div); await waitForFunction; }); it('should respect timeout', async({page}) => { let error = null; - await page.waitForFunction('false', {timeout: 10}).catch(e => error = e); + await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('waiting for function failed: timeout'); expect(error).toBeInstanceOf(playwright.errors.TimeoutError); @@ -180,7 +180,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO const watchdog = page.waitForFunction(() => { window.__counter = (window.__counter || 0) + 1; return window.__injected; - }, {timeout: 0, polling: 10}); + }, {}, {timeout: 0, polling: 10}); await page.waitForFunction(() => window.__counter > 10); await page.evaluate(() => window.__injected = true); await watchdog; diff --git a/test/web.spec.js b/test/web.spec.js index 6320bf87a4d85..7b35046dda486 100644 --- a/test/web.spec.js +++ b/test/web.spec.js @@ -41,7 +41,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, b state.page = await state.hostBrowser.newPage(); state.page.on('console', message => console.log('TEST: ' + message.text())); await state.page.goto(state.sourceServer.PREFIX + '/test/assets/playwrightweb.html'); - await state.page.evaluate((product, wsEndpoint) => setup(product, wsEndpoint), product.toLowerCase(), state.controlledBrowserApp.wsEndpoint()); + await state.page.evaluate(([product, wsEndpoint]) => setup(product, wsEndpoint), [product.toLowerCase(), state.controlledBrowserApp.wsEndpoint()]); }); afterEach(async state => { diff --git a/utils/doclint/check_public_api/JSBuilder.js b/utils/doclint/check_public_api/JSBuilder.js index ca66239ef1abe..a4b464620ce58 100644 --- a/utils/doclint/check_public_api/JSBuilder.js +++ b/utils/doclint/check_public_api/JSBuilder.js @@ -212,7 +212,7 @@ function checkSources(sources, externalDependencies) { * @return {!Documentation.Type} */ function serializeType(type, circular = []) { - let typeName = checker.typeToString(type); + let typeName = checker.typeToString(type).replace(/SmartHandle/g, 'Handle'); if (typeName === 'any' || typeName === '{ [x: string]: string; }') typeName = 'Object'; const nextCircular = [typeName].concat(circular); diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index 23c76710fee0e..3ce6f90f62708 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -16,29 +16,37 @@ import { Protocol } from './protocol'; import { ChildProcess } from 'child_process'; import { EventEmitter } from 'events'; + /** * Can be converted to JSON */ -type Serializable = {} -type ConnectionTransport = {} - -type Boxed = { [Index in keyof Args]: Args[Index] | JSHandle }; -type PageFunction = string | ((...args: Args) => R | Promise); -type PageFunctionOn = string | ((on: On, ...args: Args) => R | Promise); - -type Handle = T extends Node ? ElementHandle : JSHandle; +type Serializable = {}; + +type NoHandles = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles } : Arg); +type Unboxed = + Arg extends ElementHandle ? T : + Arg extends JSHandle ? T : + Arg extends NoHandles ? Arg : + Arg extends Array ? Array> : + Arg extends object ? { [Key in keyof Arg]: Unboxed } : + Arg; +type PageFunction = string | ((arg: Unboxed) => R | Promise); +type PageFunctionOn = string | ((on: On, arg2: Unboxed) => R | Promise); +type SmartHandle = T extends Node ? ElementHandle : JSHandle; type ElementHandleForTag = ElementHandle; +type HTMLOrSVGElement = SVGElement | HTMLElement; +type HTMLOrSVGElementHandle = ElementHandle; type WaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { waitFor: 'visible'|'attached'; } -type HTMLOrSVGElement = SVGElement | HTMLElement; -type HTMLOrSVGElementHandle = ElementHandle; - export interface Page { - evaluate(pageFunction: PageFunction, ...args: Boxed): Promise; - evaluateHandle(pageFunction: PageFunction, ...args: Boxed): Promise>; + evaluate(pageFunction: PageFunction, arg: Arg): Promise; + evaluate(pageFunction: PageFunction, arg?: any): Promise; + + evaluateHandle(pageFunction: PageFunction, arg: Arg): Promise>; + evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; $(selector: string): Promise; @@ -46,11 +54,18 @@ export interface Page { $$(selector: K): Promise[]>; $$(selector: string): Promise; - $eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + + $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; + waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: WaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: string, options?: WaitForSelectorOptionsNotHidden): Promise; @@ -59,8 +74,11 @@ export interface Page { } export interface Frame { - evaluate(pageFunction: PageFunction, ...args: Boxed): Promise; - evaluateHandle(pageFunction: PageFunction, ...args: Boxed): Promise>; + evaluate(pageFunction: PageFunction, arg: Arg): Promise; + evaluate(pageFunction: PageFunction, arg?: any): Promise; + + evaluateHandle(pageFunction: PageFunction, arg: Arg): Promise>; + evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; $(selector: string): Promise; @@ -68,11 +86,18 @@ export interface Frame { $$(selector: K): Promise[]>; $$(selector: string): Promise; - $eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + + $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; + waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: WaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: string, options?: WaitForSelectorOptionsNotHidden): Promise; @@ -81,14 +106,21 @@ export interface Frame { } export interface Worker { - evaluate(pageFunction: PageFunction, ...args: Boxed): Promise; - evaluateHandle(pageFunction: PageFunction, ...args: Boxed): Promise>; + evaluate(pageFunction: PageFunction, arg: Arg): Promise; + evaluate(pageFunction: PageFunction, arg?: any): Promise; + + evaluateHandle(pageFunction: PageFunction, arg: Arg): Promise>; + evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; } export interface JSHandle { + evaluate(pageFunction: PageFunctionOn, arg: Arg): Promise; + evaluate(pageFunction: PageFunctionOn, arg?: any): Promise; + + evaluateHandle(pageFunction: PageFunctionOn, arg: Arg): Promise>; + evaluateHandle(pageFunction: PageFunctionOn, arg?: any): Promise>; + jsonValue(): Promise; - evaluate(pageFunction: PageFunctionOn, ...args: Boxed): Promise; - evaluateHandle(pageFunction: PageFunctionOn, ...args: Boxed): Promise>; asElement(): T extends Node ? ElementHandle : null; } @@ -99,11 +131,15 @@ export interface ElementHandle extends JSHandle { $$(selector: K): Promise[]>; $$(selector: string): Promise; - $eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: K, pageFunction: PageFunctionOn, ...args: Boxed): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, ...args: Boxed): Promise; + $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; } export interface BrowserType { diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index 54555d4184c4d..90804708687dd 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -400,19 +400,19 @@ playwright.chromium.launch().then(async browser => { }, 5); const frame = page.mainFrame(); - await frame.$eval('span', (element, x, y) => { + await frame.$eval('span', (element, [x, y]) => { const spanAssertion: AssertType = true; const numberAssertion: AssertType = true; const stringAssertion: AssertType = true; - }, 5, 'asdf'); + }, [5, 'asdf']); await frame.$eval('my-custom-element', element => { const elementAssertion: AssertType = true; }); - await frame.$$eval('my-custom-element', (elements, x, y) => { + await frame.$$eval('my-custom-element', (elements, {x, y}) => { const elementAssertion: AssertType<(HTMLElement|SVGElement)[], typeof elements> = true; const numberAssertion: AssertType = true; const stringAssertion: AssertType = true; - }, 5, await page.evaluateHandle(() => 'asdf')); + }, { x: 5, y: await page.evaluateHandle(() => 'asdf') }); await frame.$$eval('input', (elements, x) => { const elementAssertion: AssertType = true; const numberAssertion: AssertType = true; @@ -420,19 +420,19 @@ playwright.chromium.launch().then(async browser => { const something = Math.random() > .5 ? 'visible' : 'attached'; const handle = await page.waitForSelector('a', {waitFor: something}); - await handle.$eval('span', (element, x, y) => { + await handle.$eval('span', (element, { x, y }) => { const spanAssertion: AssertType = true; const numberAssertion: AssertType = true; const stringAssertion: AssertType = true; - }, 5, 'asdf'); + }, { x: 5, y: 'asdf' }); await handle.$eval('my-custom-element', element => { const elementAssertion: AssertType = true; }); - await handle.$$eval('my-custom-element', (elements, x, y) => { + await handle.$$eval('my-custom-element', (elements, [x, y]) => { const elementAssertion: AssertType<(HTMLElement|SVGElement)[], typeof elements> = true; const numberAssertion: AssertType = true; const stringAssertion: AssertType = true; - }, 5, await page.evaluateHandle(() => 'asdf')); + }, [5, await page.evaluateHandle(() => 'asdf')]); await handle.$$eval('input', (elements, x) => { const elementAssertion: AssertType = true; const numberAssertion: AssertType = true;