diff --git a/new-docs/puppeteer.evaluatefn.md b/new-docs/puppeteer.evaluatefn.md index 8647ef2b7d374..7590447388097 100644 --- a/new-docs/puppeteer.evaluatefn.md +++ b/new-docs/puppeteer.evaluatefn.md @@ -8,5 +8,5 @@ Signature: ```typescript -export declare type EvaluateFn = string | ((arg1: T, ...args: any[]) => any); +export declare type EvaluateFn = string | ((arg1: T, ...args: unknown[]) => unknown); ``` diff --git a/new-docs/puppeteer.evaluatefnreturntype.md b/new-docs/puppeteer.evaluatefnreturntype.md index 471823e316ee9..67bad36a77a01 100644 --- a/new-docs/puppeteer.evaluatefnreturntype.md +++ b/new-docs/puppeteer.evaluatefnreturntype.md @@ -8,5 +8,5 @@ Signature: ```typescript -export declare type EvaluateFnReturnType = T extends (...args: any[]) => infer R ? R : unknown; +export declare type EvaluateFnReturnType = T extends (...args: unknown[]) => infer R ? R : unknown; ``` diff --git a/new-docs/puppeteer.frame.evaluate.md b/new-docs/puppeteer.frame.evaluate.md index bd06acd2eaf75..53e102eabf0a1 100644 --- a/new-docs/puppeteer.frame.evaluate.md +++ b/new-docs/puppeteer.frame.evaluate.md @@ -7,19 +7,19 @@ Signature: ```typescript -evaluate(pageFunction: Function | string, ...args: unknown[]): Promise; +evaluate(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise>>; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| pageFunction | Function \| string | a function that is run within the frame | -| args | unknown\[\] | arguments to be passed to the pageFunction | +| pageFunction | T | a function that is run within the frame | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | arguments to be passed to the pageFunction | Returns: -Promise<ReturnType> +Promise<[UnwrapPromiseLike](./puppeteer.unwrappromiselike.md)<[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<T>>> ## Remarks diff --git a/new-docs/puppeteer.jshandle.evaluate.md b/new-docs/puppeteer.jshandle.evaluate.md index c8cd3227e228c..d30e34dc3145b 100644 --- a/new-docs/puppeteer.jshandle.evaluate.md +++ b/new-docs/puppeteer.jshandle.evaluate.md @@ -9,7 +9,7 @@ This method passes this handle as the first argument to `pageFunction`. Signature: ```typescript -evaluate(pageFunction: T | string, ...args: SerializableOrJSHandle[]): Promise>; +evaluate(pageFunction: T | string, ...args: SerializableOrJSHandle[]): Promise>>; ``` ## Parameters @@ -21,7 +21,7 @@ evaluate(pageFunction: T | string, ...args: SerializableOr Returns: -Promise<[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<T>> +Promise<[UnwrapPromiseLike](./puppeteer.unwrappromiselike.md)<[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<T>>> ## Example diff --git a/new-docs/puppeteer.md b/new-docs/puppeteer.md index ad2d130b346ee..f8c728372ee88 100644 --- a/new-docs/puppeteer.md +++ b/new-docs/puppeteer.md @@ -109,5 +109,6 @@ | [Serializable](./puppeteer.serializable.md) | | | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md) | | | [UnwrapElementHandle](./puppeteer.unwrapelementhandle.md) | Unwraps a DOM element out of an ElementHandle instance | +| [UnwrapPromiseLike](./puppeteer.unwrappromiselike.md) | | | [WrapElementHandle](./puppeteer.wrapelementhandle.md) | Wraps a DOM element into an ElementHandle instance | diff --git a/new-docs/puppeteer.page.evaluate.md b/new-docs/puppeteer.page.evaluate.md index 7f87a9e59625c..3308520e936ac 100644 --- a/new-docs/puppeteer.page.evaluate.md +++ b/new-docs/puppeteer.page.evaluate.md @@ -7,19 +7,19 @@ Signature: ```typescript -evaluate(pageFunction: Function | string, ...args: unknown[]): Promise; +evaluate(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise>>; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| pageFunction | Function \| string | a function that is run within the page | -| args | unknown\[\] | arguments to be passed to the pageFunction | +| pageFunction | T | a function that is run within the page | +| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | arguments to be passed to the pageFunction | Returns: -Promise<ReturnType> +Promise<[UnwrapPromiseLike](./puppeteer.unwrappromiselike.md)<[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<T>>> the return value of `pageFunction`. @@ -47,6 +47,12 @@ You can pass a string instead of a function (although functions are recommended ``` const aHandle = await page.evaluate('1 + 2'); +``` +To get the best TypeScript experience, you should pass in as the generic the type of `pageFunction`: + +``` +const aHandle = await page.evaluate<() => number>(() => 2); + ``` ## Example 3 diff --git a/new-docs/puppeteer.serializable.md b/new-docs/puppeteer.serializable.md index 8b7a7df8bf15b..b8abdb01038b6 100644 --- a/new-docs/puppeteer.serializable.md +++ b/new-docs/puppeteer.serializable.md @@ -8,5 +8,5 @@ Signature: ```typescript -export declare type Serializable = number | string | boolean | null | JSONArray | JSONObject; +export declare type Serializable = number | string | boolean | null | BigInt | JSONArray | JSONObject; ``` diff --git a/new-docs/puppeteer.unwrappromiselike.md b/new-docs/puppeteer.unwrappromiselike.md new file mode 100644 index 0000000000000..c1b8e6459b46d --- /dev/null +++ b/new-docs/puppeteer.unwrappromiselike.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [UnwrapPromiseLike](./puppeteer.unwrappromiselike.md) + +## UnwrapPromiseLike type + +Signature: + +```typescript +export declare type UnwrapPromiseLike = T extends PromiseLike ? U : T; +``` diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index 933ceef6db5a8..28f37b9c65847 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -28,6 +28,9 @@ import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, + EvaluateFn, + EvaluateFnReturnType, + UnwrapPromiseLike, } from './EvalTypes'; import { isNode } from '../environment'; @@ -118,12 +121,15 @@ export class DOMWorld { return context.evaluateHandle(pageFunction, ...args); } - async evaluate( - pageFunction: Function | string, - ...args: unknown[] - ): Promise { + async evaluate( + pageFunction: T, + ...args: SerializableOrJSHandle[] + ): Promise>> { const context = await this.executionContext(); - return context.evaluate(pageFunction, ...args); + return context.evaluate>>( + pageFunction, + ...args + ); } async $(selector: string): Promise { @@ -206,7 +212,7 @@ export class DOMWorld { } = options; // We rely upon the fact that document.open() will reset frame lifecycle with "init" // lifecycle event. @see https://crrev.com/608658 - await this.evaluate((html) => { + await this.evaluate<(x: string) => void>((html) => { document.open(); document.write(html); document.close(); diff --git a/src/common/EvalTypes.ts b/src/common/EvalTypes.ts index 11f7d35ece82a..95b4039be4cc9 100644 --- a/src/common/EvalTypes.ts +++ b/src/common/EvalTypes.ts @@ -19,12 +19,17 @@ import { JSHandle, ElementHandle } from './JSHandle'; /** * @public */ -export type EvaluateFn = string | ((arg1: T, ...args: any[]) => any); +export type EvaluateFn = + | string + | ((arg1: T, ...args: unknown[]) => unknown); + +export type UnwrapPromiseLike = T extends PromiseLike ? U : T; + /** * @public */ export type EvaluateFnReturnType = T extends ( - ...args: any[] + ...args: unknown[] ) => infer R ? R : unknown; @@ -42,6 +47,7 @@ export type Serializable = | string | boolean | null + | BigInt | JSONArray | JSONObject; diff --git a/src/common/FrameManager.ts b/src/common/FrameManager.ts index 29a86fdd8cc26..366f7bedd90ce 100644 --- a/src/common/FrameManager.ts +++ b/src/common/FrameManager.ts @@ -32,6 +32,9 @@ import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, + EvaluateFn, + EvaluateFnReturnType, + UnwrapPromiseLike, } from './EvalTypes'; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; @@ -684,11 +687,11 @@ export class Frame { * @param pageFunction - a function that is run within the frame * @param args - arguments to be passed to the pageFunction */ - async evaluate( - pageFunction: Function | string, - ...args: unknown[] - ): Promise { - return this._mainWorld.evaluate(pageFunction, ...args); + async evaluate( + pageFunction: T, + ...args: SerializableOrJSHandle[] + ): Promise>> { + return this._mainWorld.evaluate(pageFunction, ...args); } /** diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts index f5a0c18b2837a..ffb0c6b2c7e3b 100644 --- a/src/common/JSHandle.ts +++ b/src/common/JSHandle.ts @@ -29,6 +29,7 @@ import { EvaluateFnReturnType, EvaluateHandleFn, WrapElementHandle, + UnwrapPromiseLike, } from './EvalTypes'; export interface BoxModel { @@ -153,12 +154,10 @@ export class JSHandle { async evaluate( pageFunction: T | string, ...args: SerializableOrJSHandle[] - ): Promise> { - return await this.executionContext().evaluate>( - pageFunction, - this, - ...args - ); + ): Promise>> { + return await this.executionContext().evaluate< + UnwrapPromiseLike> + >(pageFunction, this, ...args); } /** @@ -620,7 +619,9 @@ export class ElementHandle< * Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element. */ async focus(): Promise { - await this.evaluate((element) => element.focus()); + await this.evaluate<(element: HTMLElement) => void>((element) => + element.focus() + ); } /** diff --git a/src/common/Page.ts b/src/common/Page.ts index 257784a09bfcc..a58f9fbfd4e03 100644 --- a/src/common/Page.ts +++ b/src/common/Page.ts @@ -45,6 +45,9 @@ import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, + EvaluateFn, + EvaluateFnReturnType, + UnwrapPromiseLike, } from './EvalTypes'; const writeFileAsync = promisify(fs.writeFile); @@ -1516,6 +1519,13 @@ export class Page extends EventEmitter { * const aHandle = await page.evaluate('1 + 2'); * ``` * + * To get the best TypeScript experience, you should pass in as the + * generic the type of `pageFunction`: + * + * ``` + * const aHandle = await page.evaluate<() => number>(() => 2); + * ``` + * * @example * * {@link ElementHandle} instances (including {@link JSHandle}s) can be passed @@ -1532,13 +1542,11 @@ export class Page extends EventEmitter { * * @returns the return value of `pageFunction`. */ - async evaluate( - pageFunction: Function | string, - ...args: unknown[] - ): Promise { - return this._frameManager - .mainFrame() - .evaluate(pageFunction, ...args); + async evaluate( + pageFunction: T, + ...args: SerializableOrJSHandle[] + ): Promise>> { + return this._frameManager.mainFrame().evaluate(pageFunction, ...args); } async evaluateOnNewDocument( diff --git a/test/browsercontext.spec.ts b/test/browsercontext.spec.ts index b8f4cfcf0be44..98502654c657a 100644 --- a/test/browsercontext.spec.ts +++ b/test/browsercontext.spec.ts @@ -66,7 +66,10 @@ describe('BrowserContext', function () { await page.goto(server.EMPTY_PAGE); const [popupTarget] = await Promise.all([ utils.waitEvent(browser, 'targetcreated'), - page.evaluate((url) => window.open(url), server.EMPTY_PAGE), + page.evaluate<(url: string) => void>( + (url) => window.open(url), + server.EMPTY_PAGE + ), ]); expect(popupTarget.browserContext()).toBe(context); await context.close(); diff --git a/test/cookies.spec.ts b/test/cookies.spec.ts index 63092f2aa3991..e2c41f2b94722 100644 --- a/test/cookies.spec.ts +++ b/test/cookies.spec.ts @@ -389,9 +389,9 @@ describe('Cookie specs', () => { await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({ name: 'localhost-cookie', value: 'best' }); - await page.evaluate((src) => { + await page.evaluate<(src: string) => Promise>((src) => { let fulfill; - const promise = new Promise((x) => (fulfill = x)); + const promise = new Promise((x) => (fulfill = x)); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = fulfill; @@ -454,9 +454,9 @@ describe('Cookie specs', () => { try { await page.goto(httpsServer.PREFIX + '/grid.html'); - await page.evaluate((src) => { + await page.evaluate<(src: string) => Promise>((src) => { let fulfill; - const promise = new Promise((x) => (fulfill = x)); + const promise = new Promise((x) => (fulfill = x)); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = fulfill; diff --git a/test/coverage.spec.ts b/test/coverage.spec.ts index a75a6a8770cf6..bc92938d93901 100644 --- a/test/coverage.spec.ts +++ b/test/coverage.spec.ts @@ -264,7 +264,7 @@ describe('Coverage specs', function () { const { page, server } = getTestState(); await page.coverage.startCSSCoverage(); - await page.evaluate(async (url) => { + await page.evaluate<(url: string) => Promise>(async (url) => { document.body.textContent = 'hello, world'; const link = document.createElement('link'); diff --git a/test/elementhandle.spec.ts b/test/elementhandle.spec.ts index fe6252b403902..bfcef439f9cec 100644 --- a/test/elementhandle.spec.ts +++ b/test/elementhandle.spec.ts @@ -67,7 +67,7 @@ describe('ElementHandle specs', function () { '
hello
' ); const elementHandle = await page.$('div'); - await page.evaluate( + await page.evaluate<(element: HTMLElement) => void>( (element) => (element.style.height = '200px'), elementHandle ); @@ -84,7 +84,7 @@ describe('ElementHandle specs', function () { `); const element = await page.$('#therect'); const pptrBoundingBox = await element.boundingBox(); - const webBoundingBox = await page.evaluate((e) => { + const webBoundingBox = await page.evaluate((e: HTMLElement) => { const rect = e.getBoundingClientRect(); return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; }, element); @@ -211,7 +211,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); - await page.evaluate((button) => button.remove(), button); + await page.evaluate((button: HTMLElement) => button.remove(), button); let error = null; await button.click().catch((error_) => (error = error_)); expect(error.message).toBe('Node is detached from document'); @@ -221,7 +221,10 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); - await page.evaluate((button) => (button.style.display = 'none'), button); + await page.evaluate( + (button: HTMLElement) => (button.style.display = 'none'), + button + ); const error = await button.click().catch((error_) => error_); expect(error.message).toBe( 'Node is either not visible or not an HTMLElement' @@ -233,7 +236,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate( - (button) => (button.parentElement.style.display = 'none'), + (button: HTMLElement) => (button.parentElement.style.display = 'none'), button ); const error = await button.click().catch((error_) => error_); @@ -295,7 +298,12 @@ describe('ElementHandle specs', function () { (element, selector) => document.querySelector(`[id="${selector}"]`) ); const element = await page.$('getById/foo'); - expect(await page.evaluate((element) => element.id, element)).toBe('foo'); + expect( + await page.evaluate<(element: HTMLElement) => string>( + (element) => element.id, + element + ) + ).toBe('foo'); // Unregister. puppeteer.__experimental_unregisterCustomQueryHandler('getById'); @@ -340,7 +348,10 @@ describe('ElementHandle specs', function () { const classNames = await Promise.all( elements.map( async (element) => - await page.evaluate((element) => element.className, element) + await page.evaluate<(element: HTMLElement) => string>( + (element) => element.className, + element + ) ) ); diff --git a/test/emulation.spec.ts b/test/emulation.spec.ts index cc47ab719b088..a54dbafe28262 100644 --- a/test/emulation.spec.ts +++ b/test/emulation.spec.ts @@ -138,7 +138,7 @@ describe('Emulation', () => { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate( - (button) => (button.style.marginTop = '200px'), + (button: HTMLElement) => (button.style.marginTop = '200px'), button ); await button.click(); diff --git a/test/evaluation.spec.ts b/test/evaluation.spec.ts index a6e1cec95aa1c..c16cf6547cc19 100644 --- a/test/evaluation.spec.ts +++ b/test/evaluation.spec.ts @@ -40,7 +40,7 @@ describe('Evaluation specs', function () { (bigint ? it : xit)('should transfer BigInt', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, BigInt(42)); + const result = await page.evaluate((a: BigInt) => a, BigInt(42)); expect(result).toBe(BigInt(42)); }); it('should transfer NaN', async () => { @@ -155,7 +155,11 @@ describe('Evaluation specs', function () { // 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: number, b: number) => number>( + (a, b) => a * b, + a, + b + ); }); const result = await page.evaluate(async function () { return await globalThis.callController(9, 3); @@ -277,7 +281,7 @@ describe('Evaluation specs', function () { .jsonValue() .catch((error_) => error_.message); const error = await page - .evaluate((errorText) => { + .evaluate<(errorText: string) => Error>((errorText) => { throw new Error(errorText); }, errorText) .catch((error_) => error_); @@ -306,7 +310,10 @@ describe('Evaluation specs', function () { await page.setContent('
42
'); const element = await page.$('section'); - const text = await page.evaluate((e) => e.textContent, element); + const text = await page.evaluate<(e: HTMLElement) => string>( + (e) => e.textContent, + element + ); expect(text).toBe('42'); }); it('should throw if underlying element was disposed', async () => { @@ -318,7 +325,7 @@ describe('Evaluation specs', function () { await element.dispose(); let error = null; await page - .evaluate((e) => e.textContent, element) + .evaluate((e: HTMLElement) => e.textContent, element) .catch((error_) => (error = error_)); expect(error.message).toContain('JSHandle is disposed'); }); @@ -331,7 +338,7 @@ describe('Evaluation specs', function () { const bodyHandle = await page.frames()[1].$('body'); let error = null; await page - .evaluate((body) => body.innerHTML, bodyHandle) + .evaluate((body: HTMLElement) => body.innerHTML, bodyHandle) .catch((error_) => (error = error_)); expect(error).toBeTruthy(); expect(error.message).toContain( @@ -379,7 +386,7 @@ describe('Evaluation specs', function () { it('should transfer 100Mb of data from page to node.js', async function () { const { page } = getTestState(); - const a = await page.evaluate(() => + const a = await page.evaluate<() => string>(() => Array(100 * 1024 * 1024 + 1).join('a') ); expect(a.length).toBe(100 * 1024 * 1024); diff --git a/test/frame.spec.ts b/test/frame.spec.ts index 0e51db85be132..8251f1d068db5 100644 --- a/test/frame.spec.ts +++ b/test/frame.spec.ts @@ -200,7 +200,7 @@ describe('Frame specs', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/shadow.html'); - await page.evaluate(async (url) => { + await page.evaluate(async (url: string) => { const frame = document.createElement('iframe'); frame.src = url; document.body.shadowRoot.appendChild(frame); @@ -213,7 +213,7 @@ describe('Frame specs', function () { const { page, server } = getTestState(); await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE); - await page.evaluate((url) => { + await page.evaluate((url: string) => { const frame = document.createElement('iframe'); frame.name = 'theFrameName'; frame.src = url; diff --git a/test/input.spec.ts b/test/input.spec.ts index b5fa2d04e5ccb..cd2c10339ca7b 100644 --- a/test/input.spec.ts +++ b/test/input.spec.ts @@ -36,7 +36,7 @@ describe('input tests', function () { await page.goto(server.PREFIX + '/input/fileupload.html'); const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD); const input = await page.$('input'); - await page.evaluate((e) => { + await page.evaluate((e: HTMLElement) => { globalThis._inputEvents = []; e.addEventListener('change', (ev) => globalThis._inputEvents.push(ev.type) @@ -46,18 +46,18 @@ describe('input tests', function () { ); }, input); await input.uploadFile(filePath); - expect(await page.evaluate((e) => e.files[0].name, input)).toBe( - 'file-to-upload.txt' - ); - expect(await page.evaluate((e) => e.files[0].type, input)).toBe( - 'text/plain' - ); + expect( + await page.evaluate((e: HTMLInputElement) => e.files[0].name, input) + ).toBe('file-to-upload.txt'); + expect( + await page.evaluate((e: HTMLInputElement) => e.files[0].type, input) + ).toBe('text/plain'); expect(await page.evaluate(() => globalThis._inputEvents)).toEqual([ 'input', 'change', ]); expect( - await page.evaluate((e) => { + await page.evaluate((e: HTMLInputElement) => { const reader = new FileReader(); const promise = new Promise((fulfill) => (reader.onload = fulfill)); reader.readAsText(e.files[0]); diff --git a/test/jshandle.spec.ts b/test/jshandle.spec.ts index 276d88b9f5830..cdcb7efb24ecd 100644 --- a/test/jshandle.spec.ts +++ b/test/jshandle.spec.ts @@ -37,7 +37,10 @@ describe('JSHandle', function () { const { page } = getTestState(); const navigatorHandle = await page.evaluateHandle(() => navigator); - const text = await page.evaluate((e) => e.userAgent, navigatorHandle); + const text = await page.evaluate( + (e: Navigator) => e.userAgent, + navigatorHandle + ); expect(text).toContain('Mozilla'); }); it('should accept object handle to primitive types', async () => { @@ -75,7 +78,9 @@ describe('JSHandle', function () { globalThis.FOO = 123; return window; }); - expect(await page.evaluate((e) => e.FOO, aHandle)).toBe(123); + expect(await page.evaluate((e: { FOO: number }) => e.FOO, aHandle)).toBe( + 123 + ); }); it('should work with primitives', async () => { const { page } = getTestState(); @@ -84,7 +89,9 @@ describe('JSHandle', function () { globalThis.FOO = 123; return window; }); - expect(await page.evaluate((e) => e.FOO, aHandle)).toBe(123); + expect(await page.evaluate((e: { FOO: number }) => e.FOO, aHandle)).toBe( + 123 + ); }); }); @@ -193,7 +200,10 @@ describe('JSHandle', function () { const element = aHandle.asElement(); expect(element).toBeTruthy(); expect( - await page.evaluate((e) => e.nodeType === Node.TEXT_NODE, element) + await page.evaluate( + (e: HTMLElement) => e.nodeType === Node.TEXT_NODE, + element + ) ); }); }); diff --git a/test/keyboard.spec.ts b/test/keyboard.spec.ts index c90876adeb08b..cd13574db54af 100644 --- a/test/keyboard.spec.ts +++ b/test/keyboard.spec.ts @@ -387,7 +387,15 @@ describe('Keyboard', function () { }); }); await page.keyboard.press('Meta'); - const [key, code, metaKey] = await page.evaluate('result'); + // Have to do this because we lose a lot of type info when evaluating a + // string not a function. This is why functions are recommended rather than + // using strings (although we'll leave this test so we have coverage of both + // approaches.) + const [key, code, metaKey] = (await page.evaluate('result')) as [ + string, + string, + boolean + ]; if (isFirefox && os.platform() !== 'darwin') expect(key).toBe('OS'); else expect(key).toBe('Meta'); diff --git a/test/mouse.spec.ts b/test/mouse.spec.ts index 97aae6b90112c..cb114607b1fe2 100644 --- a/test/mouse.spec.ts +++ b/test/mouse.spec.ts @@ -61,7 +61,7 @@ describe('Mouse', function () { }); }); await page.mouse.click(50, 60); - const event = await page.evaluate( + const event = await page.evaluate<() => MouseEvent>( () => globalThis.clickPromise ); expect(event.type).toBe('click'); @@ -75,13 +75,15 @@ describe('Mouse', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); - const { x, y, width, height } = await page.evaluate(dimensions); + const { x, y, width, height } = await page.evaluate<() => Dimensions>( + dimensions + ); const mouse = page.mouse; await mouse.move(x + width - 4, y + height - 4); await mouse.down(); await mouse.move(x + width + 100, y + height + 100); await mouse.up(); - const newDimensions = await page.evaluate(dimensions); + const newDimensions = await page.evaluate<() => Dimensions>(dimensions); expect(newDimensions.width).toBe(Math.round(width + 104)); expect(newDimensions.height).toBe(Math.round(height + 104)); }); @@ -163,13 +165,15 @@ describe('Mouse', function () { for (const [modifier, key] of modifiers) { await page.keyboard.down(modifier); await page.click('#button-3'); - if (!(await page.evaluate((mod) => globalThis.lastEvent[mod], key))) + if ( + !(await page.evaluate((mod: string) => globalThis.lastEvent[mod], key)) + ) throw new Error(key + ' should be true'); await page.keyboard.up(modifier); } await page.click('#button-3'); for (const [modifier, key] of modifiers) { - if (await page.evaluate((mod) => globalThis.lastEvent[mod], key)) + if (await page.evaluate((mod: string) => globalThis.lastEvent[mod], key)) throw new Error(modifiers[modifier] + ' should be false'); } }); diff --git a/test/navigation.spec.ts b/test/navigation.spec.ts index 31c230245b6d1..c654b4af1fdc3 100644 --- a/test/navigation.spec.ts +++ b/test/navigation.spec.ts @@ -495,7 +495,7 @@ describe('navigation', function () { const [response] = await Promise.all([ page.waitForNavigation(), page.evaluate( - (url) => (window.location.href = url), + (url: string) => (window.location.href = url), server.PREFIX + '/grid.html' ), ]); @@ -733,7 +733,7 @@ describe('navigation', function () { const [response] = await Promise.all([ frame.waitForNavigation(), frame.evaluate( - (url) => (window.location.href = url), + (url: string) => (window.location.href = url), server.PREFIX + '/grid.html' ), ]); diff --git a/test/page.spec.ts b/test/page.spec.ts index a0e6661e5ddd4..9674948121c42 100644 --- a/test/page.spec.ts +++ b/test/page.spec.ts @@ -27,6 +27,7 @@ import { describeFailsFirefox, } from './mocha-utils'; import { Page, Metrics } from '../src/common/Page'; +import { JSHandle } from '../src/common/JSHandle'; describe('Page', function () { setupTestBrowserHooks(); @@ -397,7 +398,7 @@ describe('Page', function () { const prototypeHandle = await page.evaluateHandle(() => Set.prototype); const objectsHandle = await page.queryObjects(prototypeHandle); const count = await page.evaluate( - (objects) => objects.length, + (objects: JSHandle[]) => objects.length, objectsHandle ); expect(count).toBe(1); @@ -416,7 +417,7 @@ describe('Page', function () { const prototypeHandle = await page.evaluateHandle(() => Set.prototype); const objectsHandle = await page.queryObjects(prototypeHandle); const count = await page.evaluate( - (objects) => objects.length, + (objects: JSHandle[]) => objects.length, objectsHandle ); expect(count).toBe(1); @@ -515,7 +516,7 @@ describe('Page', function () { const [message] = await Promise.all([ waitEvent(page, 'console'), page.evaluate( - async (url) => fetch(url).catch(() => {}), + async (url: string) => fetch(url).catch(() => {}), server.EMPTY_PAGE ), ]); @@ -887,8 +888,8 @@ describe('Page', function () { await page.exposeFunction('complexObject', function (a, b) { return { x: a.x + b.x }; }); - const result = await page.evaluate<{ x: number }>(async () => - globalThis.complexObject({ x: 5 }, { x: 2 }) + const result = await page.evaluate<() => Promise<{ x: number }>>( + async () => globalThis.complexObject({ x: 5 }, { x: 2 }) ); expect(result.x).toBe(7); }); @@ -1334,7 +1335,7 @@ describe('Page', function () { }); const styleHandle = await page.$('style'); const styleContent = await page.evaluate( - (style) => style.innerHTML, + (style: HTMLStyleElement) => style.innerHTML, styleHandle ); expect(styleContent).toContain(path.join('assets', 'injectedstyle.css')); diff --git a/test/queryselector.spec.ts b/test/queryselector.spec.ts index df484e0b55417..3beb03de3711b 100644 --- a/test/queryselector.spec.ts +++ b/test/queryselector.spec.ts @@ -103,7 +103,7 @@ describe('querySelector', function () { const elements = await page.$$('div'); expect(elements.length).toBe(2); const promises = elements.map((element) => - page.evaluate((e) => e.textContent, element) + page.evaluate((e: HTMLElement) => e.textContent, element) ); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); @@ -151,7 +151,10 @@ describe('querySelector', function () { const html = await page.$('html'); const second = await html.$('.second'); const inner = await second.$('.inner'); - const content = await page.evaluate((e) => e.textContent, inner); + const content = await page.evaluate( + (e: HTMLElement) => e.textContent, + inner + ); expect(content).toBe('A'); }); @@ -263,7 +266,7 @@ describe('querySelector', function () { const elements = await html.$$('div'); expect(elements.length).toBe(2); const promises = elements.map((element) => - page.evaluate((e) => e.textContent, element) + page.evaluate((e: HTMLElement) => e.textContent, element) ); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); @@ -291,7 +294,10 @@ describe('querySelector', function () { const html = await page.$('html'); const second = await html.$x(`./body/div[contains(@class, 'second')]`); const inner = await second[0].$x(`./div[contains(@class, 'inner')]`); - const content = await page.evaluate((e) => e.textContent, inner[0]); + const content = await page.evaluate( + (e: HTMLElement) => e.textContent, + inner[0] + ); expect(content).toBe('A'); }); diff --git a/test/requestinterception.spec.ts b/test/requestinterception.spec.ts index 9d4d60fb32756..024824aa83195 100644 --- a/test/requestinterception.spec.ts +++ b/test/requestinterception.spec.ts @@ -384,7 +384,7 @@ describe('request interception', function () { }); const dataURL = 'data:text/html,
yo
'; const text = await page.evaluate( - (url) => fetch(url).then((r) => r.text()), + (url: string) => fetch(url).then((r) => r.text()), dataURL ); expect(text).toBe('
yo
'); diff --git a/test/screenshot.spec.ts b/test/screenshot.spec.ts index c2cf043144b29..3687e1858052f 100644 --- a/test/screenshot.spec.ts +++ b/test/screenshot.spec.ts @@ -281,7 +281,10 @@ describe('Screenshots', function () { await page.setContent('

remove this

'); const elementHandle = await page.$('h1'); - await page.evaluate((element) => element.remove(), elementHandle); + await page.evaluate( + (element: HTMLElement) => element.remove(), + elementHandle + ); const screenshotError = await elementHandle .screenshot() .catch((error) => error); diff --git a/test/target.spec.ts b/test/target.spec.ts index 54e0fa2854b76..bc6deb006b201 100644 --- a/test/target.spec.ts +++ b/test/target.spec.ts @@ -81,7 +81,7 @@ describe('Target', function () { ) .then((target) => target.page()), page.evaluate( - (url) => window.open(url), + (url: string) => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html' ), ]); @@ -215,7 +215,7 @@ describe('Target', function () { // Open a new page. Use window.open to connect to the page later. await Promise.all([ page.evaluate( - (url) => window.open(url), + (url: string) => window.open(url), server.PREFIX + '/one-style.html' ), server.waitForRequest('/one-style.css'), diff --git a/test/waittask.spec.ts b/test/waittask.spec.ts index 9c216c1068552..d31e9523344c9 100644 --- a/test/waittask.spec.ts +++ b/test/waittask.spec.ts @@ -264,7 +264,7 @@ describe('waittask specs', function () { .waitForFunction((element) => !element.parentElement, {}, div) .then(() => (resolved = true)); expect(resolved).toBe(false); - await page.evaluate((element) => element.remove(), div); + await page.evaluate((element: HTMLElement) => element.remove(), div); await waitForFunction; }); it('should respect timeout', async () => { @@ -351,9 +351,9 @@ describe('waittask specs', function () { page.waitForSelector('.zombo'), page.setContent(`
anything
`), ]); - expect(await page.evaluate((x) => x.textContent, handle)).toBe( - 'anything' - ); + expect( + await page.evaluate((x: HTMLElement) => x.textContent, handle) + ).toBe('anything'); }); it('should resolve promise when node is added', async () => { @@ -588,7 +588,10 @@ describe('waittask specs', function () { const waitForSelector = page.waitForSelector('.zombo'); await page.setContent(`
anything
`); expect( - await page.evaluate((x) => x.textContent, await waitForSelector) + await page.evaluate( + (x: HTMLElement) => x.textContent, + await waitForSelector + ) ).toBe('anything'); }); it('should have correct stack trace for timeout', async () => { @@ -616,7 +619,10 @@ describe('waittask specs', function () { '//p[normalize-space(.)="hello world"]' ); expect( - await page.evaluate((x) => x.textContent, await waitForXPath) + await page.evaluate( + (x: HTMLElement) => x.textContent, + await waitForXPath + ) ).toBe('hello world '); }); it('should respect timeout', async () => { @@ -683,7 +689,10 @@ describe('waittask specs', function () { const waitForXPath = page.waitForXPath('//*[@class="zombo"]'); await page.setContent(`
anything
`); expect( - await page.evaluate((x) => x.textContent, await waitForXPath) + await page.evaluate( + (x: HTMLElement) => x.textContent, + await waitForXPath + ) ).toBe('anything'); }); it('should allow you to select a text node', async () => { @@ -701,7 +710,10 @@ describe('waittask specs', function () { await page.setContent(`
some text
`); const waitForXPath = page.waitForXPath('/html/body/div'); expect( - await page.evaluate((x) => x.textContent, await waitForXPath) + await page.evaluate( + (x: HTMLElement) => x.textContent, + await waitForXPath + ) ).toBe('some text'); }); }); diff --git a/test/worker.spec.ts b/test/worker.spec.ts index 86aba8ca28950..1f070df6510da 100644 --- a/test/worker.spec.ts +++ b/test/worker.spec.ts @@ -60,7 +60,10 @@ describeFailsFirefox('Workers', function () { const workerDestroyedPromise = new Promise((x) => page.once('workerdestroyed', x) ); - await page.evaluate((workerObj) => workerObj.terminate(), workerObj); + await page.evaluate( + (workerObj: Worker) => workerObj.terminate(), + workerObj + ); expect(await workerDestroyedPromise).toBe(worker); const error = await workerThisObj .getProperty('self')