From 570087ea944481ebdf18f7c5b2675ff66a4b4d46 Mon Sep 17 00:00:00 2001 From: jrandolf <101637635+jrandolf@users.noreply.github.com> Date: Wed, 15 Jun 2022 12:09:22 +0200 Subject: [PATCH] chore: use strict typing in tests (#8524) * The testing tsconfig.json inherits from the base TS config. * A lot of type assertions have been inserted...a lot. * All testing utilities have migrated to TS. * text-diff is being replaced with diff for TS compatibility. * ProtocolError has been added to PuppeteerErrors and PuppeteerErrors is no longer a record (it's been frozen). * Fixes a small bug where null was an allowable media type in emulation (should be undefined). --- .gitignore | 2 +- package.json | 4 + src/common/AriaQueryHandler.ts | 4 +- src/common/DOMWorld.ts | 4 +- src/common/Debug.ts | 2 +- src/common/DeviceDescriptors.ts | 1 + src/common/Errors.ts | 13 +- src/common/Page.ts | 4 +- src/common/fetch.ts | 2 +- test/.eslintrc.js | 1 + test/fixtures/dumpio.js | 4 +- test/src/CDPSession.spec.ts | 21 +- test/src/EventEmitter.spec.ts | 6 +- test/src/NetworkManager.spec.ts | 2 +- test/src/accessibility.spec.ts | 76 +- test/src/ariaqueryhandler.spec.ts | 245 ++-- test/src/browser.spec.ts | 4 +- test/src/browsercontext.spec.ts | 96 +- test/src/chromiumonly.spec.ts | 53 +- test/src/click.spec.ts | 221 +++- test/src/cookies.spec.ts | 86 +- test/src/coverage.spec.ts | 70 +- test/src/defaultbrowsercontext.spec.ts | 10 +- test/src/dialog.spec.ts | 18 +- test/src/drag-and-drop.spec.ts | 118 +- test/src/elementhandle.spec.ts | 256 ++-- test/src/emulation.spec.ts | 336 ++++-- test/src/evaluation.spec.ts | 322 +++-- test/src/fixtures.spec.ts | 36 +- test/src/frame.spec.ts | 157 ++- test/src/{golden-utils.js => golden-utils.ts} | 160 +-- test/src/headful.spec.ts | 189 ++- test/src/idle_override.spec.ts | 4 +- test/src/ignorehttpserrors.spec.ts | 57 +- test/src/input.spec.ts | 219 ++-- test/src/jshandle.spec.ts | 175 ++- test/src/keyboard.spec.ts | 266 +++-- test/src/launcher.spec.ts | 231 ++-- test/src/mocha-utils.ts | 36 +- test/src/mouse.spec.ts | 81 +- test/src/navigation.spec.ts | 322 ++--- test/src/network.spec.ts | 430 ++++--- test/src/oopif.spec.ts | 102 +- test/src/page.spec.ts | 1047 +++++++++++------ test/src/proxy.spec.ts | 14 +- test/src/queryselector.spec.ts | 251 ++-- .../requestinterception-experimental.spec.ts | 347 +++--- test/src/requestinterception.spec.ts | 335 ++++-- test/src/screenshot.spec.ts | 59 +- test/src/target.spec.ts | 268 +++-- test/src/touchscreen.spec.ts | 23 +- test/src/tracing.spec.ts | 29 +- test/src/utils.js | 137 --- test/src/utils.ts | 162 +++ test/src/waittask.spec.ts | 578 +++++---- test/src/worker.spec.ts | 105 +- test/tsconfig.json | 14 +- utils/testserver/src/index.ts | 8 +- utils/testserver/tsconfig.json | 8 +- 59 files changed, 4877 insertions(+), 2954 deletions(-) rename test/src/{golden-utils.js => golden-utils.ts} (50%) delete mode 100644 test/src/utils.js create mode 100644 test/src/utils.ts diff --git a/.gitignore b/.gitignore index 3b1cc7d10dc..ffcf73067f0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ /.dev_profile* /.local-chromium/ /.local-firefox/ -/test/test-user-data-dir* +/test/output-*/ build/ coverage coverage/ diff --git a/package.json b/package.json index b677db13fde..d8b0b19701c 100644 --- a/package.json +++ b/package.json @@ -86,9 +86,12 @@ "@microsoft/api-documenter": "7.17.16", "@microsoft/api-extractor": "7.24.2", "@types/debug": "4.1.7", + "@types/diff": "5.0.2", "@types/mime": "2.0.3", "@types/mocha": "9.1.1", "@types/node": "17.0.38", + "@types/pixelmatch": "5.2.4", + "@types/pngjs": "6.0.1", "@types/progress": "2.0.5", "@types/proxy-from-env": "1.0.1", "@types/rimraf": "3.0.2", @@ -101,6 +104,7 @@ "c8": "7.11.3", "commonmark": "0.30.0", "cross-env": "7.0.3", + "diff": "5.1.0", "eslint": "8.16.0", "eslint-config-prettier": "8.5.0", "eslint-formatter-codeframe": "7.32.1", diff --git a/src/common/AriaQueryHandler.ts b/src/common/AriaQueryHandler.ts index 0fb9878e9df..c0a23274e9c 100644 --- a/src/common/AriaQueryHandler.ts +++ b/src/common/AriaQueryHandler.ts @@ -111,7 +111,9 @@ const waitFor = async ( return domWorld._waitForSelectorInPage( (_: Element, selector: string) => ( - globalThis as unknown as { ariaQuerySelector(selector: string): void } + globalThis as any as unknown as { + ariaQuerySelector(selector: string): void; + } ).ariaQuerySelector(selector), selector, options, diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index 11b83f8d206..4ed27b8183f 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -719,10 +719,10 @@ export class DOMWorld { function deliverResult(name: string, seq: number, result: unknown): void { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Code is evaluated in a different context. - globalThis[name].callbacks.get(seq).resolve(result); + (globalThis as any)[name].callbacks.get(seq).resolve(result); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Code is evaluated in a different context. - globalThis[name].callbacks.delete(seq); + (globalThis as any)[name].callbacks.delete(seq); } }; diff --git a/src/common/Debug.ts b/src/common/Debug.ts index 6e2f96baa57..f768b4792fc 100644 --- a/src/common/Debug.ts +++ b/src/common/Debug.ts @@ -65,7 +65,7 @@ export const debug = (prefix: string): ((...args: unknown[]) => void) => { } return (...logArgs: unknown[]): void => { - const debugLevel = globalThis.__PUPPETEER_DEBUG; + const debugLevel = (globalThis as any).__PUPPETEER_DEBUG; if (!debugLevel) { return; } diff --git a/src/common/DeviceDescriptors.ts b/src/common/DeviceDescriptors.ts index 75a4a65d312..fc2cfc20b85 100644 --- a/src/common/DeviceDescriptors.ts +++ b/src/common/DeviceDescriptors.ts @@ -1527,6 +1527,7 @@ const devices: Device[] = [ }, }, ]; + /** * @public */ diff --git a/src/common/Errors.ts b/src/common/Errors.ts index 2676caa893f..fbd23c3abea 100644 --- a/src/common/Errors.ts +++ b/src/common/Errors.ts @@ -36,6 +36,7 @@ export class CustomError extends Error { * @public */ export class TimeoutError extends CustomError {} + /** * ProtocolError is emitted whenever there is an error from the protocol. * @@ -45,13 +46,19 @@ export class ProtocolError extends CustomError { public code?: number; public originalMessage = ''; } + /** * @public */ -export type PuppeteerErrors = Record; +export interface PuppeteerErrors { + TimeoutError: typeof TimeoutError; + ProtocolError: typeof ProtocolError; +} + /** * @public */ -export const puppeteerErrors: PuppeteerErrors = { +export const puppeteerErrors: PuppeteerErrors = Object.freeze({ TimeoutError, -}; + ProtocolError, +}); diff --git a/src/common/Page.ts b/src/common/Page.ts index 7bad98a8760..b22f9c993ee 100644 --- a/src/common/Page.ts +++ b/src/common/Page.ts @@ -2293,7 +2293,9 @@ export class Page extends EventEmitter { */ async emulateMediaType(type?: string): Promise { assert( - type === 'screen' || type === 'print' || type === null, + type === 'screen' || + type === 'print' || + (type ?? undefined) === undefined, 'Unsupported media type: ' + type ); await this.#client.send('Emulation.setEmulatedMedia', { diff --git a/src/common/fetch.ts b/src/common/fetch.ts index c0ca7e3baf0..218c178986c 100644 --- a/src/common/fetch.ts +++ b/src/common/fetch.ts @@ -16,5 +16,5 @@ /* Use the global version if we're in the browser, else load the node-fetch module. */ export const getFetch = async (): Promise => { - return globalThis.fetch || (await import('cross-fetch')).fetch; + return (globalThis as any).fetch || (await import('cross-fetch')).fetch; }; diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 9d86da20e14..878d3896627 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -1,5 +1,6 @@ module.exports = { rules: { + 'arrow-body-style': ['error', 'always'], 'no-restricted-imports': [ 'error', { diff --git a/test/fixtures/dumpio.js b/test/fixtures/dumpio.js index 40b9714f6ca..a16cf4d633d 100644 --- a/test/fixtures/dumpio.js +++ b/test/fixtures/dumpio.js @@ -2,7 +2,9 @@ const [, , puppeteerRoot, options] = process.argv; const browser = await require(puppeteerRoot).launch(JSON.parse(options)); const page = await browser.newPage(); - await page.evaluate(() => console.error('message from dumpio')); + await page.evaluate(() => { + return console.error('message from dumpio'); + }); await page.close(); await browser.close(); })(); diff --git a/test/src/CDPSession.spec.ts b/test/src/CDPSession.spec.ts index d5999d3d70e..34c095e01d6 100644 --- a/test/src/CDPSession.spec.ts +++ b/test/src/CDPSession.spec.ts @@ -21,7 +21,8 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeChromeOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; +import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js'; describeChromeOnly('Target.createCDPSession', function () { setupTestBrowserHooks(); @@ -36,7 +37,9 @@ describeChromeOnly('Target.createCDPSession', function () { client.send('Runtime.enable'), client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' }), ]); - const foo = await page.evaluate(() => globalThis.foo); + const foo = await page.evaluate(() => { + return (globalThis as any).foo; + }); expect(foo).toBe('bar'); }); it('should send events', async () => { @@ -45,7 +48,9 @@ describeChromeOnly('Target.createCDPSession', function () { const client = await page.target().createCDPSession(); await client.send('Network.enable'); const events = []; - client.on('Network.requestWillBeSent', (event) => events.push(event)); + client.on('Network.requestWillBeSent', (event) => { + return events.push(event); + }); await page.goto(server.EMPTY_PAGE); expect(events.length).toBe(1); }); @@ -77,14 +82,16 @@ describeChromeOnly('Target.createCDPSession', function () { }); expect(evalResponse.result.value).toBe(3); await client.detach(); - let error = null; + let error!: Error; try { await client.send('Runtime.evaluate', { expression: '3 + 1', returnByValue: true, }); } catch (error_) { - error = error_; + if (isErrorLike(error_)) { + error = error_ as Error; + } } expect(error.message).toContain('Session closed.'); }); @@ -92,7 +99,9 @@ describeChromeOnly('Target.createCDPSession', function () { const { page } = getTestState(); const client = await page.target().createCDPSession(); - const error = await theSourceOfTheProblems().catch((error) => error); + const error = await theSourceOfTheProblems().catch((error) => { + return error; + }); expect(error.stack).toContain('theSourceOfTheProblems'); expect(error.message).toContain('ThisCommand.DoesNotExist'); diff --git a/test/src/EventEmitter.spec.ts b/test/src/EventEmitter.spec.ts index f15a7952ffa..44624f8f558 100644 --- a/test/src/EventEmitter.spec.ts +++ b/test/src/EventEmitter.spec.ts @@ -19,7 +19,7 @@ import sinon from 'sinon'; import expect from 'expect'; describe('EventEmitter', () => { - let emitter; + let emitter: EventEmitter; beforeEach(() => { emitter = new EventEmitter(); @@ -40,7 +40,7 @@ describe('EventEmitter', () => { emitter[methodName]('foo', listener); emitter.emit('foo', data); expect(listener.callCount).toEqual(1); - expect(listener.firstCall.args[0]).toBe(data); + expect(listener.firstCall.args[0]!).toBe(data); }); it(`${methodName}: supports chaining`, () => { @@ -116,7 +116,7 @@ describe('EventEmitter', () => { emitter.emit('foo', data); expect(listener.callCount).toEqual(1); - expect(listener.firstCall.args[0]).toBe(data); + expect(listener.firstCall.args[0]!).toBe(data); }); it('returns true if the event has listeners', () => { diff --git a/test/src/NetworkManager.spec.ts b/test/src/NetworkManager.spec.ts index b78aa8c3260..f1d6b6d88cb 100644 --- a/test/src/NetworkManager.spec.ts +++ b/test/src/NetworkManager.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { describeChromeOnly } from './mocha-utils'; // eslint-disable-line import/extensions +import { describeChromeOnly } from './mocha-utils.js'; import expect from 'expect'; import { diff --git a/test/src/accessibility.spec.ts b/test/src/accessibility.spec.ts index 65d3cb3057b..e146e895662 100644 --- a/test/src/accessibility.spec.ts +++ b/test/src/accessibility.spec.ts @@ -14,13 +14,15 @@ * limitations under the License. */ +import assert from 'assert'; import expect from 'expect'; +import { SerializedAXNode } from '../../lib/cjs/puppeteer/common/Accessibility.js'; import { getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describeFailsFirefox('Accessibility', function () { setupTestBrowserHooks(); @@ -170,7 +172,10 @@ describeFailsFirefox('Accessibility', function () { ); const snapshot = await page.accessibility.snapshot(); // See https://chromium-review.googlesource.com/c/chromium/src/+/3088862 - expect(snapshot.children[0].roledescription).toEqual(undefined); + assert(snapshot); + assert(snapshot.children); + assert(snapshot.children[0]!); + expect(snapshot.children[0]!.roledescription).toBeUndefined(); }); it('orientation', async () => { const { page } = getTestState(); @@ -179,14 +184,20 @@ describeFailsFirefox('Accessibility', function () { '11' ); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].orientation).toEqual('vertical'); + assert(snapshot); + assert(snapshot.children); + assert(snapshot.children[0]!); + expect(snapshot.children[0]!.orientation).toEqual('vertical'); }); it('autocomplete', async () => { const { page } = getTestState(); await page.setContent(''); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].autocomplete).toEqual('list'); + assert(snapshot); + assert(snapshot.children); + assert(snapshot.children[0]!); + expect(snapshot.children[0]!.autocomplete).toEqual('list'); }); it('multiselectable', async () => { const { page } = getTestState(); @@ -195,7 +206,10 @@ describeFailsFirefox('Accessibility', function () { '
hey
' ); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].multiselectable).toEqual(true); + assert(snapshot); + assert(snapshot.children); + assert(snapshot.children[0]!); + expect(snapshot.children[0]!.multiselectable).toEqual(true); }); it('keyshortcuts', async () => { const { page } = getTestState(); @@ -204,7 +218,10 @@ describeFailsFirefox('Accessibility', function () { '
hey
' ); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].keyshortcuts).toEqual('foo'); + assert(snapshot); + assert(snapshot.children); + assert(snapshot.children[0]!); + expect(snapshot.children[0]!.keyshortcuts).toEqual('foo'); }); describe('filtering children of leaf nodes', function () { it('should not report text nodes inside controls', async () => { @@ -286,7 +303,9 @@ describeFailsFirefox('Accessibility', function () { ], }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual(golden); }); it('rich text editable fields with role should have children', async () => { const { page, isFirefox } = getTestState(); @@ -324,7 +343,9 @@ describeFailsFirefox('Accessibility', function () { ], }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual(golden); }); // Firefox does not support contenteditable="plaintext-only". @@ -335,7 +356,9 @@ describeFailsFirefox('Accessibility', function () { await page.setContent(`
Edit this image:my fake image
`); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual({ role: 'textbox', name: '', value: 'Edit this image:', @@ -363,7 +386,9 @@ describeFailsFirefox('Accessibility', function () { value: 'this is the inner content ', }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual(golden); }); it('checkbox with and tabIndex and label should not have children', async () => { const { page, isFirefox } = getTestState(); @@ -385,7 +410,9 @@ describeFailsFirefox('Accessibility', function () { checked: true, }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual(golden); }); it('checkbox without label should not have children', async () => { const { page, isFirefox } = getTestState(); @@ -407,7 +434,9 @@ describeFailsFirefox('Accessibility', function () { checked: true, }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); + assert(snapshot); + assert(snapshot.children); + expect(snapshot.children[0]!).toEqual(golden); }); describe('root option', function () { @@ -416,7 +445,7 @@ describeFailsFirefox('Accessibility', function () { await page.setContent(``); - const button = await page.$('button'); + const button = (await page.$('button'))!; expect(await page.accessibility.snapshot({ root: button })).toEqual({ role: 'button', name: 'My Button', @@ -427,7 +456,7 @@ describeFailsFirefox('Accessibility', function () { await page.setContent(``); - const input = await page.$('input'); + const input = (await page.$('input'))!; expect(await page.accessibility.snapshot({ root: input })).toEqual({ role: 'textbox', name: 'My Input', @@ -445,7 +474,7 @@ describeFailsFirefox('Accessibility', function () { `); - const menu = await page.$('div[role="menu"]'); + const menu = (await page.$('div[role="menu"]'))!; expect(await page.accessibility.snapshot({ root: menu })).toEqual({ role: 'menu', name: 'My Menu', @@ -461,8 +490,10 @@ describeFailsFirefox('Accessibility', function () { const { page } = getTestState(); await page.setContent(``); - const button = await page.$('button'); - await page.$eval('button', (button) => button.remove()); + const button = (await page.$('button'))!; + await page.$eval('button', (button) => { + return button.remove(); + }); expect(await page.accessibility.snapshot({ root: button })).toEqual( null ); @@ -471,7 +502,7 @@ describeFailsFirefox('Accessibility', function () { const { page } = getTestState(); await page.setContent(`
`); - const div = await page.$('div'); + const div = (await page.$('div'))!; expect(await page.accessibility.snapshot({ root: div })).toEqual(null); expect( await page.accessibility.snapshot({ @@ -492,11 +523,14 @@ describeFailsFirefox('Accessibility', function () { }); }); }); - function findFocusedNode(node) { - if (node.focused) { + + function findFocusedNode( + node: SerializedAXNode | null + ): SerializedAXNode | null { + if (node?.focused) { return node; } - for (const child of node.children || []) { + for (const child of node?.children || []) { const focusedChild = findFocusedNode(child); if (focusedChild) { return focusedChild; diff --git a/test/src/ariaqueryhandler.spec.ts b/test/src/ariaqueryhandler.spec.ts index 45d5bfc6eb3..1c6dffe0ead 100644 --- a/test/src/ariaqueryhandler.spec.ts +++ b/test/src/ariaqueryhandler.spec.ts @@ -20,10 +20,11 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeChromeOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import { ElementHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js'; import utils from './utils.js'; +import assert from 'assert'; describeChromeOnly('AriaQueryHandler', () => { setupTestBrowserHooks(); @@ -38,8 +39,11 @@ describeChromeOnly('AriaQueryHandler', () => { }); it('should find button', async () => { const { page } = getTestState(); - const expectFound = async (button: ElementHandle) => { - const id = await button.evaluate((button: Element) => button.id); + const expectFound = async (button: ElementHandle | null) => { + assert(button); + const id = await button.evaluate((button: Element) => { + return button.id; + }); expect(id).toBe('btn'); }; let button = await page.$( @@ -94,8 +98,10 @@ describeChromeOnly('AriaQueryHandler', () => { await page.setContent( '
' ); - const button = await page.$('aria/[role="button"]'); - const id = await button.evaluate((button: Element) => button.id); + const button = (await page.$('aria/[role="button"]'))!; + const id = await button!.evaluate((button: Element) => { + return button.id; + }); expect(id).toBe('btn'); }); @@ -104,8 +110,10 @@ describeChromeOnly('AriaQueryHandler', () => { await page.setContent( '
' ); - const button = await page.$('aria/Submit[role="button"]'); - const id = await button.evaluate((button: Element) => button.id); + const button = (await page.$('aria/Submit[role="button"]'))!; + const id = await button!.evaluate((button: Element) => { + return button.id; + }); expect(id).toBe('btn'); }); @@ -117,8 +125,10 @@ describeChromeOnly('AriaQueryHandler', () => { ` ); - const div = await page.$('aria/menu div'); - const id = await div.evaluate((div: Element) => div.id); + const div = (await page.$('aria/menu div'))!; + const id = await div!.evaluate((div: Element) => { + return div.id; + }); expect(id).toBe('mnu1'); }); @@ -130,8 +140,10 @@ describeChromeOnly('AriaQueryHandler', () => { ` ); - const menu = await page.$('aria/menu-label1'); - const id = await menu.evaluate((div: Element) => div.id); + const menu = (await page.$('aria/menu-label1'))!; + const id = await menu!.evaluate((div: Element) => { + return div.id; + }); expect(id).toBe('mnu1'); }); @@ -143,8 +155,10 @@ describeChromeOnly('AriaQueryHandler', () => { ` ); - const menu = await page.$('aria/menu-label2'); - const id = await menu.evaluate((div: Element) => div.id); + const menu = (await page.$('aria/menu-label2'))!; + const id = await menu!.evaluate((div: Element) => { + return div.id; + }); expect(id).toBe('mnu2'); }); }); @@ -160,7 +174,11 @@ describeChromeOnly('AriaQueryHandler', () => { ); const divs = await page.$$('aria/menu div'); const ids = await Promise.all( - divs.map((n) => n.evaluate((div: Element) => div.id)) + divs.map((n) => { + return n.evaluate((div: Element) => { + return div.id; + }); + }) ); expect(ids.join(', ')).toBe('mnu1, mnu2'); }); @@ -178,16 +196,19 @@ describeChromeOnly('AriaQueryHandler', () => { } ` ); - const sum = await page.$$eval('aria/[role="button"]', (buttons) => - buttons.reduce((acc, button) => acc + Number(button.textContent), 0) - ); + const sum = await page.$$eval('aria/[role="button"]', (buttons) => { + return buttons.reduce((acc, button) => { + return acc + Number(button.textContent); + }, 0); + }); expect(sum).toBe(50005000); }); }); describe('waitForSelector (aria)', function () { - const addElement = (tag) => - document.body.appendChild(document.createElement(tag)); + const addElement = (tag: string) => { + return document.body.appendChild(document.createElement(tag)); + }; it('should immediately resolve promise if node exists', async () => { const { page, server } = getTestState(); @@ -199,11 +220,11 @@ describeChromeOnly('AriaQueryHandler', () => { it('should work for ElementHandler.waitForSelector', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - await page.evaluate( - () => (document.body.innerHTML = `
`) - ); - const element = await page.$('div'); - await element.waitForSelector('aria/test'); + await page.evaluate(() => { + return (document.body.innerHTML = `
`); + }); + const element = (await page.$('div'))!; + await element!.waitForSelector('aria/test'); }); it('should persist query handler bindings across reloads', async () => { @@ -235,7 +256,9 @@ describeChromeOnly('AriaQueryHandler', () => { it('should work independently of `exposeFunction`', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - await page.exposeFunction('ariaQuerySelector', (a, b) => a + b); + await page.exposeFunction('ariaQuerySelector', (a: number, b: number) => { + return a + b; + }); await page.evaluate(addElement, 'button'); await page.waitForSelector('aria/[role="button"]'); const result = await page.evaluate('globalThis.ariaQuerySelector(2,8)'); @@ -245,13 +268,18 @@ describeChromeOnly('AriaQueryHandler', () => { it('should work with removed MutationObserver', async () => { const { page } = getTestState(); - await page.evaluate(() => delete window.MutationObserver); + await page.evaluate(() => { + // @ts-expect-error This is the point of the test. + return delete window.MutationObserver; + }); const [handle] = await Promise.all([ page.waitForSelector('aria/anything'), page.setContent(`

anything

`), ]); expect( - await page.evaluate((x: HTMLElement) => x.textContent, handle) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, handle) ).toBe('anything'); }); @@ -263,10 +291,10 @@ describeChromeOnly('AriaQueryHandler', () => { const watchdog = frame.waitForSelector('aria/[role="heading"]'); await frame.evaluate(addElement, 'br'); await frame.evaluate(addElement, 'h1'); - const elementHandle = await watchdog; - const tagName = await elementHandle - .getProperty('tagName') - .then((element) => element.jsonValue()); + const elementHandle = (await watchdog)!; + const tagName = await ( + await elementHandle.getProperty('tagName') + ).jsonValue(); expect(tagName).toBe('H1'); }); @@ -276,11 +304,10 @@ describeChromeOnly('AriaQueryHandler', () => { await page.goto(server.EMPTY_PAGE); const watchdog = page.waitForSelector('aria/name'); await page.evaluate(addElement, 'span'); - await page.evaluate( - () => - (document.querySelector('span').innerHTML = - '

') - ); + await page.evaluate(() => { + return (document.querySelector('span')!.innerHTML = + '

'); + }); await watchdog; }); @@ -291,10 +318,10 @@ describeChromeOnly('AriaQueryHandler', () => { await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const otherFrame = page.frames()[1]; const watchdog = page.waitForSelector('aria/[role="button"]'); - await otherFrame.evaluate(addElement, 'button'); + await otherFrame!.evaluate(addElement, 'button'); await page.evaluate(addElement, 'button'); const elementHandle = await watchdog; - expect(elementHandle.executionContext().frame()).toBe(page.mainFrame()); + expect(elementHandle!.executionContext().frame()).toBe(page.mainFrame()); }); it('should run in specified frame', async () => { @@ -304,13 +331,13 @@ describeChromeOnly('AriaQueryHandler', () => { await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); const frame1 = page.frames()[1]; const frame2 = page.frames()[2]; - const waitForSelectorPromise = frame2.waitForSelector( + const waitForSelectorPromise = frame2!.waitForSelector( 'aria/[role="button"]' ); - await frame1.evaluate(addElement, 'button'); - await frame2.evaluate(addElement, 'button'); + await frame1!.evaluate(addElement, 'button'); + await frame2!.evaluate(addElement, 'button'); const elementHandle = await waitForSelectorPromise; - expect(elementHandle.executionContext().frame()).toBe(frame2); + expect(elementHandle!.executionContext().frame()).toBe(frame2); }); it('should throw when frame is detached', async () => { @@ -318,10 +345,12 @@ describeChromeOnly('AriaQueryHandler', () => { await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const frame = page.frames()[1]; - let waitError = null; - const waitPromise = frame + let waitError!: Error; + const waitPromise = frame! .waitForSelector('aria/does-not-exist') - .catch((error) => (waitError = error)); + .catch((error) => { + return (waitError = error); + }); await utils.detachFrame(page, 'frame1'); await waitPromise; expect(waitError).toBeTruthy(); @@ -336,7 +365,9 @@ describeChromeOnly('AriaQueryHandler', () => { let imgFound = false; const waitForSelector = page .waitForSelector('aria/[role="img"]') - .then(() => (imgFound = true)); + .then(() => { + return (imgFound = true); + }); await page.goto(server.EMPTY_PAGE); expect(imgFound).toBe(false); await page.reload(); @@ -352,18 +383,22 @@ describeChromeOnly('AriaQueryHandler', () => { let divFound = false; const waitForSelector = page .waitForSelector('aria/name', { visible: true }) - .then(() => (divFound = true)); + .then(() => { + return (divFound = true); + }); await page.setContent( `
1
` ); expect(divFound).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('display') - ); + await page.evaluate(() => { + return document.querySelector('div')!.style.removeProperty('display'); + }); expect(divFound).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('visibility') - ); + await page.evaluate(() => { + return document + .querySelector('div')! + .style.removeProperty('visibility'); + }); expect(await waitForSelector).toBe(true); expect(divFound).toBe(true); }); @@ -374,18 +409,22 @@ describeChromeOnly('AriaQueryHandler', () => { let divVisible = false; const waitForSelector = page .waitForSelector('aria/inner', { visible: true }) - .then(() => (divVisible = true)); + .then(() => { + return (divVisible = true); + }); await page.setContent( `
hi
` ); expect(divVisible).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('display') - ); + await page.evaluate(() => { + return document.querySelector('div')!.style.removeProperty('display'); + }); expect(divVisible).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('visibility') - ); + await page.evaluate(() => { + return document + .querySelector('div')! + .style.removeProperty('visibility'); + }); expect(await waitForSelector).toBe(true); expect(divVisible).toBe(true); }); @@ -399,12 +438,16 @@ describeChromeOnly('AriaQueryHandler', () => { ); const waitForSelector = page .waitForSelector('aria/[role="button"]', { hidden: true }) - .then(() => (divHidden = true)); + .then(() => { + return (divHidden = true); + }); await page.waitForSelector('aria/[role="button"]'); // do a round trip expect(divHidden).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.setProperty('visibility', 'hidden') - ); + await page.evaluate(() => { + return document + .querySelector('div')! + .style.setProperty('visibility', 'hidden'); + }); expect(await waitForSelector).toBe(true); expect(divHidden).toBe(true); }); @@ -416,12 +459,16 @@ describeChromeOnly('AriaQueryHandler', () => { await page.setContent(`
`); const waitForSelector = page .waitForSelector('aria/[role="main"]', { hidden: true }) - .then(() => (divHidden = true)); + .then(() => { + return (divHidden = true); + }); await page.waitForSelector('aria/[role="main"]'); // do a round trip expect(divHidden).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.setProperty('display', 'none') - ); + await page.evaluate(() => { + return document + .querySelector('div')! + .style.setProperty('display', 'none'); + }); expect(await waitForSelector).toBe(true); expect(divHidden).toBe(true); }); @@ -433,10 +480,14 @@ describeChromeOnly('AriaQueryHandler', () => { let divRemoved = false; const waitForSelector = page .waitForSelector('aria/[role="main"]', { hidden: true }) - .then(() => (divRemoved = true)); + .then(() => { + return (divRemoved = true); + }); await page.waitForSelector('aria/[role="main"]'); // do a round trip expect(divRemoved).toBe(false); - await page.evaluate(() => document.querySelector('div').remove()); + await page.evaluate(() => { + return document.querySelector('div')!.remove(); + }); expect(await waitForSelector).toBe(true); expect(divRemoved).toBe(true); }); @@ -453,10 +504,12 @@ describeChromeOnly('AriaQueryHandler', () => { it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; + let error!: Error; await page .waitForSelector('aria/[role="button"]', { timeout: 10 }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain( 'waiting for selector `[role="button"]` failed: timeout' @@ -468,10 +521,12 @@ describeChromeOnly('AriaQueryHandler', () => { const { page } = getTestState(); await page.setContent(`
`); - let error = null; + let error!: Error; await page .waitForSelector('aria/[role="main"]', { hidden: true, timeout: 10 }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain( 'waiting for selector `[role="main"]` to be hidden failed: timeout' @@ -482,14 +537,16 @@ describeChromeOnly('AriaQueryHandler', () => { const { page } = getTestState(); let divFound = false; - const waitForSelector = page - .waitForSelector('aria/zombo') - .then(() => (divFound = true)); + const waitForSelector = page.waitForSelector('aria/zombo').then(() => { + return (divFound = true); + }); await page.setContent(`
`); expect(divFound).toBe(false); - await page.evaluate(() => - document.querySelector('div').setAttribute('aria-label', 'zombo') - ); + await page.evaluate(() => { + return document + .querySelector('div')! + .setAttribute('aria-label', 'zombo'); + }); expect(await waitForSelector).toBe(true); }); @@ -499,21 +556,22 @@ describeChromeOnly('AriaQueryHandler', () => { const waitForSelector = page.waitForSelector('aria/zombo'); await page.setContent(`
anything
`); expect( - await page.evaluate( - (x: HTMLElement) => x.textContent, - await waitForSelector - ) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, await waitForSelector) ).toBe('anything'); }); it('should have correct stack trace for timeout', async () => { const { page } = getTestState(); - let error; + let error!: Error; await page .waitForSelector('aria/zombo', { timeout: 10 }) - .catch((error_) => (error = error_)); - expect(error.stack).toContain('waiting for selector `zombo` failed'); + .catch((error_) => { + return (error = error_); + }); + expect(error!.stack).toContain('waiting for selector `zombo` failed'); }); }); @@ -566,12 +624,15 @@ describeChromeOnly('AriaQueryHandler', () => { ` ); }); - const getIds = async (elements: ElementHandle[]) => - Promise.all( - elements.map((element) => - element.evaluate((element: Element) => element.id) - ) + const getIds = async (elements: ElementHandle[]) => { + return Promise.all( + elements.map((element) => { + return element.evaluate((element: Element) => { + return element.id; + }); + }) ); + }; it('should find by name "foo"', async () => { const { page } = getTestState(); const found = await page.$$('aria/foo'); diff --git a/test/src/browser.spec.ts b/test/src/browser.spec.ts index 6676d034fe8..25892211786 100644 --- a/test/src/browser.spec.ts +++ b/test/src/browser.spec.ts @@ -15,7 +15,7 @@ */ import expect from 'expect'; -import { getTestState, setupTestBrowserHooks } from './mocha-utils'; // eslint-disable-line import/extensions +import { getTestState, setupTestBrowserHooks } from './mocha-utils.js'; describe('Browser specs', function () { setupTestBrowserHooks(); @@ -60,7 +60,7 @@ describe('Browser specs', function () { const { browser } = getTestState(); const process = await browser.process(); - expect(process.pid).toBeGreaterThan(0); + expect(process!.pid).toBeGreaterThan(0); }); it('should not return child_process for remote browser', async () => { const { browser, puppeteer } = getTestState(); diff --git a/test/src/browsercontext.spec.ts b/test/src/browsercontext.spec.ts index ba4866a764c..48466a53104 100644 --- a/test/src/browsercontext.spec.ts +++ b/test/src/browsercontext.spec.ts @@ -19,7 +19,7 @@ import { getTestState, setupTestBrowserHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import utils from './utils.js'; describe('BrowserContext', function () { @@ -27,10 +27,12 @@ describe('BrowserContext', function () { it('should have default context', async () => { const { browser } = getTestState(); expect(browser.browserContexts().length).toEqual(1); - const defaultContext = browser.browserContexts()[0]; - expect(defaultContext.isIncognito()).toBe(false); - let error = null; - await defaultContext.close().catch((error_) => (error = error_)); + const defaultContext = browser.browserContexts()[0]!; + expect(defaultContext!.isIncognito()).toBe(false); + let error!: Error; + await defaultContext!.close().catch((error_) => { + return (error = error_); + }); expect(browser.defaultBrowserContext()).toBe(defaultContext); expect(error.message).toContain('cannot be closed'); }); @@ -66,10 +68,9 @@ describe('BrowserContext', function () { await page.goto(server.EMPTY_PAGE); const [popupTarget] = await Promise.all([ utils.waitEvent(browser, 'targetcreated'), - page.evaluate<(url: string) => void>( - (url) => window.open(url), - server.EMPTY_PAGE - ), + page.evaluate<(url: string) => void>((url) => { + return window.open(url); + }, server.EMPTY_PAGE), ]); expect(popupTarget.browserContext()).toBe(context); await context.close(); @@ -78,16 +79,16 @@ describe('BrowserContext', function () { const { browser, server } = getTestState(); const context = await browser.createIncognitoBrowserContext(); - const events = []; - context.on('targetcreated', (target) => - events.push('CREATED: ' + target.url()) - ); - context.on('targetchanged', (target) => - events.push('CHANGED: ' + target.url()) - ); - context.on('targetdestroyed', (target) => - events.push('DESTROYED: ' + target.url()) - ); + const events: any[] = []; + context.on('targetcreated', (target) => { + return events.push('CREATED: ' + target.url()); + }); + context.on('targetchanged', (target) => { + return events.push('CHANGED: ' + target.url()); + }); + context.on('targetdestroyed', (target) => { + return events.push('DESTROYED: ' + target.url()); + }); const page = await context.newPage(); await page.goto(server.EMPTY_PAGE); await page.close(); @@ -104,11 +105,13 @@ describe('BrowserContext', function () { const context = await browser.createIncognitoBrowserContext(); let resolved = false; - const targetPromise = context.waitForTarget( - (target) => target.url() === server.EMPTY_PAGE - ); + const targetPromise = context.waitForTarget((target) => { + return target.url() === server.EMPTY_PAGE; + }); targetPromise - .then(() => (resolved = true)) + .then(() => { + return (resolved = true); + }) .catch((error) => { resolved = true; if (error instanceof puppeteer.errors.TimeoutError) { @@ -138,10 +141,17 @@ describe('BrowserContext', function () { const context = await browser.createIncognitoBrowserContext(); const error = await context - .waitForTarget((target) => target.url() === server.EMPTY_PAGE, { - timeout: 1, - }) - .catch((error_) => error_); + .waitForTarget( + (target) => { + return target.url() === server.EMPTY_PAGE; + }, + { + timeout: 1, + } + ) + .catch((error_) => { + return error_; + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); await context.close(); }); @@ -175,19 +185,31 @@ describe('BrowserContext', function () { }); expect(context1.targets().length).toBe(1); - expect(context1.targets()[0]).toBe(page1.target()); + expect(context1.targets()[0]!).toBe(page1.target()); expect(context2.targets().length).toBe(1); - expect(context2.targets()[0]).toBe(page2.target()); + expect(context2.targets()[0]!).toBe(page2.target()); // Make sure pages don't share localstorage or cookies. - expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe( - 'page1' - ); - expect(await page1.evaluate(() => document.cookie)).toBe('name=page1'); - expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe( - 'page2' - ); - expect(await page2.evaluate(() => document.cookie)).toBe('name=page2'); + expect( + await page1.evaluate(() => { + return localStorage.getItem('name'); + }) + ).toBe('page1'); + expect( + await page1.evaluate(() => { + return document.cookie; + }) + ).toBe('name=page1'); + expect( + await page2.evaluate(() => { + return localStorage.getItem('name'); + }) + ).toBe('page2'); + expect( + await page2.evaluate(() => { + return document.cookie; + }) + ).toBe('name=page2'); // Cleanup contexts. await Promise.all([context1.close(), context2.close()]); diff --git a/test/src/chromiumonly.spec.ts b/test/src/chromiumonly.spec.ts index 7b64a70fd39..651339dc7ba 100644 --- a/test/src/chromiumonly.spec.ts +++ b/test/src/chromiumonly.spec.ts @@ -14,12 +14,13 @@ * limitations under the License. */ import expect from 'expect'; +import { IncomingMessage } from 'http'; import { getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, describeChromeOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describeChromeOnly('Chromium-Specific Launcher tests', function () { describe('Puppeteer.launch |browserURL| option', function () { @@ -35,14 +36,22 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () { const browser1 = await puppeteer.connect({ browserURL }); const page1 = await browser1.newPage(); - expect(await page1.evaluate(() => 7 * 8)).toBe(56); + expect( + await page1.evaluate(() => { + return 7 * 8; + }) + ).toBe(56); browser1.disconnect(); const browser2 = await puppeteer.connect({ browserURL: browserURL + '/', }); const page2 = await browser2.newPage(); - expect(await page2.evaluate(() => 8 * 7)).toBe(56); + expect( + await page2.evaluate(() => { + return 8 * 7; + }) + ).toBe(56); browser2.disconnect(); originalBrowser.close(); }); @@ -56,13 +65,15 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () { ); const browserURL = 'http://127.0.0.1:21222'; - let error = null; + let error!: Error; await puppeteer .connect({ browserURL, browserWSEndpoint: originalBrowser.wsEndpoint(), }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( 'Exactly one of browserWSEndpoint, browserURL or transport' ); @@ -79,10 +90,10 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () { ); const browserURL = 'http://127.0.0.1:32333'; - let error = null; - await puppeteer - .connect({ browserURL }) - .catch((error_) => (error = error_)); + let error!: Error; + await puppeteer.connect({ browserURL }).catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( 'Failed to fetch browser webSocket URL from' ); @@ -117,11 +128,11 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () { const { defaultBrowserOptions, puppeteer } = getTestState(); const options = Object.assign({ pipe: true }, defaultBrowserOptions); const browser = await puppeteer.launch(options); - const disconnectedEventPromise = new Promise((resolve) => - browser.once('disconnected', resolve) - ); + const disconnectedEventPromise = new Promise((resolve) => { + return browser.once('disconnected', resolve); + }); // Emulate user exiting browser. - browser.process().kill(); + browser.process()!.kill(); await disconnectedEventPromise; }); }); @@ -133,26 +144,28 @@ describeChromeOnly('Chromium-Specific Page Tests', function () { it('Page.setRequestInterception should work with intervention headers', async () => { const { server, page } = getTestState(); - server.setRoute('/intervention', (req, res) => - res.end(` + server.setRoute('/intervention', (_req, res) => { + return res.end(` - `) - ); + `); + }); server.setRedirect('/intervention.js', '/redirect.js'); - let serverRequest = null; + let serverRequest: IncomingMessage | undefined; server.setRoute('/redirect.js', (req, res) => { serverRequest = req; res.end('console.log(1);'); }); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); await page.goto(server.PREFIX + '/intervention'); // Check for feature URL substring rather than https://www.chromestatus.com to // make it work with Edgium. - expect(serverRequest.headers.intervention).toContain( + expect(serverRequest!.headers['intervention']).toContain( 'feature/5718547946799104' ); }); diff --git a/test/src/click.spec.ts b/test/src/click.spec.ts index c689fe79aae..24a725f73aa 100644 --- a/test/src/click.spec.ts +++ b/test/src/click.spec.ts @@ -20,7 +20,7 @@ import { setupTestPageAndContextHooks, setupTestBrowserHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import utils from './utils.js'; describe('Page.click', function () { @@ -31,7 +31,11 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should click svg', async () => { const { page } = getTestState(); @@ -42,7 +46,11 @@ describe('Page.click', function () { `); await page.click('circle'); - expect(await page.evaluate(() => globalThis.__CLICKED)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__CLICKED; + }) + ).toBe(42); }); itFailsFirefox( 'should click the button if window.Node is removed', @@ -50,9 +58,16 @@ describe('Page.click', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => delete window.Node); + await page.evaluate(() => { + // @ts-expect-error Expected. + return delete window.Node; + }); await page.click('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); } ); // @see https://github.com/puppeteer/puppeteer/issues/4281 @@ -68,7 +83,11 @@ describe('Page.click', function () { `); await page.click('span'); - expect(await page.evaluate(() => globalThis.CLICKED)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).CLICKED; + }) + ).toBe(42); }); it('should not throw UnhandledPromiseRejection when page closes', async () => { const { page } = getTestState(); @@ -85,7 +104,11 @@ describe('Page.click', function () { await page.click('button'); await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); itFailsFirefox('should click with disabled javascript', async () => { const { page, server } = getTestState(); @@ -108,7 +131,11 @@ describe('Page.click', function () { woofdoggo `); await page.click('span'); - expect(await page.evaluate(() => globalThis.CLICKED)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).CLICKED; + }) + ).toBe(42); }); it('should select the text by triple clicking', async () => { const { page, server } = getTestState(); @@ -124,9 +151,9 @@ describe('Page.click', function () { expect( await page.evaluate(() => { const textarea = document.querySelector('textarea'); - return textarea.value.substring( - textarea.selectionStart, - textarea.selectionEnd + return textarea!.value.substring( + textarea!.selectionStart, + textarea!.selectionEnd ); }) ).toBe(text); @@ -135,11 +162,15 @@ describe('Page.click', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/offscreenbuttons.html'); - const messages = []; - page.on('console', (msg) => messages.push(msg.text())); + const messages: any[] = []; + page.on('console', (msg) => { + return messages.push(msg.text()); + }); for (let i = 0; i < 11; ++i) { // We might've scrolled to click a button - reset to (0, 0). - await page.evaluate(() => window.scrollTo(0, 0)); + await page.evaluate(() => { + return window.scrollTo(0, 0); + }); await page.click(`#btn${i}`); } expect(messages).toEqual([ @@ -162,17 +193,33 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/wrappedlink.html'); await page.click('a'); - expect(await page.evaluate(() => globalThis.__clicked)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).__clicked; + }) + ).toBe(true); }); it('should click on checkbox input and toggle', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(null); + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(null); await page.click('input#agree'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(true); - expect(await page.evaluate(() => globalThis.result.events)).toEqual([ + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).result.events; + }) + ).toEqual([ 'mouseover', 'mouseenter', 'mousemove', @@ -183,33 +230,49 @@ describe('Page.click', function () { 'change', ]); await page.click('input#agree'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(false); + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(false); }); itFailsFirefox('should click on checkbox label and toggle', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(null); + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(null); await page.click('label[for="agree"]'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(true); - expect(await page.evaluate(() => globalThis.result.events)).toEqual([ - 'click', - 'input', - 'change', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).result.events; + }) + ).toEqual(['click', 'input', 'change']); await page.click('label[for="agree"]'); - expect(await page.evaluate(() => globalThis.result.check)).toBe(false); + expect( + await page.evaluate(() => { + return (globalThis as any).result.check; + }) + ).toBe(false); }); it('should fail to click a missing button', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - let error = null; - await page - .click('button.does-not-exist') - .catch((error_) => (error = error_)); + let error!: Error; + await page.click('button.does-not-exist').catch((error_) => { + return (error = error_); + }); expect(error.message).toBe( 'No element found for selector: button.does-not-exist' ); @@ -218,7 +281,7 @@ describe('Page.click', function () { it('should not hang with touch-enabled viewports', async () => { const { page, puppeteer } = getTestState(); - await page.setViewport(puppeteer.devices['iPhone 6'].viewport); + await page.setViewport(puppeteer.devices['iPhone 6']!.viewport); await page.mouse.down(); await page.mouse.move(100, 10); await page.mouse.up(); @@ -229,13 +292,15 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.click('#button-5'); expect( - await page.evaluate(() => document.querySelector('#button-5').textContent) + await page.evaluate(() => { + return document.querySelector('#button-5')!.textContent; + }) ).toBe('clicked'); await page.click('#button-80'); expect( - await page.evaluate( - () => document.querySelector('#button-80').textContent - ) + await page.evaluate(() => { + return document.querySelector('#button-80')!.textContent; + }) ).toBe('clicked'); }); it('should double click the button', async () => { @@ -243,14 +308,14 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => { - globalThis.double = false; + (globalThis as any).double = false; const button = document.querySelector('button'); - button.addEventListener('dblclick', () => { - globalThis.double = true; + button!.addEventListener('dblclick', () => { + (globalThis as any).double = true; }); }); - const button = await page.$('button'); - await button.click({ clickCount: 2 }); + const button = (await page.$('button'))!; + await button!.click({ clickCount: 2 }); expect(await page.evaluate('double')).toBe(true); expect(await page.evaluate('result')).toBe('Clicked'); }); @@ -260,19 +325,27 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => { const button = document.querySelector('button'); - button.textContent = 'Some really long text that will go offscreen'; - button.style.position = 'absolute'; - button.style.left = '368px'; + button!.textContent = 'Some really long text that will go offscreen'; + button!.style.position = 'absolute'; + button!.style.left = '368px'; }); await page.click('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should click a rotated button', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/rotatedButton.html'); await page.click('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should fire contextmenu event on right click', async () => { const { page, server } = getTestState(); @@ -280,7 +353,9 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.click('#button-8', { button: 'right' }); expect( - await page.evaluate(() => document.querySelector('#button-8').textContent) + await page.evaluate(() => { + return document.querySelector('#button-8')!.textContent; + }) ).toBe('context menu'); }); it('should fire aux event on middle click', async () => { @@ -289,7 +364,9 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.click('#button-8', { button: 'middle' }); expect( - await page.evaluate(() => document.querySelector('#button-8').textContent) + await page.evaluate(() => { + return document.querySelector('#button-8')!.textContent; + }) ).toBe('aux click'); }); it('should fire back click', async () => { @@ -298,7 +375,9 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.click('#button-8', { button: 'back' }); expect( - await page.evaluate(() => document.querySelector('#button-8').textContent) + await page.evaluate(() => { + return document.querySelector('#button-8')!.textContent; + }) ).toBe('back click'); }); it('should fire forward click', async () => { @@ -307,7 +386,9 @@ describe('Page.click', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.click('#button-8', { button: 'forward' }); expect( - await page.evaluate(() => document.querySelector('#button-8').textContent) + await page.evaluate(() => { + return document.querySelector('#button-8')!.textContent; + }) ).toBe('forward click'); }); // @see https://github.com/puppeteer/puppeteer/issues/206 @@ -329,9 +410,13 @@ describe('Page.click', function () { server.PREFIX + '/input/button.html' ); const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); + const button = await frame!.$('button'); + await button!.click(); + expect( + await frame!.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); // @see https://github.com/puppeteer/puppeteer/issues/4110 xit('should click the button with fixed position inside an iframe', async () => { @@ -348,17 +433,25 @@ describe('Page.click', function () { server.CROSS_PROCESS_PREFIX + '/input/button.html' ); const frame = page.frames()[1]; - await frame.$eval('button', (button: HTMLElement) => - button.style.setProperty('position', 'fixed') - ); - await frame.click('button'); - expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); + await frame!.$eval('button', (button: Element) => { + return (button as HTMLElement).style.setProperty('position', 'fixed'); + }); + await frame!.click('button'); + expect( + await frame!.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should click the button with deviceScaleFactor set', async () => { const { page, server } = getTestState(); await page.setViewport({ width: 400, height: 400, deviceScaleFactor: 5 }); - expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); + expect( + await page.evaluate(() => { + return window.devicePixelRatio; + }) + ).toBe(5); await page.setContent('
spacer
'); await utils.attachFrame( page, @@ -366,8 +459,12 @@ describe('Page.click', function () { server.PREFIX + '/input/button.html' ); const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); + const button = await frame!.$('button'); + await button!.click(); + expect( + await frame!.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); }); diff --git a/test/src/cookies.spec.ts b/test/src/cookies.spec.ts index ad7339b0d39..4a73ed9d46c 100644 --- a/test/src/cookies.spec.ts +++ b/test/src/cookies.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('Cookie specs', () => { setupTestBrowserHooks(); @@ -58,36 +58,36 @@ describe('Cookie specs', () => { }); it('should properly report httpOnly cookie', async () => { const { page, server } = getTestState(); - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.setHeader('Set-Cookie', 'a=b; HttpOnly; Path=/'); res.end(); }); await page.goto(server.EMPTY_PAGE); const cookies = await page.cookies(); expect(cookies.length).toBe(1); - expect(cookies[0].httpOnly).toBe(true); + expect(cookies[0]!.httpOnly).toBe(true); }); it('should properly report "Strict" sameSite cookie', async () => { const { page, server } = getTestState(); - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.setHeader('Set-Cookie', 'a=b; SameSite=Strict'); res.end(); }); await page.goto(server.EMPTY_PAGE); const cookies = await page.cookies(); expect(cookies.length).toBe(1); - expect(cookies[0].sameSite).toBe('Strict'); + expect(cookies[0]!.sameSite).toBe('Strict'); }); it('should properly report "Lax" sameSite cookie', async () => { const { page, server } = getTestState(); - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.setHeader('Set-Cookie', 'a=b; SameSite=Lax'); res.end(); }); await page.goto(server.EMPTY_PAGE); const cookies = await page.cookies(); expect(cookies.length).toBe(1); - expect(cookies[0].sameSite).toBe('Lax'); + expect(cookies[0]!.sameSite).toBe('Lax'); }); it('should get multiple cookies', async () => { const { page, server } = getTestState(); @@ -97,7 +97,9 @@ describe('Cookie specs', () => { document.cookie = 'password=1234'; }); const cookies = await page.cookies(); - cookies.sort((a, b) => a.name.localeCompare(b.name)); + cookies.sort((a, b) => { + return a.name.localeCompare(b.name); + }); expectCookieEquals(cookies, [ { name: 'password', @@ -149,7 +151,9 @@ describe('Cookie specs', () => { } ); const cookies = await page.cookies('https://foo.com', 'https://baz.com'); - cookies.sort((a, b) => a.name.localeCompare(b.name)); + cookies.sort((a, b) => { + return a.name.localeCompare(b.name); + }); expectCookieEquals(cookies, [ { name: 'birdo', @@ -191,9 +195,11 @@ describe('Cookie specs', () => { name: 'password', value: '123456', }); - expect(await page.evaluate(() => document.cookie)).toEqual( - 'password=123456' - ); + expect( + await page.evaluate(() => { + return document.cookie; + }) + ).toEqual('password=123456'); }); itFailsFirefox('should isolate cookies in browser contexts', async () => { const { page, server, browser } = getTestState(); @@ -211,10 +217,10 @@ describe('Cookie specs', () => { const cookies2 = await anotherPage.cookies(); expect(cookies1.length).toBe(1); expect(cookies2.length).toBe(1); - expect(cookies1[0].name).toBe('page1cookie'); - expect(cookies1[0].value).toBe('page1value'); - expect(cookies2[0].name).toBe('page2cookie'); - expect(cookies2[0].value).toBe('page2value'); + expect(cookies1[0]!.name).toBe('page1cookie'); + expect(cookies1[0]!.value).toBe('page1value'); + expect(cookies2[0]!.name).toBe('page2cookie'); + expect(cookies2[0]!.value).toBe('page2value'); await anotherContext.close(); }); itFailsFirefox('should set multiple cookies', async () => { @@ -233,7 +239,11 @@ describe('Cookie specs', () => { ); const cookieStrings = await page.evaluate(() => { const cookies = document.cookie.split(';'); - return cookies.map((cookie) => cookie.trim()).sort(); + return cookies + .map((cookie) => { + return cookie.trim(); + }) + .sort(); }); expect(cookieStrings).toEqual(['foo=bar', 'password=123456']); @@ -247,8 +257,8 @@ describe('Cookie specs', () => { value: '123456', }); const cookies = await page.cookies(); - expect(cookies[0].session).toBe(true); - expect(cookies[0].expires).toBe(-1); + expect(cookies[0]!.session).toBe(true); + expect(cookies[0]!.expires).toBe(-1); }); itFailsFirefox('should set cookie with reasonable defaults', async () => { const { page, server } = getTestState(); @@ -260,7 +270,9 @@ describe('Cookie specs', () => { }); const cookies = await page.cookies(); expectCookieEquals( - cookies.sort((a, b) => a.name.localeCompare(b.name)), + cookies.sort((a, b) => { + return a.name.localeCompare(b.name); + }), [ { name: 'password', @@ -315,11 +327,11 @@ describe('Cookie specs', () => { const { page } = getTestState(); await page.goto('about:blank'); - let error = null; + let error!: Error; try { await page.setCookie({ name: 'example-cookie', value: 'best' }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toContain( 'At least one of the url and domain needs to be specified' @@ -328,7 +340,7 @@ describe('Cookie specs', () => { it('should not set a cookie with blank page URL', async () => { const { page, server } = getTestState(); - let error = null; + let error!: Error; await page.goto(server.EMPTY_PAGE); try { await page.setCookie( @@ -336,7 +348,7 @@ describe('Cookie specs', () => { { url: 'about:blank', name: 'example-cookie-blank', value: 'best' } ); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toEqual( `Blank page can not have cookie "example-cookie-blank"` @@ -345,12 +357,12 @@ describe('Cookie specs', () => { it('should not set a cookie on a data URL page', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page.goto('data:,Hello%2C%20World!'); try { await page.setCookie({ name: 'example-cookie', value: 'best' }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toContain( 'At least one of the url and domain needs to be specified' @@ -369,7 +381,7 @@ describe('Cookie specs', () => { value: 'bar', }); const [cookie] = await page.cookies(SECURE_URL); - expect(cookie.secure).toBe(true); + expect(cookie!.secure).toBe(true); } ); it('should be able to set unsecure cookie for HTTP website', async () => { @@ -383,7 +395,7 @@ describe('Cookie specs', () => { value: 'bar', }); const [cookie] = await page.cookies(HTTP_URL); - expect(cookie.secure).toBe(false); + expect(cookie!.secure).toBe(false); }); itFailsFirefox('should set a cookie on a different domain', async () => { const { page, server } = getTestState(); @@ -419,9 +431,11 @@ describe('Cookie specs', () => { await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({ name: 'localhost-cookie', value: 'best' }); await page.evaluate<(src: string) => Promise>((src) => { - let fulfill; - const promise = new Promise((x) => (fulfill = x)); - const iframe = document.createElement('iframe'); + let fulfill!: () => void; + const promise = new Promise((x) => { + return (fulfill = x); + }); + const iframe = document.createElement('iframe') as HTMLIFrameElement; document.body.appendChild(iframe); iframe.onload = fulfill; iframe.src = src; @@ -435,7 +449,7 @@ describe('Cookie specs', () => { expect(await page.evaluate('document.cookie')).toBe( 'localhost-cookie=best' ); - expect(await page.frames()[1].evaluate('document.cookie')).toBe(''); + expect(await page.frames()[1]!.evaluate('document.cookie')).toBe(''); expectCookieEquals(await page.cookies(), [ { @@ -487,8 +501,10 @@ describe('Cookie specs', () => { try { await page.goto(httpsServer.PREFIX + '/grid.html'); await page.evaluate<(src: string) => Promise>((src) => { - let fulfill; - const promise = new Promise((x) => (fulfill = x)); + let fulfill!: () => void; + const promise = new Promise((x) => { + return (fulfill = x); + }); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = fulfill; @@ -502,7 +518,7 @@ describe('Cookie specs', () => { sameSite: 'None', }); - expect(await page.frames()[1].evaluate('document.cookie')).toBe( + expect(await page.frames()[1]!.evaluate('document.cookie')).toBe( '127-same-site-cookie=best' ); expectCookieEquals( diff --git a/test/src/coverage.spec.ts b/test/src/coverage.spec.ts index 4801a8eed58..0be08ae4bae 100644 --- a/test/src/coverage.spec.ts +++ b/test/src/coverage.spec.ts @@ -20,7 +20,7 @@ import { setupTestPageAndContextHooks, setupTestBrowserHooks, describeChromeOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('Coverage specs', function () { describeChromeOnly('JSCoverage', function () { @@ -35,8 +35,8 @@ describe('Coverage specs', function () { }); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/jscoverage/simple.html'); - expect(coverage[0].ranges).toEqual([ + expect(coverage[0]!.url).toContain('/jscoverage/simple.html'); + expect(coverage[0]!.ranges).toEqual([ { start: 0, end: 17 }, { start: 35, end: 61 }, ]); @@ -48,7 +48,7 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.js'); + expect(coverage[0]!.url).toBe('nicename.js'); }); it('should ignore eval() scripts by default', async () => { const { page, server } = getTestState(); @@ -65,7 +65,9 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/jscoverage/eval.html'); const coverage = await page.coverage.stopJSCoverage(); expect( - coverage.find((entry) => entry.url.startsWith('debugger://')) + coverage.find((entry) => { + return entry.url.startsWith('debugger://'); + }) ).not.toBe(null); expect(coverage.length).toBe(2); }); @@ -75,7 +77,9 @@ describe('Coverage specs', function () { await page.coverage.startJSCoverage({ reportAnonymousScripts: true }); await page.goto(server.EMPTY_PAGE); await page.evaluate('console.log("foo")'); - await page.evaluate(() => console.log('bar')); + await page.evaluate(() => { + return console.log('bar'); + }); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(0); }); @@ -86,9 +90,11 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/jscoverage/multiple.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/jscoverage/script1.js'); - expect(coverage[1].url).toContain('/jscoverage/script2.js'); + coverage.sort((a, b) => { + return a.url.localeCompare(b.url); + }); + expect(coverage[0]!.url).toContain('/jscoverage/script1.js'); + expect(coverage[1]!.url).toContain('/jscoverage/script2.js'); }); it('should report right ranges', async () => { const { page, server } = getTestState(); @@ -97,9 +103,9 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/jscoverage/ranges.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - const entry = coverage[0]; + const entry = coverage[0]!; expect(entry.ranges.length).toBe(1); - const range = entry.ranges[0]; + const range = entry.ranges[0]!; expect(entry.text.substring(range.start, range.end)).toBe( `console.log('used!');` ); @@ -111,7 +117,7 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/jscoverage/unused.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - const entry = coverage[0]; + const entry = coverage[0]!; expect(entry.url).toContain('unused.html'); expect(entry.ranges.length).toBe(0); }); @@ -166,7 +172,7 @@ describe('Coverage specs', function () { }); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].rawScriptCoverage).toBeUndefined(); + expect(coverage[0]!.rawScriptCoverage).toBeUndefined(); }); it('should include rawScriptCoverage field when enabled', async () => { const { page, server } = getTestState(); @@ -178,7 +184,7 @@ describe('Coverage specs', function () { }); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].rawScriptCoverage).toBeTruthy(); + expect(coverage[0]!.rawScriptCoverage).toBeTruthy(); }); }); // @see https://crbug.com/990945 @@ -205,10 +211,10 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/csscoverage/simple.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/simple.html'); - expect(coverage[0].ranges).toEqual([{ start: 1, end: 22 }]); - const range = coverage[0].ranges[0]; - expect(coverage[0].text.substring(range.start, range.end)).toBe( + expect(coverage[0]!.url).toContain('/csscoverage/simple.html'); + expect(coverage[0]!.ranges).toEqual([{ start: 1, end: 22 }]); + const range = coverage[0]!.ranges[0]!; + expect(coverage[0]!.text.substring(range.start, range.end)).toBe( 'div { color: green; }' ); }); @@ -219,7 +225,7 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.css'); + expect(coverage[0]!.url).toBe('nicename.css'); }); it('should report multiple stylesheets', async () => { const { page, server } = getTestState(); @@ -228,9 +234,11 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/csscoverage/multiple.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); - expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); + coverage.sort((a, b) => { + return a.url.localeCompare(b.url); + }); + expect(coverage[0]!.url).toContain('/csscoverage/stylesheet1.css'); + expect(coverage[1]!.url).toContain('/csscoverage/stylesheet2.css'); }); it('should report stylesheets that have no coverage', async () => { const { page, server } = getTestState(); @@ -239,8 +247,8 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/csscoverage/unused.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('unused.css'); - expect(coverage[0].ranges.length).toBe(0); + expect(coverage[0]!.url).toBe('unused.css'); + expect(coverage[0]!.ranges.length).toBe(0); }); it('should work with media queries', async () => { const { page, server } = getTestState(); @@ -249,8 +257,8 @@ describe('Coverage specs', function () { await page.goto(server.PREFIX + '/csscoverage/media.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/media.html'); - expect(coverage[0].ranges).toEqual([{ start: 17, end: 38 }]); + expect(coverage[0]!.url).toContain('/csscoverage/media.html'); + expect(coverage[0]!.ranges).toEqual([{ start: 17, end: 38 }]); }); it('should work with complicated usecases', async () => { const { page, server } = getTestState(); @@ -268,9 +276,9 @@ describe('Coverage specs', function () { await page.coverage.startCSSCoverage(); await page.addStyleTag({ content: 'body { margin: 10px;}' }); // trigger style recalc - const margin = await page.evaluate( - () => window.getComputedStyle(document.body).margin - ); + const margin = await page.evaluate(() => { + return window.getComputedStyle(document.body).margin; + }); expect(margin).toBe('10px'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(0); @@ -286,7 +294,9 @@ describe('Coverage specs', function () { link.rel = 'stylesheet'; link.href = url; document.head.appendChild(link); - await new Promise((x) => (link.onload = x)); + await new Promise((x) => { + return (link.onload = x); + }); }, server.PREFIX + '/csscoverage/stylesheet1.css'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); diff --git a/test/src/defaultbrowsercontext.spec.ts b/test/src/defaultbrowsercontext.spec.ts index b84428de066..e60186cdeeb 100644 --- a/test/src/defaultbrowsercontext.spec.ts +++ b/test/src/defaultbrowsercontext.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('DefaultBrowserContext', function () { setupTestBrowserHooks(); @@ -57,9 +57,11 @@ describe('DefaultBrowserContext', function () { name: 'username', value: 'John Doe', }); - expect(await page.evaluate(() => document.cookie)).toBe( - 'username=John Doe' - ); + expect( + await page.evaluate(() => { + return document.cookie; + }) + ).toBe('username=John Doe'); expectCookieEquals(await page.cookies(), [ { name: 'username', diff --git a/test/src/dialog.spec.ts b/test/src/dialog.spec.ts index 39339f1fa2f..e5cd34dec5a 100644 --- a/test/src/dialog.spec.ts +++ b/test/src/dialog.spec.ts @@ -21,7 +21,7 @@ import { setupTestPageAndContextHooks, setupTestBrowserHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('Page.Events.Dialog', function () { setupTestBrowserHooks(); @@ -35,10 +35,12 @@ describe('Page.Events.Dialog', function () { }); page.on('dialog', onDialog); - await page.evaluate(() => alert('yo')); + await page.evaluate(() => { + return alert('yo'); + }); expect(onDialog.callCount).toEqual(1); - const dialog = onDialog.firstCall.args[0]; + const dialog = onDialog.firstCall.args[0]!; expect(dialog.type()).toBe('alert'); expect(dialog.defaultValue()).toBe(''); expect(dialog.message()).toBe('yo'); @@ -52,10 +54,12 @@ describe('Page.Events.Dialog', function () { }); page.on('dialog', onDialog); - const result = await page.evaluate(() => prompt('question?', 'yes.')); + const result = await page.evaluate(() => { + return prompt('question?', 'yes.'); + }); expect(onDialog.callCount).toEqual(1); - const dialog = onDialog.firstCall.args[0]; + const dialog = onDialog.firstCall.args[0]!; expect(dialog.type()).toBe('prompt'); expect(dialog.defaultValue()).toBe('yes.'); expect(dialog.message()).toBe('question?'); @@ -68,7 +72,9 @@ describe('Page.Events.Dialog', function () { page.on('dialog', (dialog) => { dialog.dismiss(); }); - const result = await page.evaluate(() => prompt('question?')); + const result = await page.evaluate(() => { + return prompt('question?'); + }); expect(result).toBe(null); }); }); diff --git a/test/src/drag-and-drop.spec.ts b/test/src/drag-and-drop.spec.ts index 01f2257f43c..d4178ba1f65 100644 --- a/test/src/drag-and-drop.spec.ts +++ b/test/src/drag-and-drop.spec.ts @@ -20,7 +20,7 @@ import { setupTestPageAndContextHooks, setupTestBrowserHooks, describeChromeOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describeChromeOnly('Input.drag', function () { setupTestBrowserHooks(); @@ -29,12 +29,14 @@ describeChromeOnly('Input.drag', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/drag-and-drop.html'); - const draggable = await page.$('#drag'); + const draggable = (await page.$('#drag'))!; try { - await draggable.drag({ x: 1, y: 1 }); + await draggable!.drag({ x: 1, y: 1 }); } catch (error) { - expect(error.message).toContain('Drag Interception is not enabled!'); + expect((error as Error).message).toContain( + 'Drag Interception is not enabled!' + ); } }); it('should emit a dragIntercepted event when dragged', async () => { @@ -44,11 +46,15 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); + const draggable = (await page.$('#drag'))!; const data = await draggable.drag({ x: 1, y: 1 }); expect(data.items.length).toBe(1); - expect(await page.evaluate(() => globalThis.didDragStart)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragStart; + }) + ).toBe(true); }); it('should emit a dragEnter', async () => { const { page, server } = getTestState(); @@ -57,13 +63,21 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); + const draggable = (await page.$('#drag'))!; const data = await draggable.drag({ x: 1, y: 1 }); - const dropzone = await page.$('#drop'); + const dropzone = (await page.$('#drop'))!; await dropzone.dragEnter(data); - expect(await page.evaluate(() => globalThis.didDragStart)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragEnter)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragStart; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragEnter; + }) + ).toBe(true); }); it('should emit a dragOver event', async () => { const { page, server } = getTestState(); @@ -72,15 +86,27 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); + const draggable = (await page.$('#drag'))!; const data = await draggable.drag({ x: 1, y: 1 }); - const dropzone = await page.$('#drop'); + const dropzone = (await page.$('#drop'))!; await dropzone.dragEnter(data); await dropzone.dragOver(data); - expect(await page.evaluate(() => globalThis.didDragStart)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragEnter)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragOver)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragStart; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragEnter; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragOver; + }) + ).toBe(true); }); it('can be dropped', async () => { const { page, server } = getTestState(); @@ -89,17 +115,33 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); - const dropzone = await page.$('#drop'); + const draggable = (await page.$('#drag'))!; + const dropzone = (await page.$('#drop'))!; const data = await draggable.drag({ x: 1, y: 1 }); await dropzone.dragEnter(data); await dropzone.dragOver(data); await dropzone.drop(data); - expect(await page.evaluate(() => globalThis.didDragStart)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragEnter)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragOver)).toBe(true); - expect(await page.evaluate(() => globalThis.didDrop)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragStart; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragEnter; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragOver; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDrop; + }) + ).toBe(true); }); it('can be dragged and dropped with a single function', async () => { const { page, server } = getTestState(); @@ -108,14 +150,30 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); - const dropzone = await page.$('#drop'); + const draggable = (await page.$('#drag'))!; + const dropzone = (await page.$('#drop'))!; await draggable.dragAndDrop(dropzone); - expect(await page.evaluate(() => globalThis.didDragStart)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragEnter)).toBe(true); - expect(await page.evaluate(() => globalThis.didDragOver)).toBe(true); - expect(await page.evaluate(() => globalThis.didDrop)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragStart; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragEnter; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDragOver; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).didDrop; + }) + ).toBe(true); }); it('can be disabled', async () => { const { page, server } = getTestState(); @@ -124,14 +182,16 @@ describeChromeOnly('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = await page.$('#drag'); + const draggable = (await page.$('#drag'))!; await draggable.drag({ x: 1, y: 1 }); await page.setDragInterception(false); try { await draggable.drag({ x: 1, y: 1 }); } catch (error) { - expect(error.message).toContain('Drag Interception is not enabled!'); + expect((error as Error).message).toContain( + 'Drag Interception is not enabled!' + ); } }); }); diff --git a/test/src/elementhandle.spec.ts b/test/src/elementhandle.spec.ts index 8e8c3c4b81d..917d0d580a1 100644 --- a/test/src/elementhandle.spec.ts +++ b/test/src/elementhandle.spec.ts @@ -22,7 +22,7 @@ import { setupTestPageAndContextHooks, describeFailsFirefox, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import utils from './utils.js'; import { ElementHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js'; @@ -37,7 +37,7 @@ describe('ElementHandle specs', function () { await page.setViewport({ width: 500, height: 500 }); await page.goto(server.PREFIX + '/grid.html'); - const elementHandle = await page.$('.box:nth-of-type(13)'); + const elementHandle = (await page.$('.box:nth-of-type(13)'))!; const box = await elementHandle.boundingBox(); expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 }); }); @@ -46,8 +46,8 @@ describe('ElementHandle specs', function () { await page.setViewport({ width: 500, height: 500 }); await page.goto(server.PREFIX + '/frames/nested-frames.html'); - const nestedFrame = page.frames()[1].childFrames()[1]; - const elementHandle = await nestedFrame.$('div'); + const nestedFrame = page.frames()[1]!.childFrames()[1]!; + const elementHandle = (await nestedFrame.$('div'))!; const box = await elementHandle.boundingBox(); if (isChrome) { expect(box).toEqual({ x: 28, y: 182, width: 264, height: 18 }); @@ -59,7 +59,7 @@ describe('ElementHandle specs', function () { const { page } = getTestState(); await page.setContent('
hi
'); - const element = await page.$('div'); + const element = (await page.$('div'))!; expect(await element.boundingBox()).toBe(null); }); it('should force a layout', async () => { @@ -69,11 +69,10 @@ describe('ElementHandle specs', function () { await page.setContent( '
hello
' ); - const elementHandle = await page.$('div'); - await page.evaluate<(element: HTMLElement) => void>( - (element) => (element.style.height = '200px'), - elementHandle - ); + const elementHandle = (await page.$('div'))!; + await page.evaluate((element: HTMLElement) => { + return (element.style.height = '200px'); + }, elementHandle); const box = await elementHandle.boundingBox(); expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 }); }); @@ -85,7 +84,7 @@ describe('ElementHandle specs', function () { `); - const element = await page.$('#therect'); + const element = (await page.$('#therect'))!; const pptrBoundingBox = await element.boundingBox(); const webBoundingBox = await page.evaluate((e: HTMLElement) => { const rect = e.getBoundingClientRect(); @@ -104,14 +103,14 @@ describe('ElementHandle specs', function () { // Step 1: Add Frame and position it absolutely. await utils.attachFrame(page, 'frame1', server.PREFIX + '/resetcss.html'); await page.evaluate(() => { - const frame = document.querySelector('#frame1'); + const frame = document.querySelector('#frame1')!; frame.style.position = 'absolute'; frame.style.left = '1px'; frame.style.top = '2px'; }); // Step 2: Add div and position it absolutely inside frame. - const frame = page.frames()[1]; + const frame = page.frames()[1]!; const divHandle = ( await frame.evaluateHandle(() => { const div = document.createElement('div'); @@ -127,25 +126,25 @@ describe('ElementHandle specs', function () { div.style.height = '7px'; return div; }) - ).asElement(); + ).asElement()!; // Step 3: query div's boxModel and assert box values. - const box = await divHandle.boxModel(); + const box = (await divHandle.boxModel())!; expect(box.width).toBe(6); expect(box.height).toBe(7); - expect(box.margin[0]).toEqual({ + expect(box.margin[0]!).toEqual({ x: 1 + 4, // frame.left + div.left y: 2 + 5, }); - expect(box.border[0]).toEqual({ + expect(box.border[0]!).toEqual({ x: 1 + 4 + 3, // frame.left + div.left + div.margin-left y: 2 + 5, }); - expect(box.padding[0]).toEqual({ + expect(box.padding[0]!).toEqual({ x: 1 + 4 + 3 + 1, // frame.left + div.left + div.marginLeft + div.borderLeft y: 2 + 5, }); - expect(box.content[0]).toEqual({ + expect(box.content[0]!).toEqual({ x: 1 + 4 + 3 + 1 + 2, // frame.left + div.left + div.marginLeft + div.borderLeft + dif.paddingLeft y: 2 + 5, }); @@ -155,7 +154,7 @@ describe('ElementHandle specs', function () { const { page } = getTestState(); await page.setContent('
hi
'); - const element = await page.$('div'); + const element = (await page.$('div'))!; expect(await element.boxModel()).toBe(null); }); }); @@ -166,9 +165,9 @@ describe('ElementHandle specs', function () { await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const elementHandle = await page.$('#frame1'); + const elementHandle = (await page.$('#frame1'))!; const frame = await elementHandle.contentFrame(); - expect(frame).toBe(page.frames()[1]); + expect(frame).toBe(page.frames()[1]!); }); }); @@ -178,57 +177,68 @@ describe('ElementHandle specs', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); + const button = (await page.$('button'))!; await button.click(); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should work for Shadow DOM v1', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/shadow.html'); - const buttonHandle = await page.evaluateHandle( + const buttonHandle = await page.evaluateHandle(() => { // @ts-expect-error button is expected to be in the page's scope. - () => button - ); + return button; + }); await buttonHandle.click(); expect( - await page.evaluate( + await page.evaluate(() => { // @ts-expect-error clicked is expected to be in the page's scope. - () => clicked - ) + return clicked; + }) ).toBe(true); }); it('should work for TextNodes', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const buttonTextNode = await page.evaluateHandle( - () => document.querySelector('button').firstChild - ); - let error = null; - await buttonTextNode.click().catch((error_) => (error = error_)); + const buttonTextNode = await page.evaluateHandle(() => { + return document.querySelector('button')!.firstChild; + }); + let error!: Error; + await buttonTextNode.click().catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Node is not of type HTMLElement'); }); it('should throw for detached nodes', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); - await page.evaluate((button: HTMLElement) => button.remove(), button); - let error = null; - await button.click().catch((error_) => (error = error_)); + const button = (await page.$('button'))!; + await page.evaluate((button: HTMLElement) => { + return button.remove(); + }, button); + let error!: Error; + await button.click().catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Node is detached from document'); }); it('should throw for hidden nodes', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); - await page.evaluate( - (button: HTMLElement) => (button.style.display = 'none'), - button - ); - const error = await button.click().catch((error_) => error_); + const button = (await page.$('button'))!; + await page.evaluate((button: HTMLElement) => { + return (button.style.display = 'none'); + }, button); + const error = await button.click().catch((error_) => { + return error_; + }); expect(error.message).toBe( 'Node is either not clickable or not an HTMLElement' ); @@ -237,12 +247,13 @@ describe('ElementHandle specs', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); - await page.evaluate( - (button: HTMLElement) => (button.parentElement.style.display = 'none'), - button - ); - const error = await button.click().catch((error_) => error_); + const button = (await page.$('button'))!; + await page.evaluate((button: HTMLElement) => { + return (button.parentElement!.style.display = 'none'); + }, button); + const error = await button.click().catch((error_) => { + return error_; + }); expect(error.message).toBe( 'Node is either not clickable or not an HTMLElement' ); @@ -251,8 +262,10 @@ describe('ElementHandle specs', function () { const { page } = getTestState(); await page.setContent('hello
goodbye'); - const br = await page.$('br'); - const error = await br.click().catch((error_) => error_); + const br = (await page.$('br'))!; + const error = await br.click().catch((error_) => { + return error_; + }); expect(error.message).toBe( 'Node is either not clickable or not an HTMLElement' ); @@ -267,17 +280,19 @@ describe('ElementHandle specs', function () { await page.setContent( '
bar2
Foo1
' ); - let element = await waitFor; + let element = (await waitFor)!; expect(element).toBeDefined(); const innerWaitFor = element.waitForSelector('.bar'); await element.evaluate((el) => { el.innerHTML = '
bar1
'; }); - element = await innerWaitFor; + element = (await innerWaitFor)!; expect(element).toBeDefined(); expect( - await element.evaluate((el: HTMLElement) => el.innerText) + await element.evaluate((el) => { + return (el as HTMLElement).innerText; + }) ).toStrictEqual('bar1'); }); }); @@ -298,14 +313,18 @@ describe('ElementHandle specs', function () { ` ); - const el2 = await page.waitForSelector('#el1'); + const el2 = (await page.waitForSelector('#el1'))!; expect( - await (await el2.waitForXPath('//div')).evaluate((el) => el.id) + await (await el2.waitForXPath('//div'))!.evaluate((el) => { + return el.id; + }) ).toStrictEqual('el2'); expect( - await (await el2.waitForXPath('.//div')).evaluate((el) => el.id) + await (await el2.waitForXPath('.//div'))!.evaluate((el) => { + return el.id; + }) ).toStrictEqual('el2'); }); }); @@ -315,10 +334,12 @@ describe('ElementHandle specs', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/scrollable.html'); - const button = await page.$('#button-6'); + const button = (await page.$('#button-6'))!; await button.hover(); expect( - await page.evaluate(() => document.querySelector('button:hover').id) + await page.evaluate(() => { + return document.querySelector('button:hover')!.id; + }) ).toBe('button-6'); }); }); @@ -329,7 +350,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/offscreenbuttons.html'); for (let i = 0; i < 11; ++i) { - const button = await page.$('#btn' + i); + const button = (await page.$('#btn' + i))!; // All but last button are visible. const visible = i < 10; expect(await button.isIntersectingViewport()).toBe(visible); @@ -341,7 +362,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/offscreenbuttons.html'); // a button almost cannot be seen // sometimes we expect to return false by isIntersectingViewport1 - const button = await page.$('#btn11'); + const button = (await page.$('#btn11'))!; expect( await button.isIntersectingViewport({ threshold: 0.001, @@ -354,7 +375,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/offscreenbuttons.html'); // a button almost cannot be seen // sometimes we expect to return false by isIntersectingViewport1 - const button = await page.$('#btn0'); + const button = (await page.$('#btn0'))!; expect( await button.isIntersectingViewport({ threshold: 1, @@ -374,15 +395,15 @@ describe('ElementHandle specs', function () { // Register. puppeteer.registerCustomQueryHandler('getById', { - queryOne: (element, selector) => - document.querySelector(`[id="${selector}"]`), + queryOne: (_element, selector) => { + return document.querySelector(`[id="${selector}"]`); + }, }); - const element = await page.$('getById/foo'); + const element = (await page.$('getById/foo'))!; expect( - await page.evaluate<(element: HTMLElement) => string>( - (element) => element.id, - element - ) + await page.evaluate<(element: HTMLElement) => string>((element) => { + return element.id; + }, element) ).toBe('foo'); const handlerNamesAfterRegistering = puppeteer.customQueryHandlerNames(); expect(handlerNamesAfterRegistering.includes('getById')).toBeTruthy(); @@ -407,7 +428,9 @@ describe('ElementHandle specs', function () { try { const { puppeteer } = getTestState(); puppeteer.registerCustomQueryHandler('1/2/3', { - queryOne: () => document.querySelector('foo'), + queryOne: () => { + return document.querySelector('foo'); + }, }); throw new Error( 'Custom query handler name was invalid - throw expected' @@ -424,18 +447,20 @@ describe('ElementHandle specs', function () { '
Foo1
Foo2
' ); puppeteer.registerCustomQueryHandler('getByClass', { - queryAll: (element, selector) => - document.querySelectorAll(`.${selector}`), + queryAll: (_element, selector) => { + return document.querySelectorAll(`.${selector}`); + }, }); const elements = await page.$$('getByClass/foo'); const classNames = await Promise.all( - elements.map( - async (element) => - await page.evaluate<(element: HTMLElement) => string>( - (element) => element.className, - element - ) - ) + elements.map(async (element) => { + return await page.evaluate<(element: HTMLElement) => string>( + (element) => { + return element.className; + }, + element + ); + }) ); expect(classNames).toStrictEqual(['foo', 'foo baz']); @@ -446,20 +471,22 @@ describe('ElementHandle specs', function () { '
Foo1
Foo2
' ); puppeteer.registerCustomQueryHandler('getByClass', { - queryAll: (element, selector) => - document.querySelectorAll(`.${selector}`), + queryAll: (_element, selector) => { + return document.querySelectorAll(`.${selector}`); + }, + }); + const elements = await page.$$eval('getByClass/foo', (divs) => { + return divs.length; }); - const elements = await page.$$eval( - 'getByClass/foo', - (divs) => divs.length - ); expect(elements).toBe(2); }); it('should wait correctly with waitForSelector', async () => { const { page, puppeteer } = getTestState(); puppeteer.registerCustomQueryHandler('getByClass', { - queryOne: (element, selector) => element.querySelector(`.${selector}`), + queryOne: (element, selector) => { + return element.querySelector(`.${selector}`); + }, }); const waitFor = page.waitForSelector('getByClass/foo'); @@ -475,7 +502,9 @@ describe('ElementHandle specs', function () { it('should wait correctly with waitForSelector on an element', async () => { const { page, puppeteer } = getTestState(); puppeteer.registerCustomQueryHandler('getByClass', { - queryOne: (element, selector) => element.querySelector(`.${selector}`), + queryOne: (element, selector) => { + return element.querySelector(`.${selector}`); + }, }); const waitFor = page.waitForSelector('getByClass/foo'); @@ -483,7 +512,7 @@ describe('ElementHandle specs', function () { await page.setContent( '
bar2
Foo1
' ); - let element = await waitFor; + let element = (await waitFor)!; expect(element).toBeDefined(); const innerWaitFor = element.waitForSelector('getByClass/bar'); @@ -492,10 +521,12 @@ describe('ElementHandle specs', function () { el.innerHTML = '
bar1
'; }); - element = await innerWaitFor; + element = (await innerWaitFor)!; expect(element).toBeDefined(); expect( - await element.evaluate((el: HTMLElement) => el.innerText) + await element.evaluate((el) => { + return (el as HTMLElement).innerText; + }) ).toStrictEqual('bar1'); }); @@ -504,7 +535,9 @@ describe('ElementHandle specs', function () { sinon.stub(console, 'warn').callsFake(() => {}); const { page, puppeteer } = getTestState(); puppeteer.registerCustomQueryHandler('getByClass', { - queryOne: (element, selector) => element.querySelector(`.${selector}`), + queryOne: (element, selector) => { + return element.querySelector(`.${selector}`); + }, }); const waitFor = page.waitFor('getByClass/foo'); @@ -522,12 +555,15 @@ describe('ElementHandle specs', function () { '
Foo2
' ); puppeteer.registerCustomQueryHandler('getByClass', { - queryOne: (element, selector) => element.querySelector(`.${selector}`), - queryAll: (element, selector) => - element.querySelectorAll(`.${selector}`), + queryOne: (element, selector) => { + return element.querySelector(`.${selector}`); + }, + queryAll: (element, selector) => { + return element.querySelectorAll(`.${selector}`); + }, }); - const element = await page.$('getByClass/foo'); + const element = (await page.$('getByClass/foo'))!; expect(element).toBeDefined(); const elements = await page.$$('getByClass/foo'); @@ -539,20 +575,26 @@ describe('ElementHandle specs', function () { '
text
content
' ); puppeteer.registerCustomQueryHandler('getByClass', { - queryOne: (element, selector) => element.querySelector(`.${selector}`), - queryAll: (element, selector) => - element.querySelectorAll(`.${selector}`), + queryOne: (element, selector) => { + return element.querySelector(`.${selector}`); + }, + queryAll: (element, selector) => { + return element.querySelectorAll(`.${selector}`); + }, }); - const txtContent = await page.$eval( - 'getByClass/foo', - (div) => div.textContent - ); + const txtContent = await page.$eval('getByClass/foo', (div) => { + return div.textContent; + }); expect(txtContent).toBe('text'); - const txtContents = await page.$$eval('getByClass/foo', (divs) => - divs.map((d) => d.textContent).join('') - ); + const txtContents = await page.$$eval('getByClass/foo', (divs) => { + return divs + .map((d) => { + return d.textContent; + }) + .join(''); + }); expect(txtContents).toBe('textcontent'); }); }); diff --git a/test/src/emulation.spec.ts b/test/src/emulation.spec.ts index ded262a77c1..c936c49246f 100644 --- a/test/src/emulation.spec.ts +++ b/test/src/emulation.spec.ts @@ -15,24 +15,25 @@ */ import expect from 'expect'; +import { Device } from '../../lib/cjs/puppeteer/common/DeviceDescriptors.js'; import { getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('Emulation', () => { setupTestBrowserHooks(); setupTestPageAndContextHooks(); - let iPhone; - let iPhoneLandscape; + let iPhone!: Device; + let iPhoneLandscape!: Device; before(() => { const { puppeteer } = getTestState(); - iPhone = puppeteer.devices['iPhone 6']; - iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']; + iPhone = puppeteer.devices['iPhone 6']!; + iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']!; }); describe('Page.viewport', function () { @@ -47,26 +48,52 @@ describe('Emulation', () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(800); + expect( + await page.evaluate(() => { + return window.innerWidth; + }) + ).toBe(800); await page.setViewport(iPhone.viewport); - expect(await page.evaluate(() => window.innerWidth)).toBe(375); + expect( + await page.evaluate(() => { + return window.innerWidth; + }) + ).toBe(375); await page.setViewport({ width: 400, height: 300 }); - expect(await page.evaluate(() => window.innerWidth)).toBe(400); + expect( + await page.evaluate(() => { + return window.innerWidth; + }) + ).toBe(400); }); it('should support touch emulation', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); + expect( + await page.evaluate(() => { + return 'ontouchstart' in window; + }) + ).toBe(false); await page.setViewport(iPhone.viewport); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); + expect( + await page.evaluate(() => { + return 'ontouchstart' in window; + }) + ).toBe(true); expect(await page.evaluate(dispatchTouch)).toBe('Received touch'); await page.setViewport({ width: 100, height: 100 }); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); + expect( + await page.evaluate(() => { + return 'ontouchstart' in window; + }) + ).toBe(false); function dispatchTouch() { - let fulfill; - const promise = new Promise((x) => (fulfill = x)); + let fulfill!: (value: string) => void; + const promise = new Promise((x) => { + fulfill = x; + }); window.ontouchstart = () => { fulfill('Received touch'); }; @@ -81,39 +108,51 @@ describe('Emulation', () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/detect-touch.html'); - expect(await page.evaluate(() => document.body.textContent.trim())).toBe( - 'NO' - ); + expect( + await page.evaluate(() => { + return document.body.textContent!.trim(); + }) + ).toBe('NO'); await page.setViewport(iPhone.viewport); await page.goto(server.PREFIX + '/detect-touch.html'); - expect(await page.evaluate(() => document.body.textContent.trim())).toBe( - 'YES' - ); + expect( + await page.evaluate(() => { + return document.body.textContent!.trim(); + }) + ).toBe('YES'); }); it('should detect touch when applying viewport with touches', async () => { const { page, server } = getTestState(); await page.setViewport({ width: 800, height: 600, hasTouch: true }); await page.addScriptTag({ url: server.PREFIX + '/modernizr.js' }); - expect(await page.evaluate(() => globalThis.Modernizr.touchevents)).toBe( - true - ); + expect( + await page.evaluate(() => { + return (globalThis as any).Modernizr.touchevents; + }) + ).toBe(true); }); itFailsFirefox('should support landscape emulation', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => screen.orientation.type)).toBe( - 'portrait-primary' - ); + expect( + await page.evaluate(() => { + return screen.orientation.type; + }) + ).toBe('portrait-primary'); await page.setViewport(iPhoneLandscape.viewport); - expect(await page.evaluate(() => screen.orientation.type)).toBe( - 'landscape-primary' - ); + expect( + await page.evaluate(() => { + return screen.orientation.type; + }) + ).toBe('landscape-primary'); await page.setViewport({ width: 100, height: 100 }); - expect(await page.evaluate(() => screen.orientation.type)).toBe( - 'portrait-primary' - ); + expect( + await page.evaluate(() => { + return screen.orientation.type; + }) + ).toBe('portrait-primary'); }); }); @@ -123,23 +162,32 @@ describe('Emulation', () => { await page.goto(server.PREFIX + '/mobile.html'); await page.emulate(iPhone); - expect(await page.evaluate(() => window.innerWidth)).toBe(375); - expect(await page.evaluate(() => navigator.userAgent)).toContain( - 'iPhone' - ); + expect( + await page.evaluate(() => { + return window.innerWidth; + }) + ).toBe(375); + expect( + await page.evaluate(() => { + return navigator.userAgent; + }) + ).toContain('iPhone'); }); itFailsFirefox('should support clicking', async () => { const { page, server } = getTestState(); await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); - await page.evaluate( - (button: HTMLElement) => (button.style.marginTop = '200px'), - button - ); + const button = (await page.$('button'))!; + await page.evaluate((button: HTMLElement) => { + return (button.style.marginTop = '200px'); + }, button); await button.click(); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); }); @@ -147,30 +195,46 @@ describe('Emulation', () => { itFailsFirefox('should work', async () => { const { page } = getTestState(); - expect(await page.evaluate(() => matchMedia('screen').matches)).toBe( - true - ); - expect(await page.evaluate(() => matchMedia('print').matches)).toBe( - false - ); + expect( + await page.evaluate(() => { + return matchMedia('screen').matches; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return matchMedia('print').matches; + }) + ).toBe(false); await page.emulateMediaType('print'); - expect(await page.evaluate(() => matchMedia('screen').matches)).toBe( - false - ); - expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true); - await page.emulateMediaType(null); - expect(await page.evaluate(() => matchMedia('screen').matches)).toBe( - true - ); - expect(await page.evaluate(() => matchMedia('print').matches)).toBe( - false - ); + expect( + await page.evaluate(() => { + return matchMedia('screen').matches; + }) + ).toBe(false); + expect( + await page.evaluate(() => { + return matchMedia('print').matches; + }) + ).toBe(true); + await page.emulateMediaType(); + expect( + await page.evaluate(() => { + return matchMedia('screen').matches; + }) + ).toBe(true); + expect( + await page.evaluate(() => { + return matchMedia('print').matches; + }) + ).toBe(false); }); it('should throw in case of bad argument', async () => { const { page } = getTestState(); - let error = null; - await page.emulateMediaType('bad').catch((error_) => (error = error_)); + let error!: Error; + await page.emulateMediaType('bad').catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Unsupported media type: bad'); }); }); @@ -183,105 +247,125 @@ describe('Emulation', () => { { name: 'prefers-reduced-motion', value: 'reduce' }, ]); expect( - await page.evaluate( - () => matchMedia('(prefers-reduced-motion: reduce)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-reduced-motion: reduce)').matches; + }) ).toBe(true); expect( - await page.evaluate( - () => matchMedia('(prefers-reduced-motion: no-preference)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-reduced-motion: no-preference)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([ { name: 'prefers-color-scheme', value: 'light' }, ]); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: light)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: light)').matches; + }) ).toBe(true); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: dark)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: dark)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([ { name: 'prefers-color-scheme', value: 'dark' }, ]); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: dark)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: dark)').matches; + }) ).toBe(true); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: light)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: light)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([ { name: 'prefers-reduced-motion', value: 'reduce' }, { name: 'prefers-color-scheme', value: 'light' }, ]); expect( - await page.evaluate( - () => matchMedia('(prefers-reduced-motion: reduce)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-reduced-motion: reduce)').matches; + }) ).toBe(true); expect( - await page.evaluate( - () => matchMedia('(prefers-reduced-motion: no-preference)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-reduced-motion: no-preference)').matches; + }) ).toBe(false); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: light)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: light)').matches; + }) ).toBe(true); expect( - await page.evaluate( - () => matchMedia('(prefers-color-scheme: dark)').matches - ) + await page.evaluate(() => { + return matchMedia('(prefers-color-scheme: dark)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([{ name: 'color-gamut', value: 'srgb' }]); expect( - await page.evaluate(() => matchMedia('(color-gamut: p3)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: p3)').matches; + }) ).toBe(false); expect( - await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: srgb)').matches; + }) ).toBe(true); expect( - await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: rec2020)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([{ name: 'color-gamut', value: 'p3' }]); expect( - await page.evaluate(() => matchMedia('(color-gamut: p3)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: p3)').matches; + }) ).toBe(true); expect( - await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: srgb)').matches; + }) ).toBe(true); expect( - await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: rec2020)').matches; + }) ).toBe(false); await page.emulateMediaFeatures([ { name: 'color-gamut', value: 'rec2020' }, ]); expect( - await page.evaluate(() => matchMedia('(color-gamut: p3)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: p3)').matches; + }) ).toBe(true); expect( - await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: srgb)').matches; + }) ).toBe(true); expect( - await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches) + await page.evaluate(() => { + return matchMedia('(color-gamut: rec2020)').matches; + }) ).toBe(true); }); it('should throw in case of bad argument', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page .emulateMediaFeatures([{ name: 'bad', value: '' }]) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Unsupported media feature: bad'); }); }); @@ -291,25 +375,37 @@ describe('Emulation', () => { const { page } = getTestState(); await page.evaluate(() => { - globalThis.date = new Date(1479579154987); + (globalThis as any).date = new Date(1479579154987); }); await page.emulateTimezone('America/Jamaica'); - expect(await page.evaluate(() => globalThis.date.toString())).toBe( - 'Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).date.toString(); + }) + ).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)'); await page.emulateTimezone('Pacific/Honolulu'); - expect(await page.evaluate(() => globalThis.date.toString())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).date.toString(); + }) + ).toBe( 'Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)' ); await page.emulateTimezone('America/Buenos_Aires'); - expect(await page.evaluate(() => globalThis.date.toString())).toBe( - 'Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).date.toString(); + }) + ).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)'); await page.emulateTimezone('Europe/Berlin'); - expect(await page.evaluate(() => globalThis.date.toString())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).date.toString(); + }) + ).toBe( 'Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)' ); }); @@ -317,10 +413,14 @@ describe('Emulation', () => { it('should throw for invalid timezone IDs', async () => { const { page } = getTestState(); - let error = null; - await page.emulateTimezone('Foo/Bar').catch((error_) => (error = error_)); + let error!: Error; + await page.emulateTimezone('Foo/Bar').catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Invalid timezone ID: Foo/Bar'); - await page.emulateTimezone('Baz/Qux').catch((error_) => (error = error_)); + await page.emulateTimezone('Baz/Qux').catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Invalid timezone ID: Baz/Qux'); }); }); @@ -378,11 +478,13 @@ describe('Emulation', () => { it('should throw for invalid vision deficiencies', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page // @ts-expect-error deliberately passign invalid deficiency .emulateVisionDeficiency('invalid') - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Unsupported vision deficiency: invalid'); }); }); @@ -391,8 +493,8 @@ describe('Emulation', () => { it('should change navigator.connection.effectiveType', async () => { const { page, puppeteer } = getTestState(); - const slow3G = puppeteer.networkConditions['Slow 3G']; - const fast3G = puppeteer.networkConditions['Fast 3G']; + const slow3G = puppeteer.networkConditions['Slow 3G']!; + const fast3G = puppeteer.networkConditions['Fast 3G']!; expect( await page.evaluate('window.navigator.connection.effectiveType') diff --git a/test/src/evaluation.spec.ts b/test/src/evaluation.spec.ts index 0935e7c46b2..bb6e3c96e76 100644 --- a/test/src/evaluation.spec.ts +++ b/test/src/evaluation.spec.ts @@ -22,7 +22,7 @@ import { setupTestPageAndContextHooks, itFailsFirefox, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; const bigint = typeof BigInt !== 'undefined'; @@ -34,55 +34,79 @@ describe('Evaluation specs', function () { it('should work', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => 7 * 3); + const result = await page.evaluate(() => { + return 7 * 3; + }); expect(result).toBe(21); }); (bigint ? it : xit)('should transfer BigInt', async () => { const { page } = getTestState(); - const result = await page.evaluate((a: bigint) => a, BigInt(42)); + const result = await page.evaluate((a: bigint) => { + return a; + }, BigInt(42)); expect(result).toBe(BigInt(42)); }); it('should transfer NaN', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, NaN); + const result = await page.evaluate((a) => { + return a; + }, NaN); expect(Object.is(result, NaN)).toBe(true); }); it('should transfer -0', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, -0); + const result = await page.evaluate((a) => { + return a; + }, -0); expect(Object.is(result, -0)).toBe(true); }); it('should transfer Infinity', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, Infinity); + const result = await page.evaluate((a) => { + return a; + }, Infinity); expect(Object.is(result, Infinity)).toBe(true); }); it('should transfer -Infinity', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, -Infinity); + const result = await page.evaluate((a) => { + return a; + }, -Infinity); expect(Object.is(result, -Infinity)).toBe(true); }); it('should transfer arrays', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a, [1, 2, 3]); + const result = await page.evaluate( + (a) => { + return a; + }, + [1, 2, 3] + ); expect(result).toEqual([1, 2, 3]); }); it('should transfer arrays as arrays, not objects', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => Array.isArray(a), [1, 2, 3]); + const result = await page.evaluate( + (a) => { + return Array.isArray(a); + }, + [1, 2, 3] + ); expect(result).toBe(true); }); it('should modify global environment', async () => { const { page } = getTestState(); - await page.evaluate(() => (globalThis.globalVar = 123)); + await page.evaluate(() => { + return ((globalThis as any).globalVar = 123); + }); expect(await page.evaluate('globalVar')).toBe(123); }); it('should evaluate in the page context', async () => { @@ -96,18 +120,22 @@ describe('Evaluation specs', function () { async () => { const { page } = getTestState(); - expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined); + expect( + await page.evaluate(() => { + return [Symbol('foo4')]; + }) + ).toBe(undefined); } ); it('should work with function shorthands', async () => { const { page } = getTestState(); const a = { - sum(a, b) { + sum(a: number, b: number) { return a + b; }, - async mult(a, b) { + async mult(a: number, b: number) { return a * b; }, }; @@ -117,27 +145,36 @@ describe('Evaluation specs', function () { it('should work with unicode chars', async () => { const { page } = getTestState(); - const result = await page.evaluate((a) => a['中文字符'], { - 中文字符: 42, - }); + const result = await page.evaluate( + (a) => { + return a['中文字符']; + }, + { + 中文字符: 42, + } + ); expect(result).toBe(42); }); itFailsFirefox('should throw when evaluation triggers reload', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page .evaluate(() => { location.reload(); return new Promise(() => {}); }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Protocol error'); }); it('should await promise', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => Promise.resolve(8 * 7)); + const result = await page.evaluate(() => { + return Promise.resolve(8 * 7); + }); expect(result).toBe(56); }); it('should work right after framenavigated', async () => { @@ -145,7 +182,9 @@ describe('Evaluation specs', function () { let frameEvaluation = null; page.on('framenavigated', async (frame) => { - frameEvaluation = frame.evaluate(() => 6 * 7); + frameEvaluation = frame.evaluate(() => { + return 6 * 7; + }); }); await page.goto(server.EMPTY_PAGE); expect(await frameEvaluation).toBe(42); @@ -154,50 +193,63 @@ describe('Evaluation specs', function () { const { page } = getTestState(); // Setup inpage callback, which calls Page.evaluate - await page.exposeFunction('callController', async function (a, b) { - return await page.evaluate<(a: number, b: number) => number>( - (a, b) => a * b, - a, - b - ); - }); + await page.exposeFunction( + 'callController', + async function (a: number, b: number) { + return await page.evaluate( + (a: number, b: number): number => { + return a * b; + }, + a, + b + ); + } + ); const result = await page.evaluate(async function () { - return await globalThis.callController(9, 3); + return await (globalThis as any).callController(9, 3); }); expect(result).toBe(27); }); it('should reject promise with exception', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page - // @ts-expect-error we know the object doesn't exist - .evaluate(() => notExistingObject.property) - .catch((error_) => (error = error_)); + .evaluate(() => { + // @ts-expect-error we know the object doesn't exist + return notExistingObject.property; + }) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain('notExistingObject'); }); it('should support thrown strings as error messages', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page .evaluate(() => { throw 'qwerty'; }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain('qwerty'); }); it('should support thrown numbers as error messages', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page .evaluate(() => { throw 100500; }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain('100500'); }); @@ -205,46 +257,60 @@ describe('Evaluation specs', function () { const { page } = getTestState(); const object = { foo: 'bar!' }; - const result = await page.evaluate((a) => a, object); + const result = await page.evaluate((a) => { + return a; + }, object); expect(result).not.toBe(object); expect(result).toEqual(object); }); (bigint ? it : xit)('should return BigInt', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => BigInt(42)); + const result = await page.evaluate(() => { + return BigInt(42); + }); expect(result).toBe(BigInt(42)); }); it('should return NaN', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => NaN); + const result = await page.evaluate(() => { + return NaN; + }); expect(Object.is(result, NaN)).toBe(true); }); it('should return -0', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => -0); + const result = await page.evaluate(() => { + return -0; + }); expect(Object.is(result, -0)).toBe(true); }); it('should return Infinity', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => Infinity); + const result = await page.evaluate(() => { + return Infinity; + }); expect(Object.is(result, Infinity)).toBe(true); }); it('should return -Infinity', async () => { const { page } = getTestState(); - const result = await page.evaluate(() => -Infinity); + const result = await page.evaluate(() => { + return -Infinity; + }); expect(Object.is(result, -Infinity)).toBe(true); }); - it('should accept "undefined" as one of multiple parameters', async () => { + it('should accept "null" as one of multiple parameters', async () => { const { page } = getTestState(); const result = await page.evaluate( - (a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), - undefined, + (a, b) => { + return Object.is(a, null) && Object.is(b, 'foo'); + }, + null, 'foo' ); expect(result).toBe(true); @@ -252,14 +318,22 @@ describe('Evaluation specs', function () { it('should properly serialize null fields', async () => { const { page } = getTestState(); - expect(await page.evaluate(() => ({ a: undefined }))).toEqual({}); + expect( + await page.evaluate(() => { + return { a: undefined }; + }) + ).toEqual({}); }); itFailsFirefox( 'should return undefined for non-serializable objects', async () => { const { page } = getTestState(); - expect(await page.evaluate(() => window)).toBe(undefined); + expect( + await page.evaluate(() => { + return window; + }) + ).toBe(undefined); } ); itFailsFirefox('should fail for circular object', async () => { @@ -268,7 +342,7 @@ describe('Evaluation specs', function () { const result = await page.evaluate(() => { const a: { [x: string]: any } = {}; const b = { a }; - a.b = b; + a['b'] = b; return a; }); expect(result).toBe(undefined); @@ -276,15 +350,21 @@ describe('Evaluation specs', function () { itFailsFirefox('should be able to throw a tricky error', async () => { const { page } = getTestState(); - const windowHandle = await page.evaluateHandle(() => window); + const windowHandle = await page.evaluateHandle(() => { + return window; + }); const errorText = await windowHandle .jsonValue() - .catch((error_) => error_.message); + .catch((error_) => { + return error_.message; + }); const error = await page .evaluate<(errorText: string) => Error>((errorText) => { throw new Error(errorText); }, errorText) - .catch((error_) => error_); + .catch((error_) => { + return error_; + }); expect(error.message).toContain(errorText); }); it('should accept a string', async () => { @@ -309,24 +389,27 @@ describe('Evaluation specs', function () { const { page } = getTestState(); await page.setContent('
42
'); - const element = await page.$('section'); - const text = await page.evaluate<(e: HTMLElement) => string>( - (e) => e.textContent, - element - ); + const element = (await page.$('section'))!; + const text = await page.evaluate((e) => { + return e.textContent; + }, element); expect(text).toBe('42'); }); it('should throw if underlying element was disposed', async () => { const { page } = getTestState(); await page.setContent('
39
'); - const element = await page.$('section'); + const element = (await page.$('section'))!; expect(element).toBeTruthy(); await element.dispose(); - let error = null; + let error!: Error; await page - .evaluate((e: HTMLElement) => e.textContent, element) - .catch((error_) => (error = error_)); + .evaluate((e: HTMLElement) => { + return e.textContent; + }, element) + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('JSHandle is disposed'); }); itFailsFirefox( @@ -335,11 +418,15 @@ describe('Evaluation specs', function () { const { page, server } = getTestState(); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const bodyHandle = await page.frames()[1].$('body'); - let error = null; + const bodyHandle = await page.frames()[1]!.$('body'); + let error!: Error; await page - .evaluate((body: HTMLElement) => body.innerHTML, bodyHandle) - .catch((error_) => (error = error_)); + .evaluate((body: HTMLElement) => { + return body.innerHTML; + }, bodyHandle) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); expect(error.message).toContain( 'JSHandles can be evaluated only in the context they were created' @@ -363,11 +450,17 @@ describe('Evaluation specs', function () { await Promise.all([ page.waitForNavigation(), - executionContext.evaluate(() => window.location.reload()), + executionContext.evaluate(() => { + return window.location.reload(); + }), ]); const error = await executionContext - .evaluate(() => null) - .catch((error_) => error_); + .evaluate(() => { + return null; + }) + .catch((error_) => { + return error_; + }); expect((error as Error).message).toContain('navigation'); }); itFailsFirefox( @@ -386,23 +479,24 @@ 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<() => string>(() => - Array(100 * 1024 * 1024 + 1).join('a') - ); + const a = await page.evaluate<() => string>(() => { + return Array(100 * 1024 * 1024 + 1).join('a'); + }); expect(a.length).toBe(100 * 1024 * 1024); }); it('should throw error with detailed information on exception inside promise ', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page - .evaluate( - () => - new Promise(() => { - throw new Error('Error in promise'); - }) - ) - .catch((error_) => (error = error_)); + .evaluate(() => { + return new Promise(() => { + throw new Error('Error in promise'); + }); + }) + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Error in promise'); }); }); @@ -412,26 +506,38 @@ describe('Evaluation specs', function () { const { page, server } = getTestState(); await page.evaluateOnNewDocument(function () { - globalThis.injected = 123; + (globalThis as any).injected = 123; }); await page.goto(server.PREFIX + '/tamperable.html'); - expect(await page.evaluate(() => globalThis.result)).toBe(123); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe(123); }); it('should work with CSP', async () => { const { page, server } = getTestState(); server.setCSP('/empty.html', 'script-src ' + server.PREFIX); await page.evaluateOnNewDocument(function () { - globalThis.injected = 123; + (globalThis as any).injected = 123; }); await page.goto(server.PREFIX + '/empty.html'); - expect(await page.evaluate(() => globalThis.injected)).toBe(123); + expect( + await page.evaluate(() => { + return (globalThis as any).injected; + }) + ).toBe(123); // Make sure CSP works. - await page - .addScriptTag({ content: 'window.e = 10;' }) - .catch((error) => void error); - expect(await page.evaluate(() => (window as any).e)).toBe(undefined); + await page.addScriptTag({ content: 'window.e = 10;' }).catch((error) => { + return void error; + }); + expect( + await page.evaluate(() => { + return (window as any).e; + }) + ).toBe(undefined); }); }); @@ -442,10 +548,22 @@ describe('Evaluation specs', function () { await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(page.frames().length).toBe(2); - await page.frames()[0].evaluate(() => (globalThis.FOO = 'foo')); - await page.frames()[1].evaluate(() => (globalThis.FOO = 'bar')); - expect(await page.frames()[0].evaluate(() => globalThis.FOO)).toBe('foo'); - expect(await page.frames()[1].evaluate(() => globalThis.FOO)).toBe('bar'); + await page.frames()[0]!.evaluate(() => { + return ((globalThis as any).FOO = 'foo'); + }); + await page.frames()[1]!.evaluate(() => { + return ((globalThis as any).FOO = 'bar'); + }); + expect( + await page.frames()[0]!.evaluate(() => { + return (globalThis as any).FOO; + }) + ).toBe('foo'); + expect( + await page.frames()[1]!.evaluate(() => { + return (globalThis as any).FOO; + }) + ).toBe('bar'); }); it('should have correct execution contexts', async () => { const { page, server } = getTestState(); @@ -453,10 +571,14 @@ describe('Evaluation specs', function () { await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expect( - await page.frames()[0].evaluate(() => document.body.textContent.trim()) + await page.frames()[0]!.evaluate(() => { + return document.body.textContent!.trim(); + }) ).toBe(''); expect( - await page.frames()[1].evaluate(() => document.body.textContent.trim()) + await page.frames()[1]!.evaluate(() => { + return document.body.textContent!.trim(); + }) ).toBe(`Hi, I'm frame`); }); it('should execute after cross-site navigation', async () => { @@ -464,13 +586,17 @@ describe('Evaluation specs', function () { await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); - expect(await mainFrame.evaluate(() => window.location.href)).toContain( - 'localhost' - ); + expect( + await mainFrame.evaluate(() => { + return window.location.href; + }) + ).toContain('localhost'); await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); - expect(await mainFrame.evaluate(() => window.location.href)).toContain( - '127' - ); + expect( + await mainFrame.evaluate(() => { + return window.location.href; + }) + ).toContain('127'); }); }); }); diff --git a/test/src/fixtures.spec.ts b/test/src/fixtures.spec.ts index ec68dbe151e..3dd75de2b21 100644 --- a/test/src/fixtures.spec.ts +++ b/test/src/fixtures.spec.ts @@ -17,7 +17,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import expect from 'expect'; -import { getTestState, itHeadlessOnly } from './mocha-utils'; // eslint-disable-line import/extensions +import { getTestState, itHeadlessOnly } from './mocha-utils.js'; import path from 'path'; @@ -36,8 +36,12 @@ describe('Fixtures', function () { puppeteerPath, JSON.stringify(options), ]); - res.stderr.on('data', (data) => (dumpioData += data.toString('utf8'))); - await new Promise((resolve) => res.on('close', resolve)); + res.stderr.on('data', (data) => { + return (dumpioData += data.toString('utf8')); + }); + await new Promise((resolve) => { + return res.on('close', resolve); + }); expect(dumpioData).toContain('message from dumpio'); }); it('should dump browser process stderr', async () => { @@ -51,8 +55,12 @@ describe('Fixtures', function () { puppeteerPath, JSON.stringify(options), ]); - res.stderr.on('data', (data) => (dumpioData += data.toString('utf8'))); - await new Promise((resolve) => res.on('close', resolve)); + res.stderr.on('data', (data) => { + return (dumpioData += data.toString('utf8')); + }); + await new Promise((resolve) => { + return res.on('close', resolve); + }); expect(dumpioData).toContain('DevTools listening on ws://'); }); it('should close the browser when the node process closes', async () => { @@ -68,10 +76,10 @@ describe('Fixtures', function () { puppeteerPath, JSON.stringify(options), ]); - let wsEndPointCallback; - const wsEndPointPromise = new Promise( - (x) => (wsEndPointCallback = x) - ); + let wsEndPointCallback: (value: string) => void; + const wsEndPointPromise = new Promise((x) => { + return (wsEndPointCallback = x); + }); let output = ''; res.stdout.on('data', (data) => { output += data; @@ -83,13 +91,17 @@ describe('Fixtures', function () { browserWSEndpoint: await wsEndPointPromise, }); const promises = [ - new Promise((resolve) => browser.once('disconnected', resolve)), - new Promise((resolve) => res.on('close', resolve)), + new Promise((resolve) => { + return browser.once('disconnected', resolve); + }), + new Promise((resolve) => { + return res.on('close', resolve); + }), ]; if (process.platform === 'win32') { execSync(`taskkill /pid ${res.pid} /T /F`); } else { - process.kill(res.pid); + process.kill(res.pid!); } await Promise.all(promises); }); diff --git a/test/src/frame.spec.ts b/test/src/frame.spec.ts index 18e6c448a32..b5f82ce6a26 100644 --- a/test/src/frame.spec.ts +++ b/test/src/frame.spec.ts @@ -14,15 +14,16 @@ * limitations under the License. */ -import utils from './utils.js'; import expect from 'expect'; +import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js'; +import { Frame } from '../../lib/cjs/puppeteer/common/FrameManager.js'; import { getTestState, + itFailsFirefox, setupTestBrowserHooks, setupTestPageAndContextHooks, - itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions -import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js'; +} from './mocha-utils.js'; +import utils, { dumpFrames } from './utils.js'; describe('Frame specs', function () { setupTestBrowserHooks(); @@ -36,8 +37,8 @@ describe('Frame specs', function () { await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(page.frames().length).toBe(2); const [frame1, frame2] = page.frames(); - const context1 = await frame1.executionContext(); - const context2 = await frame2.executionContext(); + const context1 = await frame1!.executionContext(); + const context2 = await frame2!.executionContext(); expect(context1).toBeTruthy(); expect(context2).toBeTruthy(); expect(context1 !== context2).toBeTruthy(); @@ -45,12 +46,20 @@ describe('Frame specs', function () { expect(context2.frame()).toBe(frame2); await Promise.all([ - context1.evaluate(() => (globalThis.a = 1)), - context2.evaluate(() => (globalThis.a = 2)), + context1.evaluate(() => { + return ((globalThis as any).a = 1); + }), + context2.evaluate(() => { + return ((globalThis as any).a = 2); + }), ]); const [a1, a2] = await Promise.all([ - context1.evaluate(() => globalThis.a), - context2.evaluate(() => globalThis.a), + context1.evaluate(() => { + return (globalThis as any).a; + }), + context2.evaluate(() => { + return (globalThis as any).a; + }), ]); expect(a1).toBe(1); expect(a2).toBe(2); @@ -63,7 +72,9 @@ describe('Frame specs', function () { await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); - const windowHandle = await mainFrame.evaluateHandle(() => window); + const windowHandle = await mainFrame.evaluateHandle(() => { + return window; + }); expect(windowHandle).toBeTruthy(); }); }); @@ -72,10 +83,20 @@ describe('Frame specs', function () { itFailsFirefox('should throw for detached frames', async () => { const { page, server } = getTestState(); - const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame1 = (await utils.attachFrame( + page, + 'frame1', + server.EMPTY_PAGE + ))!; await utils.detachFrame(page, 'frame1'); - let error = null; - await frame1.evaluate(() => 7 * 8).catch((error_) => (error = error_)); + let error!: Error; + await frame1 + .evaluate(() => { + return 7 * 8; + }) + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( 'Execution context is not available in detached frame' ); @@ -89,7 +110,9 @@ describe('Frame specs', function () { // This test checks if Frame.evaluate allows a readonly array to be an argument. // See https://github.com/puppeteer/puppeteer/issues/6953. const readonlyArray: readonly string[] = ['a', 'b', 'c']; - await mainFrame.evaluate((arr) => arr, readonlyArray); + await mainFrame.evaluate((arr) => { + return arr; + }, readonlyArray); }); }); @@ -98,7 +121,7 @@ describe('Frame specs', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/frames/nested-frames.html'); - expect(utils.dumpFrames(page.mainFrame())).toEqual([ + expect(dumpFrames(page.mainFrame())).toEqual([ 'http://localhost:/frames/nested-frames.html', ' http://localhost:/frames/two-frames.html (2frames)', ' http://localhost:/frames/frame.html (uno)', @@ -113,25 +136,31 @@ describe('Frame specs', function () { await page.goto(server.EMPTY_PAGE); // validate frameattached events - const attachedFrames = []; - page.on('frameattached', (frame) => attachedFrames.push(frame)); + const attachedFrames: Frame[] = []; + page.on('frameattached', (frame) => { + return attachedFrames.push(frame); + }); await utils.attachFrame(page, 'frame1', './assets/frame.html'); expect(attachedFrames.length).toBe(1); - expect(attachedFrames[0].url()).toContain('/assets/frame.html'); + expect(attachedFrames[0]!.url()).toContain('/assets/frame.html'); // validate framenavigated events - const navigatedFrames = []; - page.on('framenavigated', (frame) => navigatedFrames.push(frame)); + const navigatedFrames: Frame[] = []; + page.on('framenavigated', (frame) => { + return navigatedFrames.push(frame); + }); await utils.navigateFrame(page, 'frame1', './empty.html'); expect(navigatedFrames.length).toBe(1); - expect(navigatedFrames[0].url()).toBe(server.EMPTY_PAGE); + expect(navigatedFrames[0]!.url()).toBe(server.EMPTY_PAGE); // validate framedetached events - const detachedFrames = []; - page.on('framedetached', (frame) => detachedFrames.push(frame)); + const detachedFrames: Frame[] = []; + page.on('framedetached', (frame) => { + return detachedFrames.push(frame); + }); await utils.detachFrame(page, 'frame1'); expect(detachedFrames.length).toBe(1); - expect(detachedFrames[0].isDetached()).toBe(true); + expect(detachedFrames[0]!.isDetached()).toBe(true); } ); it('should send "framenavigated" when navigating on anchor URLs', async () => { @@ -156,8 +185,12 @@ describe('Frame specs', function () { const { page, server } = getTestState(); let hasEvents = false; - page.on('frameattached', () => (hasEvents = true)); - page.on('framedetached', () => (hasEvents = true)); + page.on('frameattached', () => { + return (hasEvents = true); + }); + page.on('framedetached', () => { + return (hasEvents = true); + }); await page.goto(server.EMPTY_PAGE); expect(hasEvents).toBe(false); }); @@ -167,9 +200,15 @@ describe('Frame specs', function () { let attachedFrames = []; let detachedFrames = []; let navigatedFrames = []; - page.on('frameattached', (frame) => attachedFrames.push(frame)); - page.on('framedetached', (frame) => detachedFrames.push(frame)); - page.on('framenavigated', (frame) => navigatedFrames.push(frame)); + page.on('frameattached', (frame) => { + return attachedFrames.push(frame); + }); + page.on('framedetached', (frame) => { + return detachedFrames.push(frame); + }); + page.on('framenavigated', (frame) => { + return navigatedFrames.push(frame); + }); await page.goto(server.PREFIX + '/frames/nested-frames.html'); expect(attachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(0); @@ -189,9 +228,15 @@ describe('Frame specs', function () { let attachedFrames = []; let detachedFrames = []; let navigatedFrames = []; - page.on('frameattached', (frame) => attachedFrames.push(frame)); - page.on('framedetached', (frame) => detachedFrames.push(frame)); - page.on('framenavigated', (frame) => navigatedFrames.push(frame)); + page.on('frameattached', (frame) => { + return attachedFrames.push(frame); + }); + page.on('framedetached', (frame) => { + return detachedFrames.push(frame); + }); + page.on('framenavigated', (frame) => { + return navigatedFrames.push(frame); + }); await page.goto(server.PREFIX + '/frames/frameset.html'); expect(attachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(0); @@ -212,11 +257,13 @@ describe('Frame specs', function () { await page.evaluate(async (url: string) => { const frame = document.createElement('iframe'); frame.src = url; - document.body.shadowRoot.appendChild(frame); - await new Promise((x) => (frame.onload = x)); + document.body.shadowRoot!.appendChild(frame); + await new Promise((x) => { + return (frame.onload = x); + }); }, server.EMPTY_PAGE); expect(page.frames().length).toBe(2); - expect(page.frames()[1].url()).toBe(server.EMPTY_PAGE); + expect(page.frames()[1]!.url()).toBe(server.EMPTY_PAGE); }); itFailsFirefox('should report frame.name()', async () => { const { page, server } = getTestState(); @@ -227,20 +274,22 @@ describe('Frame specs', function () { frame.name = 'theFrameName'; frame.src = url; document.body.appendChild(frame); - return new Promise((x) => (frame.onload = x)); + return new Promise((x) => { + return (frame.onload = x); + }); }, server.EMPTY_PAGE); - expect(page.frames()[0].name()).toBe(''); - expect(page.frames()[1].name()).toBe('theFrameId'); - expect(page.frames()[2].name()).toBe('theFrameName'); + expect(page.frames()[0]!.name()).toBe(''); + expect(page.frames()[1]!.name()).toBe('theFrameId'); + expect(page.frames()[2]!.name()).toBe('theFrameName'); }); itFailsFirefox('should report frame.parent()', async () => { const { page, server } = getTestState(); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - expect(page.frames()[0].parentFrame()).toBe(null); - expect(page.frames()[1].parentFrame()).toBe(page.mainFrame()); - expect(page.frames()[2].parentFrame()).toBe(page.mainFrame()); + expect(page.frames()[0]!.parentFrame()).toBe(null); + expect(page.frames()[1]!.parentFrame()).toBe(page.mainFrame()); + expect(page.frames()[2]!.parentFrame()).toBe(page.mainFrame()); }); itFailsFirefox( 'should report different frame instance when frame re-attaches', @@ -253,13 +302,15 @@ describe('Frame specs', function () { server.EMPTY_PAGE ); await page.evaluate(() => { - globalThis.frame = document.querySelector('#frame1'); - globalThis.frame.remove(); + (globalThis as any).frame = document.querySelector('#frame1'); + (globalThis as any).frame.remove(); }); - expect(frame1.isDetached()).toBe(true); + expect(frame1!.isDetached()).toBe(true); const [frame2] = await Promise.all([ utils.waitEvent(page, 'frameattached'), - page.evaluate(() => document.body.appendChild(globalThis.frame)), + page.evaluate(() => { + return document.body.appendChild((globalThis as any).frame); + }), ]); expect(frame2.isDetached()).toBe(false); expect(frame1).not.toBe(frame2); @@ -271,7 +322,7 @@ describe('Frame specs', function () { await page.goto(server.PREFIX + '/frames/one-frame-url-fragment.html'); expect(page.frames().length).toBe(2); - expect(page.frames()[1].url()).toBe( + expect(page.frames()[1]!.url()).toBe( server.PREFIX + '/frames/frame.html?param=value#fragment' ); }); @@ -281,11 +332,11 @@ describe('Frame specs', function () { await page.setViewport({ width: 1000, height: 1000 }); await page.goto(server.PREFIX + '/frames/lazy-frame.html'); - expect(page.frames().map((frame) => frame._hasStartedLoading)).toEqual([ - true, - true, - false, - ]); + expect( + page.frames().map((frame) => { + return frame._hasStartedLoading; + }) + ).toEqual([true, true, false]); }); }); diff --git a/test/src/golden-utils.js b/test/src/golden-utils.ts similarity index 50% rename from test/src/golden-utils.js rename to test/src/golden-utils.ts index e6e73842170..1c150dc4260 100644 --- a/test/src/golden-utils.js +++ b/test/src/golden-utils.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Copyright 2017 Google Inc. All rights reserved. * @@ -14,45 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const path = require('path'); -const fs = require('fs'); -const Diff = require('text-diff'); -const mime = require('mime'); -const PNG = require('pngjs').PNG; -const jpeg = require('jpeg-js'); -const pixelmatch = require('pixelmatch'); +import assert from 'assert'; +import diff from 'diff'; +import fs from 'fs'; +import jpeg from 'jpeg-js'; +import mime from 'mime'; +import path from 'path'; +import pixelmatch from 'pixelmatch'; +import { PNG } from 'pngjs'; -module.exports = { compare }; +interface DiffFile { + diff: string | Buffer; + ext?: string; +} -const GoldenComparators = { - 'image/png': compareImages, - 'image/jpeg': compareImages, - 'text/plain': compareText, +const GoldenComparators = new Map< + string, + ( + actualBuffer: string | Buffer, + expectedBuffer: string | Buffer, + mimeType: string + ) => DiffFile | undefined +>(); + +const addSuffix = ( + filePath: string, + suffix: string, + customExtension?: string +): string => { + const dirname = path.dirname(filePath); + const ext = path.extname(filePath); + const name = path.basename(filePath, ext); + return path.join(dirname, name + suffix + (customExtension || ext)); }; -/** - * @param {?Object} actualBuffer - * @param {!Buffer} expectedBuffer - * @param {!string} mimeType - * @returns {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} - */ -function compareImages(actualBuffer, expectedBuffer, mimeType) { - if (!actualBuffer || !(actualBuffer instanceof Buffer)) { - return { errorMessage: 'Actual result should be Buffer.' }; - } +const compareImages = ( + actualBuffer: string | Buffer, + expectedBuffer: string | Buffer, + mimeType: string +): DiffFile | undefined => { + assert(typeof actualBuffer !== 'string'); + assert(typeof expectedBuffer !== 'string'); const actual = mimeType === 'image/png' ? PNG.sync.read(actualBuffer) : jpeg.decode(actualBuffer); + const expected = mimeType === 'image/png' ? PNG.sync.read(expectedBuffer) : jpeg.decode(expectedBuffer); if (expected.width !== actual.width || expected.height !== actual.height) { - return { - errorMessage: `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px. `, - }; + throw new Error( + `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px.` + ); } const diff = new PNG({ width: expected.width, height: expected.height }); const count = pixelmatch( @@ -63,64 +78,68 @@ function compareImages(actualBuffer, expectedBuffer, mimeType) { expected.height, { threshold: 0.1 } ); - return count > 0 ? { diff: PNG.sync.write(diff) } : null; -} + return count > 0 ? { diff: PNG.sync.write(diff) } : undefined; +}; -/** - * @param {?Object} actual - * @param {!Buffer} expectedBuffer - * @returns {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} - */ -function compareText(actual, expectedBuffer) { - if (typeof actual !== 'string') { - return { errorMessage: 'Actual result should be string' }; - } +const compareText = ( + actual: string | Buffer, + expectedBuffer: string | Buffer +): DiffFile | undefined => { + assert(typeof actual === 'string'); const expected = expectedBuffer.toString('utf-8'); if (expected === actual) { - return null; + return; } - const diff = new Diff(); - const result = diff.main(expected, actual); - diff.cleanupSemantic(result); - let html = diff.prettyHtml(result); - const diffStylePath = path.join(__dirname, 'diffstyle.css'); - html = `` + html; + const result = diff.diffLines(expected, actual); + const html = result.reduce((text, change) => { + text += change.added + ? `${change.value}` + : change.removed + ? `${change.value}` + : change.value; + return text; + }, ``); return { diff: html, - diffExtension: '.html', + ext: '.html', }; -} +}; -/** - * @param {?Object} actual - * @param {string} goldenName - * @returns {!{pass: boolean, message: (undefined|string)}} - */ -function compare(goldenPath, outputPath, actual, goldenName) { +GoldenComparators.set('image/png', compareImages); +GoldenComparators.set('image/jpeg', compareImages); +GoldenComparators.set('text/plain', compareText); + +export const compare = ( + goldenPath: string, + outputPath: string, + actual: string | Buffer, + goldenName: string +): { pass: true } | { pass: false; message: string } => { goldenPath = path.normalize(goldenPath); outputPath = path.normalize(outputPath); const expectedPath = path.join(goldenPath, goldenName); const actualPath = path.join(outputPath, goldenName); - const messageSuffix = - 'Output is saved in "' + path.basename(outputPath + '" directory'); + const messageSuffix = `Output is saved in "${path.basename( + outputPath + '" directory' + )}`; if (!fs.existsSync(expectedPath)) { ensureOutputDir(); fs.writeFileSync(actualPath, actual); return { pass: false, - message: goldenName + ' is missing in golden results. ' + messageSuffix, + message: `${goldenName} is missing in golden results. ${messageSuffix}`, }; } const expected = fs.readFileSync(expectedPath); const mimeType = mime.getType(goldenName); - const comparator = GoldenComparators[mimeType]; + assert(mimeType); + const comparator = GoldenComparators.get(mimeType); if (!comparator) { return { pass: false, - message: - 'Failed to find comparator with type ' + mimeType + ': ' + goldenName, + message: `Failed to find comparator with type ${mimeType}: ${goldenName}`, }; } const result = comparator(actual, expected, mimeType); @@ -135,18 +154,14 @@ function compare(goldenPath, outputPath, actual, goldenName) { // Copy expected to the output/ folder for convenience. fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); } - if (result.diff) { - const diffPath = addSuffix(actualPath, '-diff', result.diffExtension); + if (result) { + const diffPath = addSuffix(actualPath, '-diff', result.ext); fs.writeFileSync(diffPath, result.diff); } - let message = goldenName + ' mismatch!'; - if (result.errorMessage) { - message += ' ' + result.errorMessage; - } return { pass: false, - message: message + ' ' + messageSuffix, + message: `${goldenName} mismatch! ${messageSuffix}`, }; function ensureOutputDir() { @@ -154,17 +169,4 @@ function compare(goldenPath, outputPath, actual, goldenName) { fs.mkdirSync(outputPath); } } -} - -/** - * @param {string} filePath - * @param {string} suffix - * @param {string=} customExtension - * @returns {string} - */ -function addSuffix(filePath, suffix, customExtension) { - const dirname = path.dirname(filePath); - const ext = path.extname(filePath); - const name = path.basename(filePath, ext); - return path.join(dirname, name + suffix + (customExtension || ext)); -} +}; diff --git a/test/src/headful.spec.ts b/test/src/headful.spec.ts index 1eeb67bf0c2..371ba425cd1 100644 --- a/test/src/headful.spec.ts +++ b/test/src/headful.spec.ts @@ -14,17 +14,21 @@ * limitations under the License. */ -import path from 'path'; -import os from 'os'; -import fs from 'fs'; -import { promisify } from 'util'; import expect from 'expect'; -import { - getTestState, - describeChromeOnly, - itFailsWindows, -} from './mocha-utils'; // eslint-disable-line import/extensions +import fs from 'fs'; +import os from 'os'; +import path from 'path'; import rimraf from 'rimraf'; +import { promisify } from 'util'; +import { + PuppeteerLaunchOptions, + PuppeteerNode, +} from '../../lib/cjs/puppeteer/node/Puppeteer.js'; +import { + describeChromeOnly, + getTestState, + itFailsWindows, +} from './mocha-utils.js'; const rmAsync = promisify(rimraf); const mkdtempAsync = promisify(fs.mkdtemp); @@ -39,12 +43,22 @@ describeChromeOnly('headful tests', function () { */ this.timeout(20 * 1000); - let headfulOptions; - let headlessOptions; - let extensionOptions; - let forcedOopifOptions; - let devtoolsOptions; - const browsers = []; + let headfulOptions: PuppeteerLaunchOptions | undefined; + let headlessOptions: PuppeteerLaunchOptions & { headless: boolean }; + let extensionOptions: PuppeteerLaunchOptions & { + headless: boolean; + args: string[]; + }; + let forcedOopifOptions: PuppeteerLaunchOptions & { + headless: boolean; + devtools: boolean; + args: string[]; + }; + let devtoolsOptions: PuppeteerLaunchOptions & { + headless: boolean; + devtools: boolean; + }; + const browsers: any[] = []; beforeEach(() => { const { server, defaultBrowserOptions } = getTestState(); @@ -81,7 +95,7 @@ describeChromeOnly('headful tests', function () { }); }); - async function launchBrowser(puppeteer, options) { + async function launchBrowser(puppeteer: PuppeteerNode, options: any) { const browser = await puppeteer.launch(options); browsers.push(browser); return browser; @@ -106,7 +120,9 @@ describeChromeOnly('headful tests', function () { ); const page = await browserWithExtension.newPage(); const backgroundPageTarget = await browserWithExtension.waitForTarget( - (target) => target.type() === 'background_page' + (target: { type: () => string }) => { + return target.type() === 'background_page'; + } ); await page.close(); await browserWithExtension.close(); @@ -119,11 +135,21 @@ describeChromeOnly('headful tests', function () { extensionOptions ); const backgroundPageTarget = await browserWithExtension.waitForTarget( - (target) => target.type() === 'background_page' + (target: { type: () => string }) => { + return target.type() === 'background_page'; + } ); - const page = await backgroundPageTarget.page(); - expect(await page.evaluate(() => 2 * 3)).toBe(6); - expect(await page.evaluate(() => globalThis.MAGIC)).toBe(42); + const page = (await backgroundPageTarget.page())!; + expect( + await page.evaluate(() => { + return 2 * 3; + }) + ).toBe(6); + expect( + await page.evaluate(() => { + return (globalThis as any).MAGIC; + }) + ).toBe(42); await browserWithExtension.close(); }); it('target.page() should return a DevTools page if custom isPageTarget is provided', async function () { @@ -140,18 +166,24 @@ describeChromeOnly('headful tests', function () { ); }, }); - const devtoolsPageTarget = await browser.waitForTarget( - (target) => target.type() === 'other' - ); - const page = await devtoolsPageTarget.page(); - expect(await page.evaluate(() => 2 * 3)).toBe(6); + const devtoolsPageTarget = await browser.waitForTarget((target) => { + return target.type() === 'other'; + }); + const page = (await devtoolsPageTarget.page())!; + expect( + await page.evaluate(() => { + return 2 * 3; + }) + ).toBe(6); expect(await browser.pages()).toContainEqual(page); await browser.close(); }); it('should have default url when launching browser', async function () { const { puppeteer } = getTestState(); const browser = await launchBrowser(puppeteer, extensionOptions); - const pages = (await browser.pages()).map((page) => page.url()); + const pages = (await browser.pages()).map((page: { url: () => any }) => { + return page.url(); + }); expect(pages).toEqual(['about:blank']); await browser.close(); }); @@ -169,11 +201,10 @@ describeChromeOnly('headful tests', function () { ); const headfulPage = await headfulBrowser.newPage(); await headfulPage.goto(server.EMPTY_PAGE); - await headfulPage.evaluate( - () => - (document.cookie = - 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT') - ); + await headfulPage.evaluate(() => { + return (document.cookie = + 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); + }); await headfulBrowser.close(); // Read the cookie from headless chrome const headlessBrowser = await launchBrowser( @@ -182,7 +213,9 @@ describeChromeOnly('headful tests', function () { ); const headlessPage = await headlessBrowser.newPage(); await headlessPage.goto(server.EMPTY_PAGE); - const cookie = await headlessPage.evaluate(() => document.cookie); + const cookie = await headlessPage.evaluate(() => { + return document.cookie; + }); await headlessBrowser.close(); // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 await rmAsync(userDataDir).catch(() => {}); @@ -198,17 +231,23 @@ describeChromeOnly('headful tests', function () { const page = await browser.newPage(); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(true); - page.on('request', (r) => r.respond({ body: 'YO, GOOGLE.COM' })); + page.on('request', (r: { respond: (arg0: { body: string }) => any }) => { + return r.respond({ body: 'YO, GOOGLE.COM' }); + }); await page.evaluate(() => { const frame = document.createElement('iframe'); frame.setAttribute('src', 'https://google.com/'); document.body.appendChild(frame); - return new Promise((x) => (frame.onload = x)); + return new Promise((x) => { + return (frame.onload = x); + }); }); await page.waitForSelector('iframe[src="https://google.com/"]'); const urls = page .frames() - .map((frame) => frame.url()) + .map((frame: { url: () => any }) => { + return frame.url(); + }) .sort(); expect(urls).toEqual([server.EMPTY_PAGE, 'https://google.com/']); await browser.close(); @@ -221,26 +260,32 @@ describeChromeOnly('headful tests', function () { // Setup our session listeners to observe OOPIF activity. const session = await page.target().createCDPSession(); - const networkEvents = []; - const otherSessions = []; + const networkEvents: any[] = []; + const otherSessions: any[] = []; await session.send('Target.setAutoAttach', { autoAttach: true, flatten: true, waitForDebuggerOnStart: true, }); - session.on('sessionattached', async (session) => { - otherSessions.push(session); + session.on( + 'sessionattached', + async (session: { + on: (arg0: string, arg1: (params: any) => number) => void; + send: (arg0: string) => any; + }) => { + otherSessions.push(session); - session.on('Network.requestWillBeSent', (params) => - networkEvents.push(params) - ); - await session.send('Network.enable'); - await session.send('Runtime.runIfWaitingForDebugger'); - }); + session.on('Network.requestWillBeSent', (params: any) => { + return networkEvents.push(params); + }); + await session.send('Network.enable'); + await session.send('Runtime.runIfWaitingForDebugger'); + } + ); // Navigate to the empty page and add an OOPIF iframe with at least one request. await page.goto(server.EMPTY_PAGE); - await page.evaluate((frameUrl) => { + await page.evaluate((frameUrl: string) => { const frame = document.createElement('iframe'); frame.setAttribute('src', frameUrl); document.body.appendChild(frame); @@ -255,14 +300,16 @@ describeChromeOnly('headful tests', function () { expect(otherSessions).toHaveLength(1); // Resume the iframe and trigger another request. - const iframeSession = otherSessions[0]; + const iframeSession = otherSessions[0]!; await iframeSession.send('Runtime.evaluate', { expression: `fetch('/fetch')`, awaitPromise: true, }); await browser.close(); - const requests = networkEvents.map((event) => event.request.url); + const requests = networkEvents.map((event) => { + return event.request.url; + }); expect(requests).toContain(`http://oopifdomain:${server.PORT}/fetch`); }); it('should close browser with beforeunload page', async () => { @@ -286,7 +333,9 @@ describeChromeOnly('headful tests', function () { const context = await browser.createIncognitoBrowserContext(); await Promise.all([ context.newPage(), - browser.waitForTarget((target) => target.url().includes('devtools://')), + browser.waitForTarget((target: { url: () => string | string[] }) => { + return target.url().includes('devtools://'); + }), ]); await browser.close(); }); @@ -300,20 +349,28 @@ describeChromeOnly('headful tests', function () { const page2 = await browser.newPage(); await page1.bringToFront(); - expect(await page1.evaluate(() => document.visibilityState)).toBe( - 'visible' - ); - expect(await page2.evaluate(() => document.visibilityState)).toBe( - 'hidden' - ); + expect( + await page1.evaluate(() => { + return document.visibilityState; + }) + ).toBe('visible'); + expect( + await page2.evaluate(() => { + return document.visibilityState; + }) + ).toBe('hidden'); await page2.bringToFront(); - expect(await page1.evaluate(() => document.visibilityState)).toBe( - 'hidden' - ); - expect(await page2.evaluate(() => document.visibilityState)).toBe( - 'visible' - ); + expect( + await page1.evaluate(() => { + return document.visibilityState; + }) + ).toBe('hidden'); + expect( + await page2.evaluate(() => { + return document.visibilityState; + }) + ).toBe('visible'); await page1.close(); await page2.close(); @@ -340,7 +397,7 @@ describeChromeOnly('headful tests', function () { const promises = []; for (let i = 0; i < N; ++i) { promises.push( - pages[i].screenshot({ + pages[i]!.screenshot({ clip: { x: 50 * i, y: 0, width: 50, height: 50 }, }) ); @@ -349,7 +406,11 @@ describeChromeOnly('headful tests', function () { for (let i = 0; i < N; ++i) { expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); } - await Promise.all(pages.map((page) => page.close())); + await Promise.all( + pages.map((page) => { + return page.close(); + }) + ); await browser.close(); }); diff --git a/test/src/idle_override.spec.ts b/test/src/idle_override.spec.ts index 18e7da3986c..1025c1f66dd 100644 --- a/test/src/idle_override.spec.ts +++ b/test/src/idle_override.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describeFailsFirefox('Emulate idle state', () => { setupTestBrowserHooks(); @@ -29,7 +29,7 @@ describeFailsFirefox('Emulate idle state', () => { async function getIdleState() { const { page } = getTestState(); - const stateElement = await page.$('#state'); + const stateElement = (await page.$('#state'))!; return await page.evaluate((element: HTMLElement) => { return element.innerText; }, stateElement); diff --git a/test/src/ignorehttpserrors.spec.ts b/test/src/ignorehttpserrors.spec.ts index 688cee8843e..7d1c53ee867 100644 --- a/test/src/ignorehttpserrors.spec.ts +++ b/test/src/ignorehttpserrors.spec.ts @@ -16,20 +16,26 @@ import expect from 'expect'; import { TLSSocket } from 'tls'; +import { + Browser, + BrowserContext, +} from '../../lib/cjs/puppeteer/common/Browser.js'; +import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; +import { HTTPResponse } from '../../lib/cjs/puppeteer/common/HTTPResponse.js'; import { getTestState, describeFailsFirefox, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('ignoreHTTPSErrors', function () { /* Note that this test creates its own browser rather than use * the one provided by the test set-up as we need one * with ignoreHTTPSErrors set to true */ - let browser; - let context; - let page; + let browser!: Browser; + let context: BrowserContext; + let page!: Page; before(async () => { const { defaultBrowserOptions, puppeteer } = getTestState(); @@ -42,7 +48,6 @@ describe('ignoreHTTPSErrors', function () { after(async () => { await browser.close(); - browser = null; }); beforeEach(async () => { @@ -52,8 +57,6 @@ describe('ignoreHTTPSErrors', function () { afterEach(async () => { await context.close(); - context = null; - page = null; }); describeFailsFirefox('Response.securityDetails', function () { @@ -64,10 +67,10 @@ describe('ignoreHTTPSErrors', function () { httpsServer.waitForRequest('/empty.html'), page.goto(httpsServer.EMPTY_PAGE), ]); - const securityDetails = response.securityDetails(); + const securityDetails = response!.securityDetails()!; expect(securityDetails.issuer()).toBe('puppeteer-tests'); const protocol = (serverRequest.socket as TLSSocket) - .getProtocol() + .getProtocol()! .replace('v', ' '); expect(securityDetails.protocol()).toBe(protocol); expect(securityDetails.subjectName()).toBe('puppeteer-tests'); @@ -81,24 +84,26 @@ describe('ignoreHTTPSErrors', function () { it('should be |null| for non-secure requests', async () => { const { server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.securityDetails()).toBe(null); }); it('Network redirects should report SecurityDetails', async () => { const { httpsServer } = getTestState(); httpsServer.setRedirect('/plzredirect', '/empty.html'); - const responses = []; - page.on('response', (response) => responses.push(response)); + const responses: HTTPResponse[] = []; + page.on('response', (response) => { + return responses.push(response); + }); const [serverRequest] = await Promise.all([ httpsServer.waitForRequest('/plzredirect'), page.goto(httpsServer.PREFIX + '/plzredirect'), ]); expect(responses.length).toBe(2); - expect(responses[0].status()).toBe(302); - const securityDetails = responses[0].securityDetails(); + expect(responses[0]!.status()).toBe(302); + const securityDetails = responses[0]!.securityDetails()!; const protocol = (serverRequest.socket as TLSSocket) - .getProtocol() + .getProtocol()! .replace('v', ' '); expect(securityDetails.protocol()).toBe(protocol); }); @@ -107,25 +112,27 @@ describe('ignoreHTTPSErrors', function () { it('should work', async () => { const { httpsServer } = getTestState(); - let error = null; - const response = await page - .goto(httpsServer.EMPTY_PAGE) - .catch((error_) => (error = error_)); - expect(error).toBe(null); + let error!: Error; + const response = await page.goto(httpsServer.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); + expect(error).toBeUndefined(); expect(response.ok()).toBe(true); }); itFailsFirefox('should work with request interception', async () => { const { httpsServer } = getTestState(); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); - const response = await page.goto(httpsServer.EMPTY_PAGE); + page.on('request', (request) => { + return request.continue(); + }); + const response = (await page.goto(httpsServer.EMPTY_PAGE))!; expect(response.status()).toBe(200); }); itFailsFirefox('should work with mixed content', async () => { const { server, httpsServer } = getTestState(); - httpsServer.setRoute('/mixedcontent.html', (req, res) => { + httpsServer.setRoute('/mixedcontent.html', (_req, res) => { res.end(``); }); await page.goto(httpsServer.PREFIX + '/mixedcontent.html', { @@ -134,7 +141,7 @@ describe('ignoreHTTPSErrors', function () { expect(page.frames().length).toBe(2); // Make sure blocked iframe has functional execution context // @see https://github.com/puppeteer/puppeteer/issues/2709 - expect(await page.frames()[0].evaluate('1 + 2')).toBe(3); - expect(await page.frames()[1].evaluate('2 + 3')).toBe(5); + expect(await page.frames()[0]!.evaluate('1 + 2')).toBe(3); + expect(await page.frames()[1]!.evaluate('2 + 3')).toBe(5); }); }); diff --git a/test/src/input.spec.ts b/test/src/input.spec.ts index ad96a47c7fc..f7eca8b308d 100644 --- a/test/src/input.spec.ts +++ b/test/src/input.spec.ts @@ -21,7 +21,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt'); @@ -35,33 +35,42 @@ 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'); + const input = (await page.$('input'))!; await page.evaluate((e: HTMLElement) => { - globalThis._inputEvents = []; - e.addEventListener('change', (ev) => - globalThis._inputEvents.push(ev.type) - ); - e.addEventListener('input', (ev) => - globalThis._inputEvents.push(ev.type) - ); + (globalThis as any)._inputEvents = []; + e.addEventListener('change', (ev) => { + return (globalThis as any)._inputEvents.push(ev.type); + }); + e.addEventListener('input', (ev) => { + return (globalThis as any)._inputEvents.push(ev.type); + }); }, input); await input.uploadFile(filePath); expect( - await page.evaluate((e: HTMLInputElement) => e.files[0].name, input) + await page.evaluate((e: HTMLInputElement) => { + return e.files![0]!.name; + }, input) ).toBe('file-to-upload.txt'); expect( - await page.evaluate((e: HTMLInputElement) => e.files[0].type, input) + await page.evaluate((e: HTMLInputElement) => { + return e.files![0]!.type; + }, input) ).toBe('text/plain'); - expect(await page.evaluate(() => globalThis._inputEvents)).toEqual([ - 'input', - 'change', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any)._inputEvents; + }) + ).toEqual(['input', 'change']); expect( await page.evaluate((e: HTMLInputElement) => { const reader = new FileReader(); - const promise = new Promise((fulfill) => (reader.onload = fulfill)); - reader.readAsText(e.files[0]); - return promise.then(() => reader.result); + const promise = new Promise((fulfill) => { + return (reader.onload = fulfill); + }); + reader.readAsText(e.files![0]!); + return promise.then(() => { + return reader.result; + }); }, input) ).toBe('contents of the file'); }); @@ -94,28 +103,30 @@ describe('input tests', function () { it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; - await page - .waitForFileChooser({ timeout: 1 }) - .catch((error_) => (error = error_)); + let error!: Error; + await page.waitForFileChooser({ timeout: 1 }).catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should respect default timeout when there is no custom timeout', async () => { const { page, puppeteer } = getTestState(); page.setDefaultTimeout(1); - let error = null; - await page.waitForFileChooser().catch((error_) => (error = error_)); + let error!: Error; + await page.waitForFileChooser().catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should prioritize exact timeout over default timeout', async () => { const { page, puppeteer } = getTestState(); page.setDefaultTimeout(0); - let error = null; - await page - .waitForFileChooser({ timeout: 1 }) - .catch((error_) => (error = error_)); + let error!: Error; + await page.waitForFileChooser({ timeout: 1 }).catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should work with no timeout', async () => { @@ -123,13 +134,13 @@ describe('input tests', function () { const [chooser] = await Promise.all([ page.waitForFileChooser({ timeout: 0 }), - page.evaluate(() => - setTimeout(() => { + page.evaluate(() => { + return setTimeout(() => { const el = document.createElement('input'); el.type = 'file'; el.click(); - }, 50) - ), + }, 50); + }), ]); expect(chooser).toBeTruthy(); }); @@ -140,7 +151,9 @@ describe('input tests', function () { const [fileChooser1, fileChooser2] = await Promise.all([ page.waitForFileChooser(), page.waitForFileChooser(), - page.$eval('input', (input: HTMLInputElement) => input.click()), + page.$eval('input', (input) => { + return (input as HTMLInputElement).click(); + }), ]); expect(fileChooser1 === fileChooser2).toBe(true); }); @@ -159,36 +172,43 @@ describe('input tests', function () { ]); await Promise.all([ chooser.accept([FILE_TO_UPLOAD]), - new Promise((x) => page.once('metrics', x)), + new Promise((x) => { + return page.once('metrics', x); + }), ]); expect( - await page.$eval( - 'input', - (input: HTMLInputElement) => input.files.length - ) + await page.$eval('input', (input) => { + return (input as HTMLInputElement).files!.length; + }) ).toBe(1); expect( - await page.$eval( - 'input', - (input: HTMLInputElement) => input.files[0].name - ) + await page.$eval('input', (input) => { + return (input as HTMLInputElement).files![0]!.name; + }) ).toBe('file-to-upload.txt'); }); it('should be able to read selected file', async () => { const { page } = getTestState(); await page.setContent(``); - page - .waitForFileChooser() - .then((chooser) => chooser.accept([FILE_TO_UPLOAD])); + page.waitForFileChooser().then((chooser) => { + return chooser.accept([FILE_TO_UPLOAD]); + }); expect( - await page.$eval('input', async (picker: HTMLInputElement) => { - picker.click(); - await new Promise((x) => (picker.oninput = x)); + await page.$eval('input', async (picker) => { + const pick = picker as HTMLInputElement; + pick.click(); + await new Promise((x) => { + return (pick.oninput = x); + }); const reader = new FileReader(); - const promise = new Promise((fulfill) => (reader.onload = fulfill)); - reader.readAsText(picker.files[0]); - return promise.then(() => reader.result); + const promise = new Promise((fulfill) => { + return (reader.onload = fulfill); + }); + reader.readAsText(pick.files![0]!); + return promise.then(() => { + return reader.result; + }); }) ).toBe('contents of the file'); }); @@ -196,22 +216,30 @@ describe('input tests', function () { const { page } = getTestState(); await page.setContent(``); - page - .waitForFileChooser() - .then((chooser) => chooser.accept([FILE_TO_UPLOAD])); + page.waitForFileChooser().then((chooser) => { + return chooser.accept([FILE_TO_UPLOAD]); + }); expect( - await page.$eval('input', async (picker: HTMLInputElement) => { - picker.click(); - await new Promise((x) => (picker.oninput = x)); - return picker.files.length; + await page.$eval('input', async (picker) => { + const pick = picker as HTMLInputElement; + pick.click(); + await new Promise((x) => { + return (pick.oninput = x); + }); + return pick.files!.length; }) ).toBe(1); - page.waitForFileChooser().then((chooser) => chooser.accept([])); + page.waitForFileChooser().then((chooser) => { + return chooser.accept([]); + }); expect( - await page.$eval('input', async (picker: HTMLInputElement) => { - picker.click(); - await new Promise((x) => (picker.oninput = x)); - return picker.files.length; + await page.$eval('input', async (picker) => { + const pick = picker as HTMLInputElement; + pick.click(); + await new Promise((x) => { + return (pick.oninput = x); + }); + return pick.files!.length; }) ).toBe(0); }); @@ -223,7 +251,7 @@ describe('input tests', function () { page.waitForFileChooser(), page.click('input'), ]); - let error = null; + let error!: Error; await chooser .accept([ path.relative( @@ -232,7 +260,9 @@ describe('input tests', function () { ), path.relative(process.cwd(), __dirname + '/../assets/pptr.png'), ]) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).not.toBe(null); }); it('should succeed even for non-existent files', async () => { @@ -243,27 +273,34 @@ describe('input tests', function () { page.waitForFileChooser(), page.click('input'), ]); - let error: Error | undefined; - await chooser - .accept(['file-does-not-exist.txt']) - .catch((error_) => (error = error_)); + let error!: Error; + await chooser.accept(['file-does-not-exist.txt']).catch((error_) => { + return (error = error_); + }); expect(error).toBeUndefined(); }); it('should error on read of non-existent files', async () => { const { page } = getTestState(); await page.setContent(``); - page - .waitForFileChooser() - .then((chooser) => chooser.accept(['file-does-not-exist.txt'])); + page.waitForFileChooser().then((chooser) => { + return chooser.accept(['file-does-not-exist.txt']); + }); expect( - await page.$eval('input', async (picker: HTMLInputElement) => { - picker.click(); - await new Promise((x) => (picker.oninput = x)); + await page.$eval('input', async (picker) => { + const pick = picker as HTMLInputElement; + pick.click(); + await new Promise((x) => { + return (pick.oninput = x); + }); const reader = new FileReader(); - const promise = new Promise((fulfill) => (reader.onerror = fulfill)); - reader.readAsText(picker.files[0]); - return promise.then(() => false); + const promise = new Promise((fulfill) => { + return (reader.onerror = fulfill); + }); + reader.readAsText(pick.files![0]!); + return promise.then(() => { + return false; + }); }) ).toBeFalsy(); }); @@ -273,11 +310,15 @@ describe('input tests', function () { await page.setContent(``); const [fileChooser] = await Promise.all([ page.waitForFileChooser(), - page.$eval('input', (input: HTMLInputElement) => input.click()), + page.$eval('input', (input) => { + return (input as HTMLInputElement).click(); + }), ]); await fileChooser.accept([]); - let error = null; - await fileChooser.accept([]).catch((error_) => (error = error_)); + let error!: Error; + await fileChooser.accept([]).catch((error_) => { + return (error = error_); + }); expect(error.message).toBe( 'Cannot accept FileChooser which is already handled!' ); @@ -294,13 +335,17 @@ describe('input tests', function () { await page.setContent(``); const [fileChooser1] = await Promise.all([ page.waitForFileChooser(), - page.$eval('input', (input: HTMLInputElement) => input.click()), + page.$eval('input', (input) => { + return (input as HTMLInputElement).click(); + }), ]); await fileChooser1.cancel(); // If this resolves, than we successfully canceled file chooser. await Promise.all([ page.waitForFileChooser(), - page.$eval('input', (input: HTMLInputElement) => input.click()), + page.$eval('input', (input) => { + return (input as HTMLInputElement).click(); + }), ]); }); it('should fail when canceling file chooser twice', async () => { @@ -309,15 +354,17 @@ describe('input tests', function () { await page.setContent(``); const [fileChooser] = await Promise.all([ page.waitForFileChooser(), - page.$eval('input', (input: HTMLInputElement) => input.click()), + page.$eval('input', (input) => { + return (input as HTMLElement).click(); + }), ]); await fileChooser.cancel(); - let error = null; + let error!: Error; try { fileChooser.cancel(); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe( diff --git a/test/src/jshandle.spec.ts b/test/src/jshandle.spec.ts index 9a37ac4849c..4acff01aca3 100644 --- a/test/src/jshandle.spec.ts +++ b/test/src/jshandle.spec.ts @@ -22,7 +22,7 @@ import { setupTestPageAndContextHooks, itFailsFirefox, shortWaitForArrayToHaveAtLeastNElements, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('JSHandle', function () { setupTestBrowserHooks(); @@ -32,24 +32,31 @@ describe('JSHandle', function () { it('should work', async () => { const { page } = getTestState(); - const windowHandle = await page.evaluateHandle(() => window); + const windowHandle = await page.evaluateHandle(() => { + return window; + }); expect(windowHandle).toBeTruthy(); }); it('should accept object handle as an argument', async () => { const { page } = getTestState(); - const navigatorHandle = await page.evaluateHandle(() => navigator); - const text = await page.evaluate( - (e: Navigator) => e.userAgent, - navigatorHandle - ); + const navigatorHandle = await page.evaluateHandle(() => { + return navigator; + }); + const text = await page.evaluate((e: Navigator) => { + return e.userAgent; + }, navigatorHandle); expect(text).toContain('Mozilla'); }); it('should accept object handle to primitive types', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => 5); - const isFive = await page.evaluate((e) => Object.is(e, 5), aHandle); + const aHandle = await page.evaluateHandle(() => { + return 5; + }); + const isFive = await page.evaluate((e) => { + return Object.is(e, 5); + }, aHandle); expect(isFive).toBeTruthy(); }); it('should warn about recursive objects', async () => { @@ -57,31 +64,44 @@ describe('JSHandle', function () { const test: { obj?: unknown } = {}; test.obj = test; - let error = null; + let error!: Error; await page - // @ts-expect-error we are deliberately passing a bad type here (nested object) - .evaluateHandle((opts) => opts.elem, { test }) - .catch((error_) => (error = error_)); + .evaluateHandle( + (opts) => { + return opts.elem; + }, + // @ts-expect-error we are deliberately passing a bad type here (nested object) + { test } + ) + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Recursive objects are not allowed.'); }); it('should accept object handle to unserializable value', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => Infinity); - expect(await page.evaluate((e) => Object.is(e, Infinity), aHandle)).toBe( - true - ); + const aHandle = await page.evaluateHandle(() => { + return Infinity; + }); + expect( + await page.evaluate((e) => { + return Object.is(e, Infinity); + }, aHandle) + ).toBe(true); }); it('should use the same JS wrappers', async () => { const { page } = getTestState(); const aHandle = await page.evaluateHandle(() => { - globalThis.FOO = 123; + (globalThis as any).FOO = 123; return window; }); - expect(await page.evaluate((e: { FOO: number }) => e.FOO, aHandle)).toBe( - 123 - ); + expect( + await page.evaluate((e: { FOO: number }) => { + return e.FOO; + }, aHandle) + ).toBe(123); }); }); @@ -89,11 +109,13 @@ describe('JSHandle', function () { it('should work', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => ({ - one: 1, - two: 2, - three: 3, - })); + const aHandle = await page.evaluateHandle(() => { + return { + one: 1, + two: 2, + three: 3, + }; + }); const twoHandle = await aHandle.getProperty('two'); expect(await twoHandle.jsonValue()).toEqual(2); }); @@ -101,11 +123,13 @@ describe('JSHandle', function () { it('should return a JSHandle even if the property does not exist', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => ({ - one: 1, - two: 2, - three: 3, - })); + const aHandle = await page.evaluateHandle(() => { + return { + one: 1, + two: 2, + three: 3, + }; + }); const undefinedHandle = await aHandle.getProperty('doesnotexist'); expect(undefinedHandle).toBeInstanceOf(JSHandle); expect(await undefinedHandle.jsonValue()).toBe(undefined); @@ -116,7 +140,9 @@ describe('JSHandle', function () { it('should work', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => ({ foo: 'bar' })); + const aHandle = await page.evaluateHandle(() => { + return { foo: 'bar' }; + }); const json = await aHandle.jsonValue>(); expect(json).toEqual({ foo: 'bar' }); }); @@ -124,7 +150,9 @@ describe('JSHandle', function () { it('works with jsonValues that are not objects', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => ['a', 'b']); + const aHandle = await page.evaluateHandle(() => { + return ['a', 'b']; + }); const json = await aHandle.jsonValue(); expect(json).toEqual(['a', 'b']); }); @@ -132,7 +160,9 @@ describe('JSHandle', function () { it('works with jsonValues that are primitives', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => 'foo'); + const aHandle = await page.evaluateHandle(() => { + return 'foo'; + }); const json = await aHandle.jsonValue(); expect(json).toEqual('foo'); }); @@ -140,9 +170,9 @@ describe('JSHandle', function () { itFailsFirefox('should not work with dates', async () => { const { page } = getTestState(); - const dateHandle = await page.evaluateHandle( - () => new Date('2017-09-26T00:00:00.000Z') - ); + const dateHandle = await page.evaluateHandle(() => { + return new Date('2017-09-26T00:00:00.000Z'); + }); const json = await dateHandle.jsonValue(); expect(json).toEqual({}); }); @@ -150,8 +180,10 @@ describe('JSHandle', function () { const { page, isChrome } = getTestState(); const windowHandle = await page.evaluateHandle('window'); - let error = null; - await windowHandle.jsonValue().catch((error_) => (error = error_)); + let error!: Error; + await windowHandle.jsonValue().catch((error_) => { + return (error = error_); + }); if (isChrome) { expect(error.message).toContain('Object reference chain is too long'); } else { @@ -164,11 +196,13 @@ describe('JSHandle', function () { it('should work', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => ({ - foo: 'bar', - })); + const aHandle = await page.evaluateHandle(() => { + return { + foo: 'bar', + }; + }); const properties = await aHandle.getProperties(); - const foo = properties.get('foo'); + const foo = properties.get('foo')!; expect(foo).toBeTruthy(); expect(await foo.jsonValue()).toBe('bar'); }); @@ -192,8 +226,8 @@ describe('JSHandle', function () { return new B(); }); const properties = await aHandle.getProperties(); - expect(await properties.get('a').jsonValue()).toBe('1'); - expect(await properties.get('b').jsonValue()).toBe('2'); + expect(await properties.get('a')!.jsonValue()).toBe('1'); + expect(await properties.get('b')!.jsonValue()).toBe('2'); }); }); @@ -201,14 +235,18 @@ describe('JSHandle', function () { it('should work', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => document.body); + const aHandle = await page.evaluateHandle(() => { + return document.body; + }); const element = aHandle.asElement(); expect(element).toBeTruthy(); }); it('should return null for non-elements', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => 2); + const aHandle = await page.evaluateHandle(() => { + return 2; + }); const element = aHandle.asElement(); expect(element).toBeFalsy(); }); @@ -216,16 +254,15 @@ describe('JSHandle', function () { const { page } = getTestState(); await page.setContent('
ee!
'); - const aHandle = await page.evaluateHandle( - () => document.querySelector('div').firstChild - ); + const aHandle = await page.evaluateHandle(() => { + return document.querySelector('div')!.firstChild; + }); const element = aHandle.asElement(); expect(element).toBeTruthy(); expect( - await page.evaluate( - (e: HTMLElement) => e.nodeType === Node.TEXT_NODE, - element - ) + await page.evaluate((e: HTMLElement) => { + return e.nodeType === Node.TEXT_NODE; + }, element) ); }); }); @@ -234,15 +271,21 @@ describe('JSHandle', function () { it('should work for primitives', async () => { const { page } = getTestState(); - const numberHandle = await page.evaluateHandle(() => 2); + const numberHandle = await page.evaluateHandle(() => { + return 2; + }); expect(numberHandle.toString()).toBe('JSHandle:2'); - const stringHandle = await page.evaluateHandle(() => 'a'); + const stringHandle = await page.evaluateHandle(() => { + return 'a'; + }); expect(stringHandle.toString()).toBe('JSHandle:a'); }); it('should work for complicated objects', async () => { const { page } = getTestState(); - const aHandle = await page.evaluateHandle(() => window); + const aHandle = await page.evaluateHandle(() => { + return window; + }); expect(aHandle.toString()).toBe('JSHandle@object'); }); it('should work with different subtypes', async () => { @@ -315,9 +358,11 @@ describe('JSHandle', function () { `; }); await page.evaluate(async () => { - return new Promise((resolve) => window.requestAnimationFrame(resolve)); + return new Promise((resolve) => { + return window.requestAnimationFrame(resolve); + }); }); - const divHandle = await page.$('div'); + const divHandle = (await page.$('div'))!; expect(await divHandle.clickablePoint()).toEqual({ x: 45 + 60, // margin + middle point offset y: 45 + 30, // margin + middle point offset @@ -343,10 +388,12 @@ describe('JSHandle', function () { `; }); await page.evaluate(async () => { - return new Promise((resolve) => window.requestAnimationFrame(resolve)); + return new Promise((resolve) => { + return window.requestAnimationFrame(resolve); + }); }); - const frame = page.frames()[1]; - const divHandle = await frame.$('div'); + const frame = page.frames()[1]!; + const divHandle = (await frame.$('div'))!; expect(await divHandle.clickablePoint()).toEqual({ x: 20 + 45 + 60, // iframe pos + margin + middle point offset y: 20 + 45 + 30, // iframe pos + margin + middle point offset @@ -367,7 +414,7 @@ describe('JSHandle', function () { itFailsFirefox('should work', async () => { const { page } = getTestState(); - const clicks = []; + const clicks: [x: number, y: number][] = []; await page.exposeFunction('reportClick', (x: number, y: number): void => { clicks.push([x, y]); @@ -384,7 +431,7 @@ describe('JSHandle', function () { }); }); - const divHandle = await page.$('div'); + const divHandle = (await page.$('div'))!; await divHandle.click(); await divHandle.click({ offset: { diff --git a/test/src/keyboard.spec.ts b/test/src/keyboard.spec.ts index bde9649c322..2288869e16f 100644 --- a/test/src/keyboard.spec.ts +++ b/test/src/keyboard.spec.ts @@ -22,7 +22,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import { KeyInput } from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js'; describe('Keyboard', function () { @@ -40,16 +40,20 @@ describe('Keyboard', function () { const text = 'Hello world. I am the text that was typed!'; await page.keyboard.type(text); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe(text); }); itFailsFirefox('should press the metaKey', async () => { const { page, isFirefox } = getTestState(); await page.evaluate(() => { - (window as any).keyPromise = new Promise((resolve) => - document.addEventListener('keydown', (event) => resolve(event.key)) - ); + (window as any).keyPromise = new Promise((resolve) => { + return document.addEventListener('keydown', (event) => { + return resolve(event.key); + }); + }); }); await page.keyboard.press('Meta'); expect(await page.evaluate('keyPromise')).toBe( @@ -62,14 +66,18 @@ describe('Keyboard', function () { await page.goto(server.PREFIX + '/input/textarea.html'); await page.type('textarea', 'Hello World!'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('Hello World!'); for (let i = 0; i < 'World!'.length; i++) { page.keyboard.press('ArrowLeft'); } await page.keyboard.type('inserted '); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('Hello inserted World!'); page.keyboard.down('Shift'); for (let i = 0; i < 'inserted '.length; i++) { @@ -78,26 +86,38 @@ describe('Keyboard', function () { page.keyboard.up('Shift'); await page.keyboard.press('Backspace'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('Hello World!'); }); it('should send a character with ElementHandle.press', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = await page.$('textarea'); + const textarea = (await page.$('textarea'))!; await textarea.press('a'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('a'); - await page.evaluate(() => - window.addEventListener('keydown', (e) => e.preventDefault(), true) - ); + await page.evaluate(() => { + return window.addEventListener( + 'keydown', + (e) => { + return e.preventDefault(); + }, + true + ); + }); await textarea.press('b'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('a'); }); itFailsFirefox( @@ -106,10 +126,12 @@ describe('Keyboard', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = await page.$('textarea'); + const textarea = (await page.$('textarea'))!; await textarea.press('a', { text: 'ё' }); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('ё'); } ); @@ -120,14 +142,24 @@ describe('Keyboard', function () { await page.focus('textarea'); await page.keyboard.sendCharacter('嗨'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('嗨'); - await page.evaluate(() => - window.addEventListener('keydown', (e) => e.preventDefault(), true) - ); + await page.evaluate(() => { + return window.addEventListener( + 'keydown', + (e) => { + return e.preventDefault(); + }, + true + ); + }); await page.keyboard.sendCharacter('a'); expect( - await page.evaluate(() => document.querySelector('textarea').value) + await page.evaluate(() => { + return document.querySelector('textarea')!.value; + }) ).toBe('嗨a'); }); itFailsFirefox('should report shiftKey', async () => { @@ -142,7 +174,11 @@ describe('Keyboard', function () { ]); for (const [modifierKey, modifierCode] of codeForKey) { await keyboard.down(modifierKey); - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( 'Keydown: ' + modifierKey + ' ' + @@ -156,7 +192,11 @@ describe('Keyboard', function () { await keyboard.down('!'); // Shift+! will generate a keypress if (modifierKey === 'Shift') { - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( 'Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + @@ -164,17 +204,25 @@ describe('Keyboard', function () { ']' ); } else { - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keydown: ! Digit1 49 [' + modifierKey + ']' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']'); } await keyboard.up('!'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keyup: ! Digit1 49 [' + modifierKey + ']' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']'); await keyboard.up(modifierKey); - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( 'Keyup: ' + modifierKey + ' ' + @@ -191,36 +239,52 @@ describe('Keyboard', function () { await page.goto(server.PREFIX + '/input/keyboard.html'); const keyboard = page.keyboard; await keyboard.down('Control'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keydown: Control ControlLeft 17 [Control]' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keydown: Control ControlLeft 17 [Control]'); await keyboard.down('Alt'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keydown: Alt AltLeft 18 [Alt Control]' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keydown: Alt AltLeft 18 [Alt Control]'); await keyboard.down(';'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keydown: ; Semicolon 186 [Alt Control]' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keydown: ; Semicolon 186 [Alt Control]'); await keyboard.up(';'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keyup: ; Semicolon 186 [Alt Control]' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keyup: ; Semicolon 186 [Alt Control]'); await keyboard.up('Control'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keyup: Control ControlLeft 17 [Alt]' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keyup: Control ControlLeft 17 [Alt]'); await keyboard.up('Alt'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( - 'Keyup: Alt AltLeft 18 []' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe('Keyup: Alt AltLeft 18 []'); }); it('should send proper codes while typing', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.type('!'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( [ 'Keydown: ! Digit1 49 []', 'Keypress: ! Digit1 33 33 []', @@ -228,7 +292,11 @@ describe('Keyboard', function () { ].join('\n') ); await page.keyboard.type('^'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( [ 'Keydown: ^ Digit6 54 []', 'Keypress: ^ Digit6 94 94 []', @@ -243,7 +311,11 @@ describe('Keyboard', function () { const keyboard = page.keyboard; await keyboard.down('Shift'); await page.keyboard.type('~'); - expect(await page.evaluate(() => globalThis.getResult())).toBe( + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toBe( [ 'Keydown: Shift ShiftLeft 16 [Shift]', 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode @@ -275,33 +347,59 @@ describe('Keyboard', function () { ); }); await page.keyboard.type('Hello World!'); - expect(await page.evaluate(() => globalThis.textarea.value)).toBe( - 'He Wrd!' - ); + expect( + await page.evaluate(() => { + return (globalThis as any).textarea.value; + }) + ).toBe('He Wrd!'); }); itFailsFirefox('should specify repeat property', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); await page.focus('textarea'); - await page.evaluate(() => - document - .querySelector('textarea') - .addEventListener('keydown', (e) => (globalThis.lastEvent = e), true) - ); + await page.evaluate(() => { + return document.querySelector('textarea')!.addEventListener( + 'keydown', + (e) => { + return ((globalThis as any).lastEvent = e); + }, + true + ); + }); await page.keyboard.down('a'); - expect(await page.evaluate(() => globalThis.lastEvent.repeat)).toBe(false); + expect( + await page.evaluate(() => { + return (globalThis as any).lastEvent.repeat; + }) + ).toBe(false); await page.keyboard.press('a'); - expect(await page.evaluate(() => globalThis.lastEvent.repeat)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).lastEvent.repeat; + }) + ).toBe(true); await page.keyboard.down('b'); - expect(await page.evaluate(() => globalThis.lastEvent.repeat)).toBe(false); + expect( + await page.evaluate(() => { + return (globalThis as any).lastEvent.repeat; + }) + ).toBe(false); await page.keyboard.down('b'); - expect(await page.evaluate(() => globalThis.lastEvent.repeat)).toBe(true); + expect( + await page.evaluate(() => { + return (globalThis as any).lastEvent.repeat; + }) + ).toBe(true); await page.keyboard.up('a'); await page.keyboard.down('a'); - expect(await page.evaluate(() => globalThis.lastEvent.repeat)).toBe(false); + expect( + await page.evaluate(() => { + return (globalThis as any).lastEvent.repeat; + }) + ).toBe(false); }); itFailsFirefox('should type all kinds of characters', async () => { const { page, server } = getTestState(); @@ -319,11 +417,13 @@ describe('Keyboard', function () { await page.evaluate(() => { window.addEventListener( 'keydown', - (event) => (globalThis.keyLocation = event.location), + (event) => { + return ((globalThis as any).keyLocation = event.location); + }, true ); }); - const textarea = await page.$('textarea'); + const textarea = (await page.$('textarea'))!; await textarea.press('Digit5'); expect(await page.evaluate('keyLocation')).toBe(0); @@ -343,15 +443,21 @@ describe('Keyboard', function () { let error = await page.keyboard // @ts-expect-error bad input .press('NotARealKey') - .catch((error_) => error_); + .catch((error_) => { + return error_; + }); expect(error.message).toBe('Unknown key: "NotARealKey"'); // @ts-expect-error bad input - error = await page.keyboard.press('ё').catch((error_) => error_); + error = await page.keyboard.press('ё').catch((error_) => { + return error_; + }); expect(error && error.message).toBe('Unknown key: "ё"'); // @ts-expect-error bad input - error = await page.keyboard.press('😊').catch((error_) => error_); + error = await page.keyboard.press('😊').catch((error_) => { + return error_; + }); expect(error && error.message).toBe('Unknown key: "😊"'); }); itFailsFirefox('should type emoji', async () => { @@ -360,10 +466,9 @@ describe('Keyboard', function () { await page.goto(server.PREFIX + '/input/textarea.html'); await page.type('textarea', '👹 Tokyo street Japan 🇯🇵'); expect( - await page.$eval( - 'textarea', - (textarea: HTMLInputElement) => textarea.value - ) + await page.$eval('textarea', (textarea) => { + return (textarea as HTMLInputElement).value; + }) ).toBe('👹 Tokyo street Japan 🇯🇵'); }); itFailsFirefox('should type emoji into an iframe', async () => { @@ -375,23 +480,22 @@ describe('Keyboard', function () { 'emoji-test', server.PREFIX + '/input/textarea.html' ); - const frame = page.frames()[1]; - const textarea = await frame.$('textarea'); + const frame = page.frames()[1]!; + const textarea = (await frame.$('textarea'))!; await textarea.type('👹 Tokyo street Japan 🇯🇵'); expect( - await frame.$eval( - 'textarea', - (textarea: HTMLInputElement) => textarea.value - ) + await frame.$eval('textarea', (textarea) => { + return (textarea as HTMLInputElement).value; + }) ).toBe('👹 Tokyo street Japan 🇯🇵'); }); itFailsFirefox('should press the meta key', async () => { const { page, isFirefox } = getTestState(); await page.evaluate(() => { - globalThis.result = null; + (globalThis as any).result = null; document.addEventListener('keydown', (event) => { - globalThis.result = [event.key, event.code, event.metaKey]; + (globalThis as any).result = [event.key, event.code, event.metaKey]; }); }); await page.keyboard.press('Meta'); diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index e01a5d7015b..13620065c8e 100644 --- a/test/src/launcher.spec.ts +++ b/test/src/launcher.spec.ts @@ -13,24 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import Protocol from 'devtools-protocol'; +import expect from 'expect'; import fs from 'fs'; import os from 'os'; import path from 'path'; +import rimraf from 'rimraf'; import sinon from 'sinon'; +import { TLSSocket } from 'tls'; import { promisify } from 'util'; -import Protocol from 'devtools-protocol'; +import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; +import { Product } from '../../lib/cjs/puppeteer/common/Product.js'; import { getTestState, itChromeOnly, itFailsFirefox, itFirefoxOnly, itOnlyRegularInstall, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import utils from './utils.js'; -import expect from 'expect'; -import rimraf from 'rimraf'; -import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; -import { TLSSocket } from 'tls'; const mkdtempAsync = promisify(fs.mkdtemp); const readFileAsync = promisify(fs.readFile); @@ -73,7 +74,7 @@ describe('Launcher specs', function () { expect(await browserFetcher.canDownload('100000')).toBe(false); expect(await browserFetcher.canDownload(expectedRevision)).toBe(true); - revisionInfo = await browserFetcher.download(expectedRevision); + revisionInfo = (await browserFetcher.download(expectedRevision))!; expect(revisionInfo.local).toBe(true); expect(await readFileAsync(revisionInfo.executablePath, 'utf8')).toBe( 'LINUX BINARY\n' @@ -118,7 +119,7 @@ describe('Launcher specs', function () { expect(await browserFetcher.canDownload('100000')).toBe(false); expect(await browserFetcher.canDownload(expectedVersion)).toBe(true); - revisionInfo = await browserFetcher.download(expectedVersion); + revisionInfo = (await browserFetcher.download(expectedVersion))!; expect(revisionInfo.local).toBe(true); expect(await readFileAsync(revisionInfo.executablePath, 'utf8')).toBe( 'FIREFOX LINUX BINARY\n' @@ -147,7 +148,9 @@ describe('Launcher specs', function () { const page = await remote.newPage(); const navigationPromise = page .goto(server.PREFIX + '/one-style.html', { timeout: 60000 }) - .catch((error_) => error_); + .catch((error_) => { + return error_; + }); await server.waitForRequest('/one-style.css'); remote.disconnect(); const error = await navigationPromise; @@ -170,7 +173,9 @@ describe('Launcher specs', function () { const page = await remote.newPage(); const watchdog = page .waitForSelector('div', { timeout: 60000 }) - .catch((error_) => error_); + .catch((error_) => { + return error_; + }); remote.disconnect(); const error = await watchdog; expect(error.message).toContain('Protocol error'); @@ -187,8 +192,12 @@ describe('Launcher specs', function () { }); const newPage = await remote.newPage(); const results = await Promise.all([ - newPage.waitForRequest(server.EMPTY_PAGE).catch((error) => error), - newPage.waitForResponse(server.EMPTY_PAGE).catch((error) => error), + newPage.waitForRequest(server.EMPTY_PAGE).catch((error) => { + return error; + }), + newPage.waitForResponse(server.EMPTY_PAGE).catch((error) => { + return error; + }), browser.close(), ]); for (let i = 0; i < 2; i++) { @@ -204,10 +213,14 @@ describe('Launcher specs', function () { const { defaultBrowserOptions, puppeteer } = getTestState(); const browser = await puppeteer.launch(defaultBrowserOptions); const page = await browser.newPage(); - let error = null; + let error!: Error; const neverResolves = page - .evaluate(() => new Promise(() => {})) - .catch((error_) => (error = error_)); + .evaluate(() => { + return new Promise(() => {}); + }) + .catch((error_) => { + return (error = error_); + }); await browser.close(); await neverResolves; expect(error.message).toContain('Protocol error'); @@ -215,11 +228,13 @@ describe('Launcher specs', function () { it('should reject if executable path is invalid', async () => { const { defaultBrowserOptions, puppeteer } = getTestState(); - let waitError = null; + let waitError!: Error; const options = Object.assign({}, defaultBrowserOptions, { executablePath: 'random-invalid-path', }); - await puppeteer.launch(options).catch((error) => (waitError = error)); + await puppeteer.launch(options).catch((error) => { + return (waitError = error); + }); expect(waitError.message).toContain('Failed to launch'); }); it('userDataDir option', async () => { @@ -315,13 +330,19 @@ describe('Launcher specs', function () { const browser = await puppeteer.launch(options); const page = await browser.newPage(); await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => (localStorage.hey = 'hello')); + await page.evaluate(() => { + return (localStorage['hey'] = 'hello'); + }); await browser.close(); const browser2 = await puppeteer.launch(options); const page2 = await browser2.newPage(); await page2.goto(server.EMPTY_PAGE); - expect(await page2.evaluate(() => localStorage.hey)).toBe('hello'); + expect( + await page2.evaluate(() => { + return localStorage['hey']; + }) + ).toBe('hello'); await browser2.close(); // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 await rmAsync(userDataDir).catch(() => {}); @@ -336,19 +357,20 @@ describe('Launcher specs', function () { const browser = await puppeteer.launch(options); const page = await browser.newPage(); await page.goto(server.EMPTY_PAGE); - await page.evaluate( - () => - (document.cookie = - 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT') - ); + await page.evaluate(() => { + return (document.cookie = + 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); + }); await browser.close(); const browser2 = await puppeteer.launch(options); const page2 = await browser2.newPage(); await page2.goto(server.EMPTY_PAGE); - expect(await page2.evaluate(() => document.cookie)).toBe( - 'doSomethingOnlyOnce=true' - ); + expect( + await page2.evaluate(() => { + return document.cookie; + }) + ).toBe('doSomethingOnlyOnce=true'); await browser2.close(); // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 await rmAsync(userDataDir).catch(() => {}); @@ -422,16 +444,16 @@ describe('Launcher specs', function () { const browser = await puppeteer.launch( Object.assign({}, defaultBrowserOptions, { // Ignore first and third default argument. - ignoreDefaultArgs: [defaultArgs[0], defaultArgs[2]], + ignoreDefaultArgs: [defaultArgs[0]!, defaultArgs[2]], }) ); - const spawnargs = browser.process().spawnargs; + const spawnargs = browser.process()!.spawnargs; if (!spawnargs) { throw new Error('spawnargs not present'); } - expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1); - expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1); - expect(spawnargs.indexOf(defaultArgs[2])).toBe(-1); + expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1); + expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1); + expect(spawnargs.indexOf(defaultArgs[2]!)).toBe(-1); await browser.close(); } ); @@ -444,22 +466,24 @@ describe('Launcher specs', function () { const browser = await puppeteer.launch( Object.assign({}, defaultBrowserOptions, { // Only the first argument is fixed, others are optional. - ignoreDefaultArgs: [defaultArgs[0]], + ignoreDefaultArgs: [defaultArgs[0]!], }) ); - const spawnargs = browser.process().spawnargs; + const spawnargs = browser.process()!.spawnargs; if (!spawnargs) { throw new Error('spawnargs not present'); } - expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1); - expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1); + expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1); + expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1); await browser.close(); } ); it('should have default URL when launching browser', async function () { const { defaultBrowserOptions, puppeteer } = getTestState(); const browser = await puppeteer.launch(defaultBrowserOptions); - const pages = (await browser.pages()).map((page) => page.url()); + const pages = (await browser.pages()).map((page) => { + return page.url(); + }); expect(pages).toEqual(['about:blank']); await browser.close(); }); @@ -473,7 +497,7 @@ describe('Launcher specs', function () { const browser = await puppeteer.launch(options); const pages = await browser.pages(); expect(pages.length).toBe(1); - const page = pages[0]; + const page = pages[0]!; if (page.url() !== server.EMPTY_PAGE) { await page.waitForNavigation(); } @@ -486,8 +510,10 @@ describe('Launcher specs', function () { const options = Object.assign({}, defaultBrowserOptions, { timeout: 1, }); - let error = null; - await puppeteer.launch(options).catch((error_) => (error = error_)); + let error!: Error; + await puppeteer.launch(options).catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should set the default viewport', async () => { @@ -550,8 +576,10 @@ describe('Launcher specs', function () { pipe: true, }); - let error = null; - await puppeteer.launch(options).catch((error_) => (error = error_)); + let error!: Error; + await puppeteer.launch(options).catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('either pipe or debugging port'); }); itChromeOnly( @@ -575,11 +603,11 @@ describe('Launcher specs', function () { }); describe('Puppeteer.launch', function () { - let productName; + let productName!: Product; before(async () => { const { puppeteer } = getTestState(); - productName = puppeteer._productName; + productName = puppeteer._productName!; }); after(async () => { @@ -639,11 +667,19 @@ describe('Launcher specs', function () { browserWSEndpoint: originalBrowser.wsEndpoint(), }); const page = await otherBrowser.newPage(); - expect(await page.evaluate(() => 7 * 8)).toBe(56); + expect( + await page.evaluate(() => { + return 7 * 8; + }) + ).toBe(56); otherBrowser.disconnect(); const secondPage = await originalBrowser.newPage(); - expect(await secondPage.evaluate(() => 7 * 6)).toBe(42); + expect( + await secondPage.evaluate(() => { + return 7 * 6; + }) + ).toBe(42); await originalBrowser.close(); }); it('should be able to close remote browser', async () => { @@ -670,16 +706,18 @@ describe('Launcher specs', function () { ignoreHTTPSErrors: true, }); const page = await browser.newPage(); - let error = null; + let error!: Error; const [serverRequest, response] = await Promise.all([ httpsServer.waitForRequest('/empty.html'), - page.goto(httpsServer.EMPTY_PAGE).catch((error_) => (error = error_)), + page.goto(httpsServer.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }), ]); - expect(error).toBe(null); + expect(error).toBeUndefined(); expect(response.ok()).toBe(true); expect(response.securityDetails()).toBeTruthy(); const protocol = (serverRequest.socket as TLSSocket) - .getProtocol() + .getProtocol()! .replace('v', ' '); expect(response.securityDetails().protocol()).toBe(protocol); await page.close(); @@ -700,8 +738,9 @@ describe('Launcher specs', function () { const browser = await puppeteer.connect({ browserWSEndpoint, - targetFilter: (targetInfo: Protocol.Target.TargetInfo) => - !targetInfo.url?.includes('should-be-ignored'), + targetFilter: (targetInfo: Protocol.Target.TargetInfo) => { + return !targetInfo.url?.includes('should-be-ignored'); + }, }); const pages = await browser.pages(); @@ -711,10 +750,13 @@ describe('Launcher specs', function () { await browser.disconnect(); await originalBrowser.close(); - expect(pages.map((p: Page) => p.url()).sort()).toEqual([ - 'about:blank', - server.EMPTY_PAGE, - ]); + expect( + pages + .map((p: Page) => { + return p.url(); + }) + .sort() + ).toEqual(['about:blank', server.EMPTY_PAGE]); }); itFailsFirefox( 'should be able to reconnect to a disconnected browser', @@ -729,10 +771,9 @@ describe('Launcher specs', function () { const browser = await puppeteer.connect({ browserWSEndpoint }); const pages = await browser.pages(); - const restoredPage = pages.find( - (page) => - page.url() === server.PREFIX + '/frames/nested-frames.html' - ); + const restoredPage = pages.find((page) => { + return page.url() === server.PREFIX + '/frames/nested-frames.html'; + })!; expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([ 'http://localhost:/frames/nested-frames.html', ' http://localhost:/frames/two-frames.html (2frames)', @@ -740,7 +781,11 @@ describe('Launcher specs', function () { ' http://localhost:/frames/frame.html (dos)', ' http://localhost:/frames/frame.html (aframe)', ]); - expect(await restoredPage.evaluate(() => 7 * 8)).toBe(56); + expect( + await restoredPage.evaluate(() => { + return 7 * 8; + }) + ).toBe(56); await browser.close(); } ); @@ -755,13 +800,23 @@ describe('Launcher specs', function () { browserWSEndpoint: browserOne.wsEndpoint(), }); const [page1, page2] = await Promise.all([ - new Promise((x) => - browserOne.once('targetcreated', (target) => x(target.page())) - ), + new Promise((x) => { + return browserOne.once('targetcreated', (target) => { + return x(target.page()); + }); + }), browserTwo.newPage(), ]); - expect(await page1.evaluate(() => 7 * 8)).toBe(56); - expect(await page2.evaluate(() => 7 * 6)).toBe(42); + expect( + await page1.evaluate(() => { + return 7 * 8; + }) + ).toBe(56); + expect( + await page2.evaluate(() => { + return 7 * 6; + }) + ).toBe(42); await browserOne.close(); } ); @@ -775,12 +830,14 @@ describe('Launcher specs', function () { const browserTwo = await puppeteer.connect({ browserWSEndpoint }); const pages = await browserTwo.pages(); - const pageTwo = pages.find((page) => page.url() === server.EMPTY_PAGE); + const pageTwo = pages.find((page) => { + return page.url() === server.EMPTY_PAGE; + })!; await pageTwo.reload(); const bodyHandle = await pageTwo.waitForSelector('body', { timeout: 10000, }); - await bodyHandle.dispose(); + await bodyHandle!.dispose(); await browserTwo.close(); }); }); @@ -802,13 +859,15 @@ describe('Launcher specs', function () { const sandbox = sinon.createSandbox(); beforeEach(() => { - process.env.PUPPETEER_EXECUTABLE_PATH = ''; + process.env['PUPPETEER_EXECUTABLE_PATH'] = ''; sandbox .stub(process.env, 'PUPPETEER_EXECUTABLE_PATH') .value('SOME_CUSTOM_EXECUTABLE'); }); - afterEach(() => sandbox.restore()); + afterEach(() => { + return sandbox.restore(); + }); it('its value is returned', async () => { const { puppeteer } = getTestState(); @@ -840,13 +899,15 @@ describe('Launcher specs', function () { const sandbox = sinon.createSandbox(); beforeEach(() => { - process.env.PUPPETEER_EXECUTABLE_PATH = ''; + process.env['PUPPETEER_EXECUTABLE_PATH'] = ''; sandbox .stub(process.env, 'PUPPETEER_EXECUTABLE_PATH') .value('SOME_CUSTOM_EXECUTABLE'); }); - afterEach(() => sandbox.restore()); + afterEach(() => { + return sandbox.restore(); + }); it('its value is returned', async () => { const { puppeteer } = getTestState(); @@ -888,10 +949,16 @@ describe('Launcher specs', function () { const { server, puppeteer, defaultBrowserOptions } = getTestState(); const browser = await puppeteer.launch(defaultBrowserOptions); - const events = []; - browser.on('targetcreated', () => events.push('CREATED')); - browser.on('targetchanged', () => events.push('CHANGED')); - browser.on('targetdestroyed', () => events.push('DESTROYED')); + const events: string[] = []; + browser.on('targetcreated', () => { + return events.push('CREATED'); + }); + browser.on('targetchanged', () => { + return events.push('CHANGED'); + }); + browser.on('targetdestroyed', () => { + return events.push('DESTROYED'); + }); const page = await browser.newPage(); await page.goto(server.EMPTY_PAGE); await page.close(); @@ -911,9 +978,15 @@ describe('Launcher specs', function () { let disconnectedOriginal = 0; let disconnectedRemote1 = 0; let disconnectedRemote2 = 0; - originalBrowser.on('disconnected', () => ++disconnectedOriginal); - remoteBrowser1.on('disconnected', () => ++disconnectedRemote1); - remoteBrowser2.on('disconnected', () => ++disconnectedRemote2); + originalBrowser.on('disconnected', () => { + return ++disconnectedOriginal; + }); + remoteBrowser1.on('disconnected', () => { + return ++disconnectedRemote1; + }); + remoteBrowser2.on('disconnected', () => { + return ++disconnectedRemote2; + }); await Promise.all([ utils.waitEvent(remoteBrowser2, 'disconnected'), diff --git a/test/src/mocha-utils.ts b/test/src/mocha-utils.ts index 8e92f2e0385..e23837e6b1e 100644 --- a/test/src/mocha-utils.ts +++ b/test/src/mocha-utils.ts @@ -25,12 +25,15 @@ import { Browser, BrowserContext, } from '../../lib/cjs/puppeteer/common/Browser.js'; -import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js'; import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; -import { PuppeteerNode } from '../../lib/cjs/puppeteer/node/Puppeteer.js'; +import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js'; +import { + PuppeteerLaunchOptions, + PuppeteerNode, +} from '../../lib/cjs/puppeteer/node/Puppeteer.js'; import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js'; -import utils from './utils.js'; import { TestServer } from '../../utils/testserver/lib/index.js'; +import { extendExpectWithToBeGolden } from './utils.js'; const setupServer = async () => { const assetsPath = path.join(__dirname, '../assets'); @@ -55,8 +58,9 @@ const setupServer = async () => { return { server, httpsServer }; }; -export const getTestState = (): PuppeteerTestState => - state as PuppeteerTestState; +export const getTestState = (): PuppeteerTestState => { + return state as PuppeteerTestState; +}; const product = process.env['PRODUCT'] || process.env['PUPPETEER_PRODUCT'] || 'Chromium'; @@ -127,7 +131,7 @@ const setupGoldenAssertions = (): void => { if (fs.existsSync(OUTPUT_DIR)) { rimraf.sync(OUTPUT_DIR); } - utils.extendExpectWithToBeGolden(GOLDEN_DIR, OUTPUT_DIR); + extendExpectWithToBeGolden(GOLDEN_DIR, OUTPUT_DIR); }; setupGoldenAssertions(); @@ -137,9 +141,7 @@ interface PuppeteerTestState { context: BrowserContext; page: Page; puppeteer: PuppeteerNode; - defaultBrowserOptions: { - [x: string]: any; - }; + defaultBrowserOptions: PuppeteerLaunchOptions; server: TestServer; httpsServer: TestServer; isFirefox: boolean; @@ -304,16 +306,16 @@ export const mochaHooks = { ], beforeEach: async (): Promise => { - state.server.reset(); - state.httpsServer.reset(); + state.server!.reset(); + state.httpsServer!.reset(); }, afterAll: [ async (): Promise => { - await state.server.stop(); - state.server = null; - await state.httpsServer.stop(); - state.httpsServer = null; + await state.server!.stop(); + state.server = undefined; + await state.httpsServer!.stop(); + state.httpsServer = undefined; }, ], @@ -357,6 +359,8 @@ export const shortWaitForArrayToHaveAtLeastNElements = async ( if (data.length >= minLength) { break; } - await new Promise((resolve) => setTimeout(resolve, timeout)); + await new Promise((resolve) => { + return setTimeout(resolve, timeout); + }); } }; diff --git a/test/src/mouse.spec.ts b/test/src/mouse.spec.ts index 289954ba021..1c0073aadd0 100644 --- a/test/src/mouse.spec.ts +++ b/test/src/mouse.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import { KeyInput } from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js'; interface Dimensions { @@ -31,7 +31,7 @@ interface Dimensions { } function dimensions(): Dimensions { - const rect = document.querySelector('textarea').getBoundingClientRect(); + const rect = document.querySelector('textarea')!.getBoundingClientRect(); return { x: rect.left, y: rect.top, @@ -47,7 +47,7 @@ describe('Mouse', function () { const { page } = getTestState(); await page.evaluate(() => { - globalThis.clickPromise = new Promise((resolve) => { + (globalThis as any).clickPromise = new Promise((resolve) => { document.addEventListener('click', (event) => { resolve({ type: event.type, @@ -61,9 +61,9 @@ describe('Mouse', function () { }); }); await page.mouse.click(50, 60); - const event = await page.evaluate<() => MouseEvent>( - () => globalThis.clickPromise - ); + const event = await page.evaluate<() => MouseEvent>(() => { + return (globalThis as any).clickPromise; + }); expect(event.type).toBe('click'); expect(event.detail).toBe(1); expect(event.clientX).toBe(50); @@ -96,10 +96,12 @@ describe('Mouse', function () { "This is the text that we are going to try to select. Let's see how it goes."; await page.keyboard.type(text); // Firefox needs an extra frame here after typing or it will fail to set the scrollTop - await page.evaluate(() => new Promise(requestAnimationFrame)); - await page.evaluate( - () => (document.querySelector('textarea').scrollTop = 0) - ); + await page.evaluate(() => { + return new Promise(requestAnimationFrame); + }); + await page.evaluate(() => { + return (document.querySelector('textarea')!.scrollTop = 0); + }); const { x, y } = await page.evaluate(dimensions); await page.mouse.move(x + 2, y + 2); await page.mouse.down(); @@ -107,7 +109,7 @@ describe('Mouse', function () { await page.mouse.up(); expect( await page.evaluate(() => { - const textarea = document.querySelector('textarea'); + const textarea = document.querySelector('textarea')!; return textarea.value.substring( textarea.selectionStart, textarea.selectionEnd @@ -121,15 +123,21 @@ describe('Mouse', function () { await page.goto(server.PREFIX + '/input/scrollable.html'); await page.hover('#button-6'); expect( - await page.evaluate(() => document.querySelector('button:hover').id) + await page.evaluate(() => { + return document.querySelector('button:hover')!.id; + }) ).toBe('button-6'); await page.hover('#button-2'); expect( - await page.evaluate(() => document.querySelector('button:hover').id) + await page.evaluate(() => { + return document.querySelector('button:hover')!.id; + }) ).toBe('button-2'); await page.hover('#button-91'); expect( - await page.evaluate(() => document.querySelector('button:hover').id) + await page.evaluate(() => { + return document.querySelector('button:hover')!.id; + }) ).toBe('button-91'); }); itFailsFirefox( @@ -138,10 +146,15 @@ describe('Mouse', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.evaluate(() => delete window.Node); + await page.evaluate(() => { + // @ts-expect-error Expected. + return delete window.Node; + }); await page.hover('#button-6'); expect( - await page.evaluate(() => document.querySelector('button:hover').id) + await page.evaluate(() => { + return document.querySelector('button:hover')!.id; + }) ).toBe('button-6'); } ); @@ -149,11 +162,15 @@ describe('Mouse', function () { const { page, server, isFirefox } = getTestState(); await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.evaluate(() => - document - .querySelector('#button-3') - .addEventListener('mousedown', (e) => (globalThis.lastEvent = e), true) - ); + await page.evaluate(() => { + return document.querySelector('#button-3')!.addEventListener( + 'mousedown', + (e) => { + return ((globalThis as any).lastEvent = e); + }, + true + ); + }); const modifiers = new Map([ ['Shift', 'shiftKey'], ['Control', 'ctrlKey'], @@ -162,13 +179,15 @@ describe('Mouse', function () { ]); // In Firefox, the Meta modifier only exists on Mac if (isFirefox && os.platform() !== 'darwin') { - delete modifiers['Meta']; + modifiers.delete('Meta'); } for (const [modifier, key] of modifiers) { await page.keyboard.down(modifier); await page.click('#button-3'); if ( - !(await page.evaluate((mod: string) => globalThis.lastEvent[mod], key)) + !(await page.evaluate((mod: string) => { + return (globalThis as any).lastEvent[mod]; + }, key)) ) { throw new Error(key + ' should be true'); } @@ -177,9 +196,11 @@ describe('Mouse', function () { await page.click('#button-3'); for (const [modifier, key] of modifiers) { if ( - await page.evaluate((mod: string) => globalThis.lastEvent[mod], key) + await page.evaluate((mod: string) => { + return (globalThis as any).lastEvent[mod]; + }, key) ) { - throw new Error(modifiers[modifier] + ' should be false'); + throw new Error(modifiers.get(modifier) + ' should be false'); } } }); @@ -187,8 +208,8 @@ describe('Mouse', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/wheel.html'); - const elem = await page.$('div'); - const boundingBoxBefore = await elem.boundingBox(); + const elem = (await page.$('div'))!; + const boundingBoxBefore = (await elem.boundingBox())!; expect(boundingBoxBefore).toMatchObject({ width: 115, height: 115, @@ -211,9 +232,9 @@ describe('Mouse', function () { await page.mouse.move(100, 100); await page.evaluate(() => { - globalThis.result = []; + (globalThis as any).result = []; document.addEventListener('mousemove', (event) => { - globalThis.result.push([event.clientX, event.clientY]); + (globalThis as any).result.push([event.clientX, event.clientY]); }); }); await page.mouse.move(200, 300, { steps: 5 }); @@ -234,7 +255,7 @@ describe('Mouse', function () { await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html'); await page.evaluate(() => { document.addEventListener('click', (event) => { - globalThis.result = { x: event.clientX, y: event.clientY }; + (globalThis as any).result = { x: event.clientX, y: event.clientY }; }); }); diff --git a/test/src/navigation.spec.ts b/test/src/navigation.spec.ts index 50a482c3a9e..c31626babcf 100644 --- a/test/src/navigation.spec.ts +++ b/test/src/navigation.spec.ts @@ -22,8 +22,10 @@ import { setupTestPageAndContextHooks, itFailsFirefox, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import os from 'os'; +import { ServerResponse } from 'http'; +import { HTTPRequest } from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; describe('navigation', function () { setupTestBrowserHooks(); @@ -62,31 +64,35 @@ describe('navigation', function () { it('should return response when page changes its URL after load', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/historyapi.html'); + const response = (await page.goto(server.PREFIX + '/historyapi.html'))!; expect(response.status()).toBe(200); }); itFailsFirefox('should work with subframes return 204', async () => { const { page, server } = getTestState(); - server.setRoute('/frames/frame.html', (req, res) => { + server.setRoute('/frames/frame.html', (_req, res) => { res.statusCode = 204; res.end(); }); - let error = null; + let error!: Error; await page .goto(server.PREFIX + '/frames/one-frame.html') - .catch((error_) => (error = error_)); - expect(error).toBe(null); + .catch((error_) => { + return (error = error_); + }); + expect(error).toBeUndefined(); }); itFailsFirefox('should fail when server returns 204', async () => { const { page, server, isChrome } = getTestState(); - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.statusCode = 204; res.end(); }); - let error = null; - await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); + let error!: Error; + await page.goto(server.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); expect(error).not.toBe(null); if (isChrome) { expect(error.message).toContain('net::ERR_ABORTED'); @@ -100,7 +106,7 @@ describe('navigation', function () { const response = await page.goto(server.EMPTY_PAGE, { waitUntil: 'domcontentloaded', }); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); }); it('should work when page calls history API in beforeunload', async () => { const { page, server } = getTestState(); @@ -109,12 +115,14 @@ describe('navigation', function () { await page.evaluate(() => { window.addEventListener( 'beforeunload', - () => history.replaceState(null, 'initial', window.location.href), + () => { + return history.replaceState(null, 'initial', window.location.href); + }, false ); }); const response = await page.goto(server.PREFIX + '/grid.html'); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); }); itFailsFirefox( 'should navigate to empty page with networkidle0', @@ -124,7 +132,7 @@ describe('navigation', function () { const response = await page.goto(server.EMPTY_PAGE, { waitUntil: 'networkidle0', }); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); } ); itFailsFirefox( @@ -135,14 +143,16 @@ describe('navigation', function () { const response = await page.goto(server.EMPTY_PAGE, { waitUntil: 'networkidle2', }); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); } ); itFailsFirefox('should fail when navigating to bad url', async () => { const { page, isChrome } = getTestState(); - let error = null; - await page.goto('asdfasdf').catch((error_) => (error = error_)); + let error!: Error; + await page.goto('asdfasdf').catch((error_) => { + return (error = error_); + }); if (isChrome) { expect(error.message).toContain('Cannot navigate to invalid URL'); } else { @@ -165,15 +175,21 @@ describe('navigation', function () { // Make sure that network events do not emit 'undefined'. // @see https://crbug.com/750469 - const requests = []; - page.on('request', () => requests.push('request')); - page.on('requestfinished', () => requests.push('requestfinished')); - page.on('requestfailed', () => requests.push('requestfailed')); + const requests: string[] = []; + page.on('request', () => { + return requests.push('request'); + }); + page.on('requestfinished', () => { + return requests.push('requestfinished'); + }); + page.on('requestfailed', () => { + return requests.push('requestfailed'); + }); - let error = null; - await page - .goto(httpsServer.EMPTY_PAGE) - .catch((error_) => (error = error_)); + let error!: Error; + await page.goto(httpsServer.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); if (isChrome) { expect(error.message).toContain(EXPECTED_SSL_CERT_MESSAGE); } else { @@ -181,18 +197,20 @@ describe('navigation', function () { } expect(requests.length).toBe(2); - expect(requests[0]).toBe('request'); - expect(requests[1]).toBe('requestfailed'); + expect(requests[0]!).toBe('request'); + expect(requests[1]!).toBe('requestfailed'); }); it('should fail when navigating to bad SSL after redirects', async () => { const { page, server, httpsServer, isChrome } = getTestState(); server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html'); - let error = null; + let error!: Error; await page .goto(httpsServer.PREFIX + '/redirect/1.html') - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); if (isChrome) { expect(error.message).toContain(EXPECTED_SSL_CERT_MESSAGE); } else { @@ -202,11 +220,13 @@ describe('navigation', function () { it('should throw if networkidle is passed as an option', async () => { const { page, server } = getTestState(); - let error = null; + let error!: Error; await page // @ts-expect-error purposefully passing an old option .goto(server.EMPTY_PAGE, { waitUntil: 'networkidle' }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( '"networkidle" option is no longer supported' ); @@ -214,10 +234,12 @@ describe('navigation', function () { it('should fail when main resources failed to load', async () => { const { page, isChrome } = getTestState(); - let error = null; + let error!: Error; await page .goto('http://localhost:44123/non-existing-url') - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); if (isChrome) { expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); } else { @@ -229,10 +251,12 @@ describe('navigation', function () { // Hang for request to the empty.html server.setRoute('/empty.html', () => {}); - let error = null; + let error!: Error; await page .goto(server.PREFIX + '/empty.html', { timeout: 1 }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); @@ -241,11 +265,11 @@ describe('navigation', function () { // Hang for request to the empty.html server.setRoute('/empty.html', () => {}); - let error = null; + let error!: Error; page.setDefaultNavigationTimeout(1); - await page - .goto(server.PREFIX + '/empty.html') - .catch((error_) => (error = error_)); + await page.goto(server.PREFIX + '/empty.html').catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); @@ -254,11 +278,11 @@ describe('navigation', function () { // Hang for request to the empty.html server.setRoute('/empty.html', () => {}); - let error = null; + let error!: Error; page.setDefaultTimeout(1); - await page - .goto(server.PREFIX + '/empty.html') - .catch((error_) => (error = error_)); + await page.goto(server.PREFIX + '/empty.html').catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); @@ -267,43 +291,47 @@ describe('navigation', function () { // Hang for request to the empty.html server.setRoute('/empty.html', () => {}); - let error = null; + let error!: Error; page.setDefaultTimeout(0); page.setDefaultNavigationTimeout(1); - await page - .goto(server.PREFIX + '/empty.html') - .catch((error_) => (error = error_)); + await page.goto(server.PREFIX + '/empty.html').catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should disable timeout when its set to 0', async () => { const { page, server } = getTestState(); - let error = null; + let error!: Error; let loaded = false; - page.once('load', () => (loaded = true)); + page.once('load', () => { + return (loaded = true); + }); await page .goto(server.PREFIX + '/grid.html', { timeout: 0, waitUntil: ['load'] }) - .catch((error_) => (error = error_)); - expect(error).toBe(null); + .catch((error_) => { + return (error = error_); + }); + expect(error).toBeUndefined(); expect(loaded).toBe(true); }); it('should work when navigating to valid url', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.ok()).toBe(true); }); itFailsFirefox('should work when navigating to data url', async () => { const { page } = getTestState(); - const response = await page.goto('data:text/html,hello'); + const response = (await page.goto('data:text/html,hello'))!; expect(response.ok()).toBe(true); }); it('should work when navigating to 404', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/not-found'); + const response = (await page.goto(server.PREFIX + '/not-found'))!; expect(response.ok()).toBe(false); expect(response.status()).toBe(404); }); @@ -313,7 +341,7 @@ describe('navigation', function () { server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/redirect/3.html'); server.setRedirect('/redirect/3.html', server.EMPTY_PAGE); - const response = await page.goto(server.PREFIX + '/redirect/1.html'); + const response = (await page.goto(server.PREFIX + '/redirect/1.html'))!; expect(response.ok()).toBe(true); expect(response.url()).toBe(server.EMPTY_PAGE); }); @@ -322,20 +350,20 @@ describe('navigation', function () { async () => { const { page, server } = getTestState(); - let responses = []; + let responses: ServerResponse[] = []; // Hold on to a bunch of requests without answering. - server.setRoute('/fetch-request-a.js', (req, res) => - responses.push(res) - ); - server.setRoute('/fetch-request-b.js', (req, res) => - responses.push(res) - ); - server.setRoute('/fetch-request-c.js', (req, res) => - responses.push(res) - ); - server.setRoute('/fetch-request-d.js', (req, res) => - responses.push(res) - ); + server.setRoute('/fetch-request-a.js', (_req, res) => { + return responses.push(res); + }); + server.setRoute('/fetch-request-b.js', (_req, res) => { + return responses.push(res); + }); + server.setRoute('/fetch-request-c.js', (_req, res) => { + return responses.push(res); + }); + server.setRoute('/fetch-request-d.js', (_req, res) => { + return responses.push(res); + }); const initialFetchResourcesRequested = Promise.all([ server.waitForRequest('/fetch-request-a.js'), server.waitForRequest('/fetch-request-b.js'), @@ -355,10 +383,14 @@ describe('navigation', function () { ); // Track when the navigation gets completed. let navigationFinished = false; - navigationPromise.then(() => (navigationFinished = true)); + navigationPromise.then(() => { + return (navigationFinished = true); + }); // Wait for the page's 'load' event. - await new Promise((fulfill) => page.once('load', fulfill)); + await new Promise((fulfill) => { + return page.once('load', fulfill); + }); expect(navigationFinished).toBe(false); // Wait for the initial three resources to be requested. @@ -387,7 +419,7 @@ describe('navigation', function () { response.end(`File not found`); } - const response = await navigationPromise; + const response = (await navigationPromise)!; // Expect navigation to succeed. expect(response.ok()).toBe(true); } @@ -396,7 +428,9 @@ describe('navigation', function () { const { page, server } = getTestState(); let warning = null; - const warningHandler = (w) => (warning = w); + const warningHandler: NodeJS.WarningListener = (w) => { + return (warning = w); + }; process.on('warning', warningHandler); for (let i = 0; i < 20; ++i) { await page.goto(server.EMPTY_PAGE); @@ -408,7 +442,9 @@ describe('navigation', function () { const { page } = getTestState(); let warning = null; - const warningHandler = (w) => (warning = w); + const warningHandler: NodeJS.WarningListener = (w) => { + return (warning = w); + }; process.on('warning', warningHandler); for (let i = 0; i < 20; ++i) { await page.goto('asdf').catch(() => { @@ -422,7 +458,9 @@ describe('navigation', function () { const { context, server } = getTestState(); let warning = null; - const warningHandler = (w) => (warning = w); + const warningHandler: NodeJS.WarningListener = (w) => { + return (warning = w); + }; process.on('warning', warningHandler); await Promise.all( [...Array(20)].map(async () => { @@ -439,16 +477,15 @@ describe('navigation', function () { async () => { const { page } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); const dataURL = 'data:text/html,
yo
'; - const response = await page.goto(dataURL); + const response = (await page.goto(dataURL))!; expect(response.status()).toBe(200); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); + expect(requests[0]!.url()).toBe(dataURL); } ); itFailsFirefox( @@ -456,22 +493,21 @@ describe('navigation', function () { async () => { const { page, server } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - const response = await page.goto(server.EMPTY_PAGE + '#hash'); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!; expect(response.status()).toBe(200); expect(response.url()).toBe(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); } ); it('should work with self requesting page', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/self-request.html'); + const response = (await page.goto(server.PREFIX + '/self-request.html'))!; expect(response.status()).toBe(200); expect(response.url()).toContain('self-request.html'); }); @@ -479,11 +515,11 @@ describe('navigation', function () { const { page, httpsServer } = getTestState(); const url = httpsServer.PREFIX + '/redirect/1.html'; - let error = null; + let error!: Error; try { await page.goto(url); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toContain(url); }); @@ -510,19 +546,20 @@ describe('navigation', function () { await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForNavigation(), - page.evaluate( - (url: string) => (window.location.href = url), - server.PREFIX + '/grid.html' - ), + page.evaluate((url: string) => { + return (window.location.href = url); + }, server.PREFIX + '/grid.html'), ]); - expect(response.ok()).toBe(true); - expect(response.url()).toContain('grid.html'); + expect(response!.ok()).toBe(true); + expect(response!.url()).toContain('grid.html'); }); it('should work with both domcontentloaded and load', async () => { const { page, server } = getTestState(); - let response = null; - server.setRoute('/one-style.css', (req, res) => (response = res)); + let response!: ServerResponse; + server.setRoute('/one-style.css', (_req, res) => { + return (response = res); + }); const navigationPromise = page.goto(server.PREFIX + '/one-style.html'); const domContentLoadedPromise = page.waitForNavigation({ waitUntil: 'domcontentloaded', @@ -533,7 +570,9 @@ describe('navigation', function () { .waitForNavigation({ waitUntil: ['load', 'domcontentloaded'], }) - .then(() => (bothFired = true)); + .then(() => { + return (bothFired = true); + }); await server.waitForRequest('/one-style.css'); await domContentLoadedPromise; @@ -637,7 +676,9 @@ describe('navigation', function () { }); }); await Promise.all([ - frame.evaluate(() => window.stop()), + frame.evaluate(() => { + return window.stop(); + }), navigationPromise, ]); } @@ -651,15 +692,15 @@ describe('navigation', function () { await page.goto(server.EMPTY_PAGE); await page.goto(server.PREFIX + '/grid.html'); - let response = await page.goBack(); + let response = (await page.goBack())!; expect(response.ok()).toBe(true); expect(response.url()).toContain(server.EMPTY_PAGE); - response = await page.goForward(); + response = (await page.goForward())!; expect(response.ok()).toBe(true); expect(response.url()).toContain('/grid.html'); - response = await page.goForward(); + response = (await page.goForward())!; expect(response).toBe(null); }); itFailsFirefox('should work with HistoryAPI', async () => { @@ -686,12 +727,12 @@ describe('navigation', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/frames/one-frame.html'); - expect(page.frames()[0].url()).toContain('/frames/one-frame.html'); - expect(page.frames()[1].url()).toContain('/frames/frame.html'); + expect(page.frames()[0]!.url()).toContain('/frames/one-frame.html'); + expect(page.frames()[1]!.url()).toContain('/frames/frame.html'); - const response = await page.frames()[1].goto(server.EMPTY_PAGE); + const response = (await page.frames()[1]!.goto(server.EMPTY_PAGE))!; expect(response.ok()).toBe(true); - expect(response.frame()).toBe(page.frames()[1]); + expect(response.frame()).toBe(page.frames()[1]!); }); it('should reject when frame detaches', async () => { const { page, server } = getTestState(); @@ -700,12 +741,16 @@ describe('navigation', function () { server.setRoute('/empty.html', () => {}); const navigationPromise = page - .frames()[1] + .frames()[1]! .goto(server.EMPTY_PAGE) - .catch((error_) => error_); + .catch((error_) => { + return error_; + }); await server.waitForRequest('/empty.html'); - await page.$eval('iframe', (frame) => frame.remove()); + await page.$eval('iframe', (frame) => { + return frame.remove(); + }); const error = await navigationPromise; expect(error.message).toBe('Navigating frame was detached'); }); @@ -722,20 +767,20 @@ describe('navigation', function () { utils.attachFrame(page, 'frame3', server.EMPTY_PAGE), ]); // Navigate all frames to the same URL. - const serverResponses = []; - server.setRoute('/one-style.html', (req, res) => - serverResponses.push(res) - ); + const serverResponses: ServerResponse[] = []; + server.setRoute('/one-style.html', (_req, res) => { + return serverResponses.push(res); + }); const navigations = []; for (let i = 0; i < 3; ++i) { - navigations.push(frames[i].goto(server.PREFIX + '/one-style.html')); + navigations.push(frames[i]!.goto(server.PREFIX + '/one-style.html')); await server.waitForRequest('/one-style.html'); } // Respond from server out-of-order. const serverResponseTexts = ['AAA', 'BBB', 'CCC']; for (const i of [1, 2, 0]) { - serverResponses[i].end(serverResponseTexts[i]); - const response = await navigations[i]; + serverResponses[i]!.end(serverResponseTexts[i]); + const response = (await navigations[i])!; expect(response.frame()).toBe(frames[i]); expect(await response.text()).toBe(serverResponseTexts[i]); } @@ -747,35 +792,38 @@ describe('navigation', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/frames/one-frame.html'); - const frame = page.frames()[1]; + const frame = page.frames()[1]!; const [response] = await Promise.all([ frame.waitForNavigation(), - frame.evaluate( - (url: string) => (window.location.href = url), - server.PREFIX + '/grid.html' - ), + frame.evaluate((url: string) => { + return (window.location.href = url); + }, server.PREFIX + '/grid.html'), ]); - expect(response.ok()).toBe(true); - expect(response.url()).toContain('grid.html'); - expect(response.frame()).toBe(frame); + expect(response!.ok()).toBe(true); + expect(response!.url()).toContain('grid.html'); + expect(response!.frame()).toBe(frame); expect(page.url()).toContain('/frames/one-frame.html'); }); it('should fail when frame detaches', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/frames/one-frame.html'); - const frame = page.frames()[1]; + const frame = page.frames()[1]!; server.setRoute('/empty.html', () => {}); - let error = null; - const navigationPromise = frame - .waitForNavigation() - .catch((error_) => (error = error_)); + let error!: Error; + const navigationPromise = frame.waitForNavigation().catch((error_) => { + return (error = error_); + }); await Promise.all([ server.waitForRequest('/empty.html'), - frame.evaluate(() => ((window as any).location = '/empty.html')), + frame.evaluate(() => { + return ((window as any).location = '/empty.html'); + }), ]); - await page.$eval('iframe', (frame) => frame.remove()); + await page.$eval('iframe', (frame) => { + return frame.remove(); + }); await navigationPromise; expect(error.message).toBe('Navigating frame was detached'); }); @@ -786,9 +834,15 @@ describe('navigation', function () { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => (globalThis._foo = 10)); + await page.evaluate(() => { + return ((globalThis as any)._foo = 10); + }); await page.reload(); - expect(await page.evaluate(() => globalThis._foo)).toBe(undefined); + expect( + await page.evaluate(() => { + return (globalThis as any)._foo; + }) + ).toBe(undefined); }); }); }); diff --git a/test/src/network.spec.ts b/test/src/network.spec.ts index b1106293e73..5d3310e6441 100644 --- a/test/src/network.spec.ts +++ b/test/src/network.spec.ts @@ -26,8 +26,10 @@ import { describeFailsFirefox, itChromeOnly, itFirefoxOnly, -} from './mocha-utils'; // eslint-disable-line import/extensions -import { HTTPResponse } from '../../lib/cjs/puppeteer/api-docs-entry.js'; +} from './mocha-utils.js'; +import { HTTPRequest } from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; +import { HTTPResponse } from '../../lib/cjs/puppeteer/common/HTTPResponse.js'; +import { ServerResponse } from 'http'; describe('network', function () { setupTestBrowserHooks(); @@ -37,36 +39,35 @@ describe('network', function () { it('should fire for navigation requests', async () => { const { page, server } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - await page.goto(server.EMPTY_PAGE); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(requests.length).toBe(1); }); it('should fire for iframes', async () => { const { page, server } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - await page.goto(server.EMPTY_PAGE); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(requests.length).toBe(2); }); it('should fire for fetches', async () => { const { page, server } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => fetch('/empty.html')); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; + await page.evaluate(() => { + return fetch('/empty.html'); + }); expect(requests.length).toBe(2); }); }); @@ -74,57 +75,56 @@ describe('network', function () { it('should work for main frame navigation request', async () => { const { page, server } = getTestState(); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - await page.goto(server.EMPTY_PAGE); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(requests.length).toBe(1); - expect(requests[0].frame()).toBe(page.mainFrame()); + expect(requests[0]!.frame()).toBe(page.mainFrame()); }); itFailsFirefox('should work for subframe navigation request', async () => { const { page, server } = getTestState(); - await page.goto(server.EMPTY_PAGE); - const requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); + (await page.goto(server.EMPTY_PAGE))!; + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].frame()).toBe(page.frames()[1]); + expect(requests[0]!.frame()).toBe(page.frames()[1]!); }); it('should work for fetch requests', async () => { const { page, server } = getTestState(); - await page.goto(server.EMPTY_PAGE); - let requests = []; - page.on( - 'request', - (request) => !utils.isFavicon(request) && requests.push(request) - ); - await page.evaluate(() => fetch('/digits/1.png')); - requests = requests.filter( - (request) => !request.url().includes('favicon') - ); + (await page.goto(server.EMPTY_PAGE))!; + let requests: HTTPRequest[] = []; + page.on('request', (request) => { + return !utils.isFavicon(request) && requests.push(request); + }); + await page.evaluate(() => { + return fetch('/digits/1.png'); + }); + requests = requests.filter((request) => { + return !request.url().includes('favicon'); + }); expect(requests.length).toBe(1); - expect(requests[0].frame()).toBe(page.mainFrame()); + expect(requests[0]!.frame()).toBe(page.mainFrame()); }); }); describe('Request.headers', function () { itChromeOnly('should define Chrome as user agent header', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.request().headers()['user-agent']).toContain('Chrome'); }); itFirefoxOnly('should define Firefox as user agent header', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); @@ -133,11 +133,11 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.setHeader('foo', 'bar'); res.end(); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.headers()['foo']).toBe('bar'); }); }); @@ -147,9 +147,12 @@ describe('network', function () { const { page, server } = getTestState(); const initiators = new Map(); - page.on('request', (request) => - initiators.set(request.url().split('/').pop(), request.initiator()) - ); + page.on('request', (request) => { + return initiators.set( + request.url().split('/').pop(), + request.initiator() + ); + }); await page.goto(server.PREFIX + '/initiator.html'); expect(initiators.get('initiator.html').type).toBe('other'); @@ -171,11 +174,11 @@ describe('network', function () { ); expect(initiators.get('initiator.js').type).toBe('parser'); expect(initiators.get('injectedfile.js').type).toBe('script'); - expect(initiators.get('injectedfile.js').stack.callFrames[0].url).toBe( + expect(initiators.get('injectedfile.js').stack.callFrames[0]!.url).toBe( server.PREFIX + '/initiator.js' ); expect(initiators.get('injectedstyle.css').type).toBe('script'); - expect(initiators.get('injectedstyle.css').stack.callFrames[0].url).toBe( + expect(initiators.get('injectedstyle.css').stack.callFrames[0]!.url).toBe( server.PREFIX + '/initiator.js' ); expect(initiators.get('initiator.js').url).toBe( @@ -188,7 +191,7 @@ describe('network', function () { it('should return |false| for non-cached content', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.fromCache()).toBe(false); }); @@ -196,12 +199,12 @@ describe('network', function () { const { page, server } = getTestState(); const responses = new Map(); - page.on( - 'response', - (r) => + page.on('response', (r) => { + return ( !utils.isFavicon(r.request()) && responses.set(r.url().split('/').pop(), r) - ); + ); + }); // Load and re-load to make sure it's cached. await page.goto(server.PREFIX + '/cached/one-style.html'); @@ -219,7 +222,7 @@ describe('network', function () { it('should return |false| for non-service-worker content', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.fromServiceWorker()).toBe(false); }); @@ -227,16 +230,19 @@ describe('network', function () { const { page, server } = getTestState(); const responses = new Map(); - page.on( - 'response', - (r) => !utils.isFavicon(r) && responses.set(r.url().split('/').pop(), r) - ); + page.on('response', (r) => { + return ( + !utils.isFavicon(r) && responses.set(r.url().split('/').pop(), r) + ); + }); // Load and re-load to make sure serviceworker is installed and running. await page.goto(server.PREFIX + '/serviceworkers/fetch/sw.html', { waitUntil: 'networkidle2', }); - await page.evaluate(async () => await globalThis.activationPromise); + await page.evaluate(async () => { + return await (globalThis as any).activationPromise; + }); await page.reload(); expect(responses.size).toBe(2); @@ -251,27 +257,29 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - await page.goto(server.EMPTY_PAGE); - server.setRoute('/post', (req, res) => res.end()); - let request = null; + (await page.goto(server.EMPTY_PAGE))!; + server.setRoute('/post', (_req, res) => { + return res.end(); + }); + let request!: HTTPRequest; page.on('request', (r) => { if (!utils.isFavicon(r)) { request = r; } }); - await page.evaluate(() => - fetch('./post', { + await page.evaluate(() => { + return fetch('./post', { method: 'POST', body: JSON.stringify({ foo: 'bar' }), - }) - ); + }); + }); expect(request).toBeTruthy(); expect(request.postData()).toBe('{"foo":"bar"}'); }); it('should be |undefined| when there is no post data', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.request().postData()).toBe(undefined); }); }); @@ -280,7 +288,7 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/simple.json'); + const response = (await page.goto(server.PREFIX + '/simple.json'))!; const responseText = (await response.text()).trimEnd(); expect(responseText).toBe('{"foo": "bar"}'); }); @@ -288,7 +296,7 @@ describe('network', function () { const { page, server } = getTestState(); server.enableGzip('/simple.json'); - const response = await page.goto(server.PREFIX + '/simple.json'); + const response = (await page.goto(server.PREFIX + '/simple.json'))!; expect(response.headers()['content-encoding']).toBe('gzip'); const responseText = (await response.text()).trimEnd(); expect(responseText).toBe('{"foo": "bar"}'); @@ -297,13 +305,15 @@ describe('network', function () { const { page, server } = getTestState(); server.setRedirect('/foo.html', '/empty.html'); - const response = await page.goto(server.PREFIX + '/foo.html'); + const response = (await page.goto(server.PREFIX + '/foo.html'))!; const redirectChain = response.request().redirectChain(); expect(redirectChain.length).toBe(1); - const redirected = redirectChain[0].response(); + const redirected = redirectChain[0]!.response()!; expect(redirected.status()).toBe(302); - let error = null; - await redirected.text().catch((error_) => (error = error_)); + let error!: Error; + await redirected.text().catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( 'Response body is unavailable for redirect responses' ); @@ -311,10 +321,10 @@ describe('network', function () { it('should wait until response completes', async () => { const { page, server } = getTestState(); - await page.goto(server.EMPTY_PAGE); + (await page.goto(server.EMPTY_PAGE))!; // Setup server to trap request. - let serverResponse = null; - server.setRoute('/get', (req, res) => { + let serverResponse!: ServerResponse; + server.setRoute('/get', (_req, res) => { serverResponse = res; // In Firefox, |fetch| will be hanging until it receives |Content-Type| header // from server. @@ -323,14 +333,17 @@ describe('network', function () { }); // Setup page to trap response. let requestFinished = false; - page.on( - 'requestfinished', - (r) => (requestFinished = requestFinished || r.url().includes('/get')) - ); + page.on('requestfinished', (r) => { + return (requestFinished = requestFinished || r.url().includes('/get')); + }); // send request and wait for server response const [pageResponse] = await Promise.all([ - page.waitForResponse((r) => !utils.isFavicon(r.request())), - page.evaluate(() => fetch('./get', { method: 'GET' })), + page.waitForResponse((r) => { + return !utils.isFavicon(r.request()); + }), + page.evaluate(() => { + return fetch('./get', { method: 'GET' }); + }), server.waitForRequest('/get'), ]); @@ -341,9 +354,15 @@ describe('network', function () { const responseText = pageResponse.text(); // Write part of the response and wait for it to be flushed. - await new Promise((x) => serverResponse.write('wor', x)); + await new Promise((x) => { + return serverResponse.write('wor', x); + }); // Finish response. - await new Promise((x) => serverResponse.end('ld!', x)); + await new Promise((x) => { + serverResponse.end('ld!', () => { + return x(); + }); + }); expect(await responseText).toBe('hello world!'); }); }); @@ -352,7 +371,7 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/simple.json'); + const response = (await page.goto(server.PREFIX + '/simple.json'))!; expect(await response.json()).toEqual({ foo: 'bar' }); }); }); @@ -361,7 +380,7 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - const response = await page.goto(server.PREFIX + '/pptr.png'); + const response = (await page.goto(server.PREFIX + '/pptr.png'))!; const imageBuffer = fs.readFileSync( path.join(__dirname, '../assets', 'pptr.png') ); @@ -372,7 +391,7 @@ describe('network', function () { const { page, server } = getTestState(); server.enableGzip('/pptr.png'); - const response = await page.goto(server.PREFIX + '/pptr.png'); + const response = (await page.goto(server.PREFIX + '/pptr.png'))!; const imageBuffer = fs.readFileSync( path.join(__dirname, '../assets', 'pptr.png') ); @@ -384,7 +403,7 @@ describe('network', function () { await page.goto(server.PREFIX + '/empty.html'); - server.setRoute('/test.html', (req, res) => { + server.setRoute('/test.html', (_req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'x-ping'); res.end('Hello World'); @@ -422,22 +441,22 @@ describe('network', function () { it('should work', async () => { const { page, server } = getTestState(); - server.setRoute('/cool', (req, res) => { + server.setRoute('/cool', (_req, res) => { res.writeHead(200, 'cool!'); res.end(); }); - const response = await page.goto(server.PREFIX + '/cool'); + const response = (await page.goto(server.PREFIX + '/cool'))!; expect(response.statusText()).toBe('cool!'); }); it('handles missing status text', async () => { const { page, server } = getTestState(); - server.setRoute('/nostatus', (req, res) => { + server.setRoute('/nostatus', (_req, res) => { res.writeHead(200, ''); res.end(); }); - const response = await page.goto(server.PREFIX + '/nostatus'); + const response = (await page.goto(server.PREFIX + '/nostatus'))!; expect(response.statusText()).toBe(''); }); }); @@ -445,11 +464,13 @@ describe('network', function () { describeFailsFirefox('Response.timing', function () { it('returns timing information', async () => { const { page, server } = getTestState(); - const responses = []; - page.on('response', (response) => responses.push(response)); - await page.goto(server.EMPTY_PAGE); + const responses: HTTPResponse[] = []; + page.on('response', (response) => { + return responses.push(response); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(responses.length).toBe(1); - expect(responses[0].timing().receiveHeadersEnd).toBeGreaterThan(0); + expect(responses[0]!.timing()!.receiveHeadersEnd).toBeGreaterThan(0); }); }); @@ -457,24 +478,26 @@ describe('network', function () { it('Page.Events.Request', async () => { const { page, server } = getTestState(); - const requests = []; - page.on('request', (request) => requests.push(request)); - await page.goto(server.EMPTY_PAGE); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); - expect(requests[0].resourceType()).toBe('document'); - expect(requests[0].method()).toBe('GET'); - expect(requests[0].response()).toBeTruthy(); - expect(requests[0].frame() === page.mainFrame()).toBe(true); - expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.resourceType()).toBe('document'); + expect(requests[0]!.method()).toBe('GET'); + expect(requests[0]!.response()).toBeTruthy(); + expect(requests[0]!.frame() === page.mainFrame()).toBe(true); + expect(requests[0]!.frame()!.url()).toBe(server.EMPTY_PAGE); }); it('Page.Events.RequestServedFromCache', async () => { const { page, server } = getTestState(); - const cached = []; - page.on('requestservedfromcache', (r) => - cached.push(r.url().split('/').pop()) - ); + const cached: string[] = []; + page.on('requestservedfromcache', (r) => { + return cached.push(r.url().split('/').pop()!); + }); await page.goto(server.PREFIX + '/cached/one-style.html'); expect(cached).toEqual([]); @@ -485,18 +508,20 @@ describe('network', function () { it('Page.Events.Response', async () => { const { page, server } = getTestState(); - const responses = []; - page.on('response', (response) => responses.push(response)); - await page.goto(server.EMPTY_PAGE); + const responses: HTTPResponse[] = []; + page.on('response', (response) => { + return responses.push(response); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(responses.length).toBe(1); - expect(responses[0].url()).toBe(server.EMPTY_PAGE); - expect(responses[0].status()).toBe(200); - expect(responses[0].ok()).toBe(true); - expect(responses[0].request()).toBeTruthy(); - const remoteAddress = responses[0].remoteAddress(); + expect(responses[0]!.url()).toBe(server.EMPTY_PAGE); + expect(responses[0]!.status()).toBe(200); + expect(responses[0]!.ok()).toBe(true); + expect(responses[0]!.request()).toBeTruthy(); + const remoteAddress = responses[0]!.remoteAddress(); // Either IPv6 or IPv4, depending on environment. expect( - remoteAddress.ip.includes('::1') || remoteAddress.ip === '127.0.0.1' + remoteAddress.ip!.includes('::1') || remoteAddress.ip === '127.0.0.1' ).toBe(true); expect(remoteAddress.port).toBe(server.PORT); }); @@ -512,61 +537,73 @@ describe('network', function () { request.continue(); } }); - const failedRequests = []; - page.on('requestfailed', (request) => failedRequests.push(request)); + const failedRequests: HTTPRequest[] = []; + page.on('requestfailed', (request) => { + return failedRequests.push(request); + }); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); - expect(failedRequests[0].url()).toContain('one-style.css'); - expect(failedRequests[0].response()).toBe(null); - expect(failedRequests[0].resourceType()).toBe('stylesheet'); + expect(failedRequests[0]!.url()).toContain('one-style.css'); + expect(failedRequests[0]!.response()).toBe(null); + expect(failedRequests[0]!.resourceType()).toBe('stylesheet'); if (isChrome) { - expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); + expect(failedRequests[0]!.failure()!.errorText).toBe('net::ERR_FAILED'); } else { - expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); + expect(failedRequests[0]!.failure()!.errorText).toBe( + 'NS_ERROR_FAILURE' + ); } - expect(failedRequests[0].frame()).toBeTruthy(); + expect(failedRequests[0]!.frame()).toBeTruthy(); }); it('Page.Events.RequestFinished', async () => { const { page, server } = getTestState(); - const requests = []; - page.on('requestfinished', (request) => requests.push(request)); - await page.goto(server.EMPTY_PAGE); + const requests: HTTPRequest[] = []; + page.on('requestfinished', (request) => { + return requests.push(request); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); - expect(requests[0].response()).toBeTruthy(); - expect(requests[0].frame() === page.mainFrame()).toBe(true); - expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.response()).toBeTruthy(); + expect(requests[0]!.frame() === page.mainFrame()).toBe(true); + expect(requests[0]!.frame()!.url()).toBe(server.EMPTY_PAGE); }); it('should fire events in proper order', async () => { const { page, server } = getTestState(); - const events = []; - page.on('request', () => events.push('request')); - page.on('response', () => events.push('response')); - page.on('requestfinished', () => events.push('requestfinished')); - await page.goto(server.EMPTY_PAGE); + const events: string[] = []; + page.on('request', () => { + return events.push('request'); + }); + page.on('response', () => { + return events.push('response'); + }); + page.on('requestfinished', () => { + return events.push('requestfinished'); + }); + (await page.goto(server.EMPTY_PAGE))!; expect(events).toEqual(['request', 'response', 'requestfinished']); }); it('should support redirects', async () => { const { page, server } = getTestState(); - const events = []; - page.on('request', (request) => - events.push(`${request.method()} ${request.url()}`) - ); - page.on('response', (response) => - events.push(`${response.status()} ${response.url()}`) - ); - page.on('requestfinished', (request) => - events.push(`DONE ${request.url()}`) - ); - page.on('requestfailed', (request) => - events.push(`FAIL ${request.url()}`) - ); + const events: string[] = []; + page.on('request', (request) => { + return events.push(`${request.method()} ${request.url()}`); + }); + page.on('response', (response) => { + return events.push(`${response.status()} ${response.url()}`); + }); + page.on('requestfinished', (request) => { + return events.push(`DONE ${request.url()}`); + }); + page.on('requestfailed', (request) => { + return events.push(`FAIL ${request.url()}`); + }); server.setRedirect('/foo.html', '/empty.html'); const FOO_URL = server.PREFIX + '/foo.html'; - const response = await page.goto(FOO_URL); + const response = (await page.goto(FOO_URL))!; expect(events).toEqual([ `GET ${FOO_URL}`, `302 ${FOO_URL}`, @@ -579,8 +616,8 @@ describe('network', function () { // Check redirect chain const redirectChain = response.request().redirectChain(); expect(redirectChain.length).toBe(1); - expect(redirectChain[0].url()).toContain('/foo.html'); - expect(redirectChain[0].response().remoteAddress().port).toBe( + expect(redirectChain[0]!.url()).toContain('/foo.html'); + expect(redirectChain[0]!.response()!.remoteAddress().port).toBe( server.PORT ); }); @@ -591,9 +628,9 @@ describe('network', function () { const { page, server } = getTestState(); const requests = new Map(); - page.on('request', (request) => - requests.set(request.url().split('/').pop(), request) - ); + page.on('request', (request) => { + return requests.set(request.url().split('/').pop(), request); + }); server.setRedirect('/rrredirect', '/frames/one-frame.html'); await page.goto(server.PREFIX + '/rrredirect'); expect(requests.get('rrredirect').isNavigationRequest()).toBe(true); @@ -622,10 +659,12 @@ describe('network', function () { itFailsFirefox('should work when navigating to image', async () => { const { page, server } = getTestState(); - const requests = []; - page.on('request', (request) => requests.push(request)); - await page.goto(server.PREFIX + '/pptr.png'); - expect(requests[0].isNavigationRequest()).toBe(true); + const requests: HTTPRequest[] = []; + page.on('request', (request) => { + return requests.push(request); + }); + (await page.goto(server.PREFIX + '/pptr.png'))!; + expect(requests[0]!.isNavigationRequest()).toBe(true); }); }); @@ -645,12 +684,12 @@ describe('network', function () { it('should throw for non-string header values', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { // @ts-expect-error purposeful bad input await page.setExtraHTTPHeaders({ foo: 1 }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe( 'Expected value of header "foo" to be String, but "number" is found.' @@ -665,11 +704,15 @@ describe('network', function () { server.setAuth('/empty.html', 'user', 'pass'); let response; try { - response = await page.goto(server.EMPTY_PAGE); + response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(401); } catch (error) { // In headful, an error is thrown instead of 401. - if (!error.message.startsWith('net::ERR_INVALID_AUTH_CREDENTIALS')) { + if ( + !(error as Error).message.startsWith( + 'net::ERR_INVALID_AUTH_CREDENTIALS' + ) + ) { throw error; } } @@ -677,7 +720,7 @@ describe('network', function () { username: 'user', password: 'pass', }); - response = await page.reload(); + response = (await page.reload())!; expect(response.status()).toBe(200); }); it('should fail if wrong credentials', async () => { @@ -689,7 +732,7 @@ describe('network', function () { username: 'foo', password: 'bar', }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(401); }); it('should allow disable authentication', async () => { @@ -701,16 +744,25 @@ describe('network', function () { username: 'user3', password: 'pass3', }); - let response = await page.goto(server.EMPTY_PAGE); + let response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(200); - await page.authenticate(null); + await page.authenticate({ + username: '', + password: '', + }); // Navigate to a different origin to bust Chrome's credential caching. try { - response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); + response = (await page.goto( + server.CROSS_PROCESS_PREFIX + '/empty.html' + ))!; expect(response.status()).toBe(401); } catch (error) { // In headful, an error is thrown instead of 401. - if (!error.message.startsWith('net::ERR_INVALID_AUTH_CREDENTIALS')) { + if ( + !(error as Error).message.startsWith( + 'net::ERR_INVALID_AUTH_CREDENTIALS' + ) + ) { throw error; } } @@ -727,7 +779,9 @@ describe('network', function () { }); const responses = new Map(); - page.on('response', (r) => responses.set(r.url().split('/').pop(), r)); + page.on('response', (r) => { + return responses.set(r.url().split('/').pop(), r); + }); // Load and re-load to make sure it's cached. await page.goto(server.PREFIX + '/cached/one-style.html'); @@ -745,27 +799,29 @@ describe('network', function () { const { page, server } = getTestState(); const setCookieString = 'foo=bar'; - server.setRoute('/empty.html', (req, res) => { + server.setRoute('/empty.html', (_req, res) => { res.setHeader('set-cookie', setCookieString); res.end('hello world'); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.headers()['set-cookie']).toBe(setCookieString); }); it('Same-origin set-cookie subresource', async () => { const { page, server } = getTestState(); - await page.goto(server.EMPTY_PAGE); + (await page.goto(server.EMPTY_PAGE))!; const setCookieString = 'foo=bar'; - server.setRoute('/foo', (req, res) => { + server.setRoute('/foo', (_req, res) => { res.setHeader('set-cookie', setCookieString); res.end('hello world'); }); - const responsePromise = new Promise((resolve) => - page.on('response', (response) => resolve(response)) - ); + const responsePromise = new Promise((resolve) => { + return page.on('response', (response) => { + return resolve(response); + }); + }); page.evaluate(() => { const xhr = new XMLHttpRequest(); xhr.open('GET', '/foo'); @@ -789,7 +845,7 @@ describe('network', function () { await page.goto(httpsServer.PREFIX + '/empty.html'); const setCookieString = 'hello=world'; - httpsServer.setRoute('/setcookie.html', (req, res) => { + httpsServer.setRoute('/setcookie.html', (_req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('set-cookie', setCookieString); res.end(); diff --git a/test/src/oopif.spec.ts b/test/src/oopif.spec.ts index 0a4d4a7a243..16461cb2c7a 100644 --- a/test/src/oopif.spec.ts +++ b/test/src/oopif.spec.ts @@ -20,7 +20,7 @@ import { getTestState, describeChromeOnly, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import { Browser, BrowserContext, @@ -53,21 +53,19 @@ describeChromeOnly('OOPIF', function () { afterEach(async () => { await context.close(); - page = null; - context = null; }); after(async () => { await browser.close(); - browser = null; }); + it('should treat OOP iframes and normal iframes the same', async () => { const { server } = getTestState(); await page.goto(server.EMPTY_PAGE); - const framePromise = page.waitForFrame((frame) => - frame.url().endsWith('/empty.html') - ); + const framePromise = page.waitForFrame((frame) => { + return frame.url().endsWith('/empty.html'); + }); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame( page, @@ -137,12 +135,16 @@ describeChromeOnly('OOPIF', function () { const [frame1, frame2] = await Promise.all([frame1Promise, frame2Promise]); - expect(await frame1.evaluate(() => document.location.href)).toMatch( - /one-frame\.html$/ - ); - expect(await frame2.evaluate(() => document.location.href)).toMatch( - /frames\/frame\.html$/ - ); + expect( + await frame1.evaluate(() => { + return document.location.href; + }) + ).toMatch(/one-frame\.html$/); + expect( + await frame2.evaluate(() => { + return document.location.href; + }) + ).toMatch(/frames\/frame\.html$/); }); it('should support OOP iframes getting detached', async () => { const { server } = getTestState(); @@ -279,9 +281,9 @@ describeChromeOnly('OOPIF', function () { it('should report oopif frames', async () => { const { server } = getTestState(); - const frame = page.waitForFrame((frame) => - frame.url().endsWith('/oopif.html') - ); + const frame = page.waitForFrame((frame) => { + return frame.url().endsWith('/oopif.html'); + }); await page.goto(server.PREFIX + '/dynamic-oopif.html'); await frame; expect(oopifs(context).length).toBe(1); @@ -291,24 +293,32 @@ describeChromeOnly('OOPIF', function () { it('should wait for inner OOPIFs', async () => { const { server } = getTestState(); await page.goto(`http://mainframe:${server.PORT}/main-frame.html`); - const frame2 = await page.waitForFrame((frame) => - frame.url().endsWith('inner-frame2.html') - ); + const frame2 = await page.waitForFrame((frame) => { + return frame.url().endsWith('inner-frame2.html'); + }); expect(oopifs(context).length).toBe(2); - expect(page.frames().filter((frame) => frame.isOOPFrame()).length).toBe(2); expect( - await frame2.evaluate(() => document.querySelectorAll('button').length) + page.frames().filter((frame) => { + return frame.isOOPFrame(); + }).length + ).toBe(2); + expect( + await frame2.evaluate(() => { + return document.querySelectorAll('button').length; + }) ).toStrictEqual(1); }); it('should load oopif iframes with subresources and request interception', async () => { const { server } = getTestState(); - const frame = page.waitForFrame((frame) => - frame.url().endsWith('/oopif.html') - ); + const frame = page.waitForFrame((frame) => { + return frame.url().endsWith('/oopif.html'); + }); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); await page.goto(server.PREFIX + '/dynamic-oopif.html'); await frame; expect(oopifs(context).length).toBe(1); @@ -327,7 +337,7 @@ describeChromeOnly('OOPIF', function () { server.CROSS_PROCESS_PREFIX + '/empty.html' ); - const frame1 = oopIframe.childFrames()[0]; + const frame1 = oopIframe.childFrames()[0]!; expect(frame1.url()).toMatch(/empty.html$/); await utils.navigateFrame( oopIframe, @@ -367,13 +377,13 @@ describeChromeOnly('OOPIF', function () { button.innerText = 'click'; document.body.appendChild(button); }); - const button = await frame.waitForSelector('#test-button', { + const button = (await frame.waitForSelector('#test-button', { visible: true, - }); + }))!; const result = await button.clickablePoint(); expect(result.x).toBeGreaterThan(150); // padding + margin + border left expect(result.y).toBeGreaterThan(150); // padding + margin + border top - const resultBoxModel = await button.boxModel(); + const resultBoxModel = (await button.boxModel())!; for (const quad of [ resultBoxModel.content, resultBoxModel.border, @@ -385,7 +395,7 @@ describeChromeOnly('OOPIF', function () { expect(part.y).toBeGreaterThan(150); // padding + margin + border top } } - const resultBoundingBox = await button.boundingBox(); + const resultBoundingBox = (await button.boundingBox())!; expect(resultBoundingBox.x).toBeGreaterThan(150); // padding + margin + border left expect(resultBoundingBox.y).toBeGreaterThan(150); // padding + margin + border top }); @@ -393,9 +403,9 @@ describeChromeOnly('OOPIF', function () { it('should detect existing OOPIFs when Puppeteer connects to an existing page', async () => { const { server, puppeteer } = getTestState(); - const frame = page.waitForFrame((frame) => - frame.url().endsWith('/oopif.html') - ); + const frame = page.waitForFrame((frame) => { + return frame.url().endsWith('/oopif.html'); + }); await page.goto(server.PREFIX + '/dynamic-oopif.html'); await frame; expect(oopifs(context).length).toBe(1); @@ -403,9 +413,9 @@ describeChromeOnly('OOPIF', function () { const browserURL = 'http://127.0.0.1:21222'; const browser1 = await puppeteer.connect({ browserURL }); - const target = await browser1.waitForTarget((target) => - target.url().endsWith('dynamic-oopif.html') - ); + const target = await browser1.waitForTarget((target) => { + return target.url().endsWith('dynamic-oopif.html'); + }); await target.page(); browser1.disconnect(); }); @@ -415,11 +425,11 @@ describeChromeOnly('OOPIF', function () { await page.goto(server.PREFIX + '/lazy-oopif-frame.html'); await page.setViewport({ width: 1000, height: 1000 }); - expect(page.frames().map((frame) => frame._hasStartedLoading)).toEqual([ - true, - true, - false, - ]); + expect( + page.frames().map((frame) => { + return frame._hasStartedLoading; + }) + ).toEqual([true, true, false]); }); describe('waitForFrame', () => { @@ -433,13 +443,15 @@ describeChromeOnly('OOPIF', function () { server.CROSS_PROCESS_PREFIX + '/empty.html' ); - await page.waitForFrame((frame) => frame.url().endsWith('/empty.html')); + await page.waitForFrame((frame) => { + return frame.url().endsWith('/empty.html'); + }); }); }); }); function oopifs(context: BrowserContext) { - return context - .targets() - .filter((target) => target._getTargetInfo().type === 'iframe'); + return context.targets().filter((target) => { + return target._getTargetInfo().type === 'iframe'; + }); } diff --git a/test/src/page.spec.ts b/test/src/page.spec.ts index 37cb14aeb83..dde97a23557 100644 --- a/test/src/page.spec.ts +++ b/test/src/page.spec.ts @@ -13,22 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import fs from 'fs'; -import path from 'path'; -import utils from './utils.js'; -const { waitEvent } = utils; import expect from 'expect'; +import fs from 'fs'; +import { ServerResponse } from 'http'; +import path from 'path'; import sinon from 'sinon'; +import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js'; +import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; +import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js'; +import { Metrics, Page } from '../../lib/cjs/puppeteer/common/Page.js'; import { + describeFailsFirefox, getTestState, + itFailsFirefox, setupTestBrowserHooks, setupTestPageAndContextHooks, - itFailsFirefox, - describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions -import { Page, Metrics } from '../../lib/cjs/puppeteer/common/Page.js'; -import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js'; -import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js'; +} from './mocha-utils.js'; +import utils, { attachFrame, waitEvent } from './utils.js'; describe('Page', function () { setupTestBrowserHooks(); @@ -38,11 +39,15 @@ describe('Page', function () { const { context } = getTestState(); const newPage = await context.newPage(); - let error = null; + let error!: Error; await Promise.all([ newPage - .evaluate(() => new Promise(() => {})) - .catch((error_) => (error = error_)), + .evaluate(() => { + return new Promise(() => {}); + }) + .catch((error_) => { + return (error = error_); + }), newPage.close(), ]); expect(error.message).toContain('Protocol error'); @@ -98,8 +103,12 @@ describe('Page', function () { const newPage = await context.newPage(); const results = await Promise.all([ - newPage.waitForRequest(server.EMPTY_PAGE).catch((error) => error), - newPage.waitForResponse(server.EMPTY_PAGE).catch((error) => error), + newPage.waitForRequest(server.EMPTY_PAGE).catch((error) => { + return error; + }), + newPage.waitForResponse(server.EMPTY_PAGE).catch((error) => { + return error; + }), newPage.close(), ]); for (let i = 0; i < 2; i++) { @@ -126,7 +135,7 @@ describe('Page', function () { const { page, server } = getTestState(); const handler = sinon.spy(); - const onResponse = (response) => { + const onResponse = (response: { url: () => string }) => { // Ignore default favicon requests. if (!response.url().endsWith('favicon.ico')) { handler(); @@ -149,7 +158,7 @@ describe('Page', function () { const { page, server } = getTestState(); const handler = sinon.spy(); - const onResponse = (response) => { + const onResponse = (response: { url: () => string }) => { // Ignore default favicon requests. if (!response.url().endsWith('favicon.ico')) { handler(); @@ -178,8 +187,10 @@ describe('Page', function () { it('should throw when page crashes', async () => { const { page } = getTestState(); - let error = null; - page.on('error', (err) => (error = err)); + let error!: Error; + page.on('error', (err) => { + return (error = err); + }); page.goto('chrome://crash').catch(() => {}); await waitEvent(page, 'error'); expect(error.message).toBe('Page crashed!'); @@ -191,21 +202,45 @@ describe('Page', function () { const { page } = getTestState(); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), - page.evaluate(() => window.open('about:blank')), + new Promise((x) => { + return page.once('popup', x); + }), + page.evaluate(() => { + return window.open('about:blank'); + }), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(true); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(true); }); it('should work with noopener', async () => { const { page } = getTestState(); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), - page.evaluate(() => window.open('about:blank', null, 'noopener')), + new Promise((x) => { + return page.once('popup', x); + }), + page.evaluate(() => { + return window.open('about:blank', undefined, 'noopener'); + }), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(false); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); }); it('should work with clicking target=_blank and without rel=opener', async () => { const { page, server } = getTestState(); @@ -213,11 +248,21 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); await page.setContent('yo'); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), + new Promise((x) => { + return page.once('popup', x); + }), page.click('a'), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(false); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); }); it('should work with clicking target=_blank and with rel=opener', async () => { const { page, server } = getTestState(); @@ -227,11 +272,21 @@ describe('Page', function () { 'yo' ); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), + new Promise((x) => { + return page.once('popup', x); + }), page.click('a'), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(true); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(true); }); it('should work with fake-clicking target=_blank and rel=noopener', async () => { const { page, server } = getTestState(); @@ -241,11 +296,23 @@ describe('Page', function () { 'yo' ); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), - page.$eval('a', (a: HTMLAnchorElement) => a.click()), + new Promise((x) => { + return page.once('popup', x); + }), + page.$eval('a', (a) => { + return (a as HTMLAnchorElement).click(); + }), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(false); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); }); it('should work with clicking target=_blank and rel=noopener', async () => { const { page, server } = getTestState(); @@ -255,21 +322,31 @@ describe('Page', function () { 'yo' ); const [popup] = await Promise.all([ - new Promise((x) => page.once('popup', x)), + new Promise((x) => { + return page.once('popup', x); + }), page.click('a'), ]); - expect(await page.evaluate(() => !!window.opener)).toBe(false); - expect(await popup.evaluate(() => !!window.opener)).toBe(false); + expect( + await page.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); + expect( + await popup.evaluate(() => { + return !!window.opener; + }) + ).toBe(false); }); }); describe('BrowserContext.overridePermissions', function () { - function getPermission(page, name) { - return page.evaluate( - (name) => - navigator.permissions.query({ name }).then((result) => result.state), - name - ); + function getPermission(page: Page, name: string) { + return page.evaluate((name: PermissionName) => { + return navigator.permissions.query({ name }).then((result) => { + return result.state; + }); + }, name); } it('should be prompt by default', async () => { @@ -289,11 +366,13 @@ describe('Page', function () { const { page, server, context } = getTestState(); await page.goto(server.EMPTY_PAGE); - let error = null; + let error!: Error; await context // @ts-expect-error purposeful bad input for test .overridePermissions(server.EMPTY_PAGE, ['foo']) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Unknown permission: foo'); }); itFailsFirefox('should grant permission when listed', async () => { @@ -322,35 +401,39 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { - globalThis.events = []; + (globalThis as any).events = []; return navigator.permissions .query({ name: 'geolocation' }) .then(function (result) { - globalThis.events.push(result.state); + (globalThis as any).events.push(result.state); result.onchange = function () { - globalThis.events.push(result.state); + (globalThis as any).events.push(result.state); }; }); }); - expect(await page.evaluate(() => globalThis.events)).toEqual(['prompt']); + expect( + await page.evaluate(() => { + return (globalThis as any).events; + }) + ).toEqual(['prompt']); await context.overridePermissions(server.EMPTY_PAGE, []); - expect(await page.evaluate(() => globalThis.events)).toEqual([ - 'prompt', - 'denied', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).events; + }) + ).toEqual(['prompt', 'denied']); await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']); - expect(await page.evaluate(() => globalThis.events)).toEqual([ - 'prompt', - 'denied', - 'granted', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).events; + }) + ).toEqual(['prompt', 'denied', 'granted']); await context.clearPermissionOverrides(); - expect(await page.evaluate(() => globalThis.events)).toEqual([ - 'prompt', - 'denied', - 'granted', - 'prompt', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).events; + }) + ).toEqual(['prompt', 'denied', 'granted', 'prompt']); }); itFailsFirefox( 'should isolate permissions between browser contexts', @@ -399,17 +482,16 @@ describe('Page', function () { await context.overridePermissions(server.PREFIX, ['geolocation']); await page.goto(server.EMPTY_PAGE); await page.setGeolocation({ longitude: 10, latitude: 10 }); - const geolocation = await page.evaluate( - () => - new Promise((resolve) => - navigator.geolocation.getCurrentPosition((position) => { - resolve({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - }) - ) - ); + const geolocation = await page.evaluate(() => { + return new Promise((resolve) => { + return navigator.geolocation.getCurrentPosition((position) => { + resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + }); + }); + }); expect(geolocation).toEqual({ latitude: 10, longitude: 10, @@ -418,11 +500,11 @@ describe('Page', function () { it('should throw when invalid longitude', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { await page.setGeolocation({ longitude: 200, latitude: 10 }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toContain('Invalid longitude "200"'); }); @@ -433,21 +515,35 @@ describe('Page', function () { const { page, server } = getTestState(); await page.setOfflineMode(true); - let error = null; - await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); + let error!: Error; + await page.goto(server.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); await page.setOfflineMode(false); - const response = await page.reload(); + const response = (await page.reload())!; expect(response.status()).toBe(200); }); it('should emulate navigator.onLine', async () => { const { page } = getTestState(); - expect(await page.evaluate(() => window.navigator.onLine)).toBe(true); + expect( + await page.evaluate(() => { + return window.navigator.onLine; + }) + ).toBe(true); await page.setOfflineMode(true); - expect(await page.evaluate(() => window.navigator.onLine)).toBe(false); + expect( + await page.evaluate(() => { + return window.navigator.onLine; + }) + ).toBe(false); await page.setOfflineMode(false); - expect(await page.evaluate(() => window.navigator.onLine)).toBe(true); + expect( + await page.evaluate(() => { + return window.navigator.onLine; + }) + ).toBe(true); }); }); @@ -456,18 +552,20 @@ describe('Page', function () { const { page } = getTestState(); // Instantiate an object - await page.evaluate(() => (globalThis.set = new Set(['hello', 'world']))); - const prototypeHandle = await page.evaluateHandle(() => Set.prototype); + await page.evaluate(() => { + return ((globalThis as any).set = new Set(['hello', 'world'])); + }); + const prototypeHandle = await page.evaluateHandle(() => { + return Set.prototype; + }); const objectsHandle = await page.queryObjects(prototypeHandle); - const count = await page.evaluate( - (objects: JSHandle[]) => objects.length, - objectsHandle - ); + const count = await page.evaluate((objects: JSHandle[]) => { + return objects.length; + }, objectsHandle); expect(count).toBe(1); - const values = await page.evaluate( - (objects) => Array.from(objects[0].values()), - objectsHandle - ); + const values = await page.evaluate((objects) => { + return Array.from(objects[0]!.values()); + }, objectsHandle); expect(values).toEqual(['hello', 'world']); }); itFailsFirefox('should work for non-blank page', async () => { @@ -475,36 +573,41 @@ describe('Page', function () { // Instantiate an object await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => (globalThis.set = new Set(['hello', 'world']))); - const prototypeHandle = await page.evaluateHandle(() => Set.prototype); + await page.evaluate(() => { + return ((globalThis as any).set = new Set(['hello', 'world'])); + }); + const prototypeHandle = await page.evaluateHandle(() => { + return Set.prototype; + }); const objectsHandle = await page.queryObjects(prototypeHandle); - const count = await page.evaluate( - (objects: JSHandle[]) => objects.length, - objectsHandle - ); + const count = await page.evaluate((objects: JSHandle[]) => { + return objects.length; + }, objectsHandle); expect(count).toBe(1); }); it('should fail for disposed handles', async () => { const { page } = getTestState(); - const prototypeHandle = await page.evaluateHandle( - () => HTMLBodyElement.prototype - ); + const prototypeHandle = await page.evaluateHandle(() => { + return HTMLBodyElement.prototype; + }); await prototypeHandle.dispose(); - let error = null; - await page - .queryObjects(prototypeHandle) - .catch((error_) => (error = error_)); + let error!: Error; + await page.queryObjects(prototypeHandle).catch((error_) => { + return (error = error_); + }); expect(error.message).toBe('Prototype JSHandle is disposed!'); }); it('should fail primitive values as prototypes', async () => { const { page } = getTestState(); - const prototypeHandle = await page.evaluateHandle(() => 42); - let error = null; - await page - .queryObjects(prototypeHandle) - .catch((error_) => (error = error_)); + const prototypeHandle = await page.evaluateHandle(() => { + return 42; + }); + let error!: Error; + await page.queryObjects(prototypeHandle).catch((error_) => { + return (error = error_); + }); expect(error.message).toBe( 'Prototype JSHandle must not be referencing primitive value' ); @@ -515,10 +618,14 @@ describe('Page', function () { it('should work', async () => { const { page } = getTestState(); - let message = null; - page.once('console', (m) => (message = m)); + let message!: ConsoleMessage; + page.once('console', (m) => { + return (message = m); + }); await Promise.all([ - page.evaluate(() => console.log('hello', 5, { foo: 'bar' })), + page.evaluate(() => { + return console.log('hello', 5, { foo: 'bar' }); + }), waitEvent(page, 'console'), ]); expect(message.text()).toEqual('hello 5 JSHandle@object'); @@ -530,15 +637,17 @@ describe('Page', function () { columnNumber: expect.any(Number), }); - expect(await message.args()[0].jsonValue()).toEqual('hello'); - expect(await message.args()[1].jsonValue()).toEqual(5); - expect(await message.args()[2].jsonValue()).toEqual({ foo: 'bar' }); + expect(await message.args()[0]!.jsonValue()).toEqual('hello'); + expect(await message.args()[1]!.jsonValue()).toEqual(5); + expect(await message.args()[2]!.jsonValue()).toEqual({ foo: 'bar' }); }); it('should work for different console API calls', async () => { const { page } = getTestState(); - const messages = []; - page.on('console', (msg) => messages.push(msg)); + const messages: any[] = []; + page.on('console', (msg) => { + return messages.push(msg); + }); // All console events will be reported before `page.evaluate` is finished. await page.evaluate(() => { // A pair of time/timeEnd generates only one Console API call. @@ -550,16 +659,17 @@ describe('Page', function () { console.error('calling console.error'); console.log(Promise.resolve('should not wait until resolved!')); }); - expect(messages.map((msg) => msg.type())).toEqual([ - 'timeEnd', - 'trace', - 'dir', - 'warning', - 'error', - 'log', - ]); - expect(messages[0].text()).toContain('calling console.time'); - expect(messages.slice(1).map((msg) => msg.text())).toEqual([ + expect( + messages.map((msg) => { + return msg.type(); + }) + ).toEqual(['timeEnd', 'trace', 'dir', 'warning', 'error', 'log']); + expect(messages[0]!.text()).toContain('calling console.time'); + expect( + messages.slice(1).map((msg) => { + return msg.text(); + }) + ).toEqual([ 'calling console.trace', 'calling console.dir', 'calling console.warn', @@ -570,10 +680,14 @@ describe('Page', function () { it('should not fail for window object', async () => { const { page } = getTestState(); - let message = null; - page.once('console', (msg) => (message = msg)); + let message!: ConsoleMessage; + page.once('console', (msg) => { + return (message = msg); + }); await Promise.all([ - page.evaluate(() => console.error(window)), + page.evaluate(() => { + return console.error(window); + }), waitEvent(page, 'console'), ]); expect(message.text()).toBe('JSHandle@object'); @@ -584,10 +698,9 @@ describe('Page', function () { await page.goto('about:blank'); const [message] = await Promise.all([ waitEvent(page, 'console'), - page.evaluate( - async (url: string) => fetch(url).catch(() => {}), - server.EMPTY_PAGE - ), + page.evaluate(async (url: string) => { + return fetch(url).catch(() => {}); + }, server.EMPTY_PAGE), ]); expect(message.text()).toContain('Access-Control-Allow-Origin'); if (isChrome) { @@ -657,19 +770,25 @@ describe('Page', function () { window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0' - ); - await new Promise((x) => (win.onload = x)); + )!; + await new Promise((x) => { + return (win.onload = x); + }); // 2. In this popup, create an iframe that console.logs a message. win.document.body.innerHTML = ``; - const frame = win.document.querySelector('iframe'); - await new Promise((x) => (frame.onload = x)); + const frame = win.document.querySelector('iframe')!; + await new Promise((x) => { + return (frame.onload = x); + }); // 3. After that, remove the iframe. frame.remove(); }); const popupTarget = page .browserContext() .targets() - .find((target) => target !== page.target()); + .find((target) => { + return target !== page.target(); + })!; // 4. Connect to the popup and make sure it doesn't throw. await popupTarget.page(); }); @@ -696,14 +815,18 @@ describe('Page', function () { const { page } = getTestState(); const metricsPromise = new Promise<{ metrics: Metrics; title: string }>( - (fulfill) => page.once('metrics', fulfill) + (fulfill) => { + return page.once('metrics', fulfill); + } ); - await page.evaluate(() => console.timeStamp('test42')); + await page.evaluate(() => { + return console.timeStamp('test42'); + }); const metrics = await metricsPromise; expect(metrics.title).toBe('test42'); checkMetrics(metrics.metrics); }); - function checkMetrics(metrics) { + function checkMetrics(metrics: Metrics) { const metricsToCheck = new Set([ 'Timestamp', 'Documents', @@ -721,7 +844,7 @@ describe('Page', function () { ]); for (const name in metrics) { expect(metricsToCheck.has(name)).toBeTruthy(); - expect(metrics[name]).toBeGreaterThanOrEqual(0); + expect(metrics[name as keyof Metrics]).toBeGreaterThanOrEqual(0); metricsToCheck.delete(name); } expect(metricsToCheck.size).toBe(0); @@ -748,9 +871,9 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ - page.waitForRequest( - (request) => request.url() === server.PREFIX + '/digits/2.png' - ), + page.waitForRequest((request) => { + return request.url() === server.PREFIX + '/digits/2.png'; + }), page.evaluate(() => { fetch('/digits/1.png'); fetch('/digits/2.png'); @@ -762,20 +885,31 @@ describe('Page', function () { it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; + let error!: Error; await page - .waitForRequest(() => false, { timeout: 1 }) - .catch((error_) => (error = error_)); + .waitForRequest( + () => { + return false; + }, + { timeout: 1 } + ) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should respect default timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; + let error!: Error; page.setDefaultTimeout(1); await page - .waitForRequest(() => false) - .catch((error_) => (error = error_)); + .waitForRequest(() => { + return false; + }) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should work with no timeout', async () => { @@ -784,13 +918,13 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ page.waitForRequest(server.PREFIX + '/digits/2.png', { timeout: 0 }), - page.evaluate(() => - setTimeout(() => { + page.evaluate(() => { + return setTimeout(() => { fetch('/digits/1.png'); fetch('/digits/2.png'); fetch('/digits/3.png'); - }, 50) - ), + }, 50); + }), ]); expect(request.url()).toBe(server.PREFIX + '/digits/2.png'); }); @@ -814,20 +948,31 @@ describe('Page', function () { it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; + let error!: Error; await page - .waitForResponse(() => false, { timeout: 1 }) - .catch((error_) => (error = error_)); + .waitForResponse( + () => { + return false; + }, + { timeout: 1 } + ) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should respect default timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; + let error!: Error; page.setDefaultTimeout(1); await page - .waitForResponse(() => false) - .catch((error_) => (error = error_)); + .waitForResponse(() => { + return false; + }) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should work with predicate', async () => { @@ -835,9 +980,9 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ - page.waitForResponse( - (response) => response.url() === server.PREFIX + '/digits/2.png' - ), + page.waitForResponse((response) => { + return response.url() === server.PREFIX + '/digits/2.png'; + }), page.evaluate(() => { fetch('/digits/1.png'); fetch('/digits/2.png'); @@ -867,13 +1012,13 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForResponse(server.PREFIX + '/digits/2.png', { timeout: 0 }), - page.evaluate(() => - setTimeout(() => { + page.evaluate(() => { + return setTimeout(() => { fetch('/digits/1.png'); fetch('/digits/2.png'); fetch('/digits/3.png'); - }, 50) - ), + }, 50); + }), ]); expect(response.url()).toBe(server.PREFIX + '/digits/2.png'); }); @@ -890,19 +1035,25 @@ describe('Page', function () { return Date.now(); }), page - .evaluate(() => - (async () => { + .evaluate(() => { + return (async () => { await Promise.all([ fetch('/digits/1.png'), fetch('/digits/2.png'), ]); - await new Promise((resolve) => setTimeout(resolve, 200)); + await new Promise((resolve) => { + return setTimeout(resolve, 200); + }); await fetch('/digits/3.png'); - await new Promise((resolve) => setTimeout(resolve, 200)); + await new Promise((resolve) => { + return setTimeout(resolve, 200); + }); await fetch('/digits/4.png'); - })() - ) - .then(() => Date.now()), + })(); + }) + .then(() => { + return Date.now(); + }), ]); expect(res).toBe(undefined); expect(t1).toBeGreaterThan(t2); @@ -910,28 +1061,34 @@ describe('Page', function () { }); it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; - await page - .waitForNetworkIdle({ timeout: 1 }) - .catch((error_) => (error = error_)); + let error!: Error; + await page.waitForNetworkIdle({ timeout: 1 }).catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should respect idleTime', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); const [t1, t2] = await Promise.all([ - page.waitForNetworkIdle({ idleTime: 10 }).then(() => Date.now()), + page.waitForNetworkIdle({ idleTime: 10 }).then(() => { + return Date.now(); + }), page - .evaluate(() => - (async () => { + .evaluate(() => { + return (async () => { await Promise.all([ fetch('/digits/1.png'), fetch('/digits/2.png'), ]); - await new Promise((resolve) => setTimeout(resolve, 250)); - })() - ) - .then(() => Date.now()), + await new Promise((resolve) => { + return setTimeout(resolve, 250); + }); + })(); + }) + .then(() => { + return Date.now(); + }), ]); expect(t2).toBeGreaterThan(t1); }); @@ -940,13 +1097,13 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const [result] = await Promise.all([ page.waitForNetworkIdle({ timeout: 0 }), - page.evaluate(() => - setTimeout(() => { + page.evaluate(() => { + return setTimeout(() => { fetch('/digits/1.png'); fetch('/digits/2.png'); fetch('/digits/3.png'); - }, 50) - ), + }, 50); + }), ]); expect(result).toBe(undefined); }); @@ -956,11 +1113,11 @@ describe('Page', function () { it('should work', async () => { const { page } = getTestState(); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return a * b; }); const result = await page.evaluate(async function () { - return await globalThis.compute(9, 4); + return await (globalThis as any).compute(9, 4); }); expect(result).toBe(36); }); @@ -972,9 +1129,12 @@ describe('Page', function () { }); const { message, stack } = await page.evaluate(async () => { try { - await globalThis.woof(); + return await (globalThis as any).woof(); } catch (error) { - return { message: error.message, stack: error.stack }; + return { + message: (error as Error).message, + stack: (error as Error).stack, + }; } }); expect(message).toBe('WOOF WOOF'); @@ -988,7 +1148,7 @@ describe('Page', function () { }); const thrown = await page.evaluate(async () => { try { - await globalThis.woof(); + await (globalThis as any).woof(); } catch (error) { return error; } @@ -1002,46 +1162,48 @@ describe('Page', function () { await page.exposeFunction('woof', function () { called = true; }); - await page.evaluateOnNewDocument(() => globalThis.woof()); + await page.evaluateOnNewDocument(() => { + return (globalThis as any).woof(); + }); await page.reload(); expect(called).toBe(true); }); it('should survive navigation', async () => { const { page, server } = getTestState(); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return a * b; }); await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async function () { - return await globalThis.compute(9, 4); + return await (globalThis as any).compute(9, 4); }); expect(result).toBe(36); }); it('should await returned promise', async () => { const { page } = getTestState(); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return Promise.resolve(a * b); }); const result = await page.evaluate(async function () { - return await globalThis.compute(3, 5); + return await (globalThis as any).compute(3, 5); }); expect(result).toBe(15); }); it('should work on frames', async () => { const { page, server } = getTestState(); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return Promise.resolve(a * b); }); await page.goto(server.PREFIX + '/frames/nested-frames.html'); - const frame = page.frames()[1]; + const frame = page.frames()[1]!; const result = await frame.evaluate(async function () { - return await globalThis.compute(3, 5); + return await (globalThis as any).compute(3, 5); }); expect(result).toBe(15); }); @@ -1049,13 +1211,13 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/frames/nested-frames.html'); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return Promise.resolve(a * b); }); - const frame = page.frames()[1]; + const frame = page.frames()[1]!; const result = await frame.evaluate(async function () { - return await globalThis.compute(3, 5); + return await (globalThis as any).compute(3, 5); }); expect(result).toBe(15); }); @@ -1064,39 +1226,44 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await page.exposeFunction('compute', function (a, b) { + await page.exposeFunction('compute', function (a: number, b: number) { return Promise.resolve(a * b); }); await utils.detachFrame(page, 'frame1'); await expect( page.evaluate(async function () { - return await globalThis.compute(3, 5); + return await (globalThis as any).compute(3, 5); }) ).resolves.toEqual(15); }); it('should work with complex objects', async () => { const { page } = getTestState(); - await page.exposeFunction('complexObject', function (a, b) { - return { x: a.x + b.x }; - }); + await page.exposeFunction( + 'complexObject', + function (a: { x: any }, b: { x: any }) { + return { x: a.x + b.x }; + } + ); const result = await page.evaluate<() => Promise<{ x: number }>>( - async () => globalThis.complexObject({ x: 5 }, { x: 2 }) + async () => { + return (globalThis as any).complexObject({ x: 5 }, { x: 2 }); + } ); expect(result.x).toBe(7); }); it('should fallback to default export when passed a module object', async () => { const { page, server } = getTestState(); const moduleObject = { - default: function (a, b) { + default: function (a: number, b: number) { return a * b; }, }; await page.goto(server.EMPTY_PAGE); await page.exposeFunction('compute', moduleObject); const result = await page.evaluate(async function () { - return await globalThis.compute(9, 4); + return await (globalThis as any).compute(9, 4); }); expect(result).toBe(36); }); @@ -1106,8 +1273,10 @@ describe('Page', function () { it('should fire', async () => { const { page, server } = getTestState(); - let error = null; - page.once('pageerror', (e) => (error = e)); + let error!: Error; + page.once('pageerror', (e) => { + return (error = e); + }); await Promise.all([ page.goto(server.PREFIX + '/error.html'), waitEvent(page, 'pageerror'), @@ -1120,9 +1289,11 @@ describe('Page', function () { it('should work', async () => { const { page, server } = getTestState(); - expect(await page.evaluate(() => navigator.userAgent)).toContain( - 'Mozilla' - ); + expect( + await page.evaluate(() => { + return navigator.userAgent; + }) + ).toContain('Mozilla'); await page.setUserAgent('foobar'); const [request] = await Promise.all([ server.waitForRequest('/empty.html'), @@ -1133,9 +1304,11 @@ describe('Page', function () { it('should work for subframes', async () => { const { page, server } = getTestState(); - expect(await page.evaluate(() => navigator.userAgent)).toContain( - 'Mozilla' - ); + expect( + await page.evaluate(() => { + return navigator.userAgent; + }) + ).toContain('Mozilla'); await page.setUserAgent('foobar'); const [request] = await Promise.all([ server.waitForRequest('/empty.html'), @@ -1147,13 +1320,17 @@ describe('Page', function () { const { page, server, puppeteer } = getTestState(); await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => navigator.userAgent)).not.toContain( - 'iPhone' - ); - await page.setUserAgent(puppeteer.devices['iPhone 6'].userAgent); - expect(await page.evaluate(() => navigator.userAgent)).toContain( - 'iPhone' - ); + expect( + await page.evaluate(() => { + return navigator.userAgent; + }) + ).not.toContain('iPhone'); + await page.setUserAgent(puppeteer.devices['iPhone 6']!.userAgent); + expect( + await page.evaluate(() => { + return navigator.userAgent; + }) + ).toContain('iPhone'); }); itFailsFirefox('should work with additional userAgentMetdata', async () => { const { page, server } = getTestState(); @@ -1229,12 +1406,14 @@ describe('Page', function () { const imgPath = '/img.png'; // stall for image server.setRoute(imgPath, () => {}); - let error = null; + let error!: Error; await page .setContent(``, { timeout: 1, }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should respect default navigation timeout', async () => { @@ -1244,22 +1423,28 @@ describe('Page', function () { const imgPath = '/img.png'; // stall for image server.setRoute(imgPath, () => {}); - let error = null; + let error!: Error; await page .setContent(``) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should await resources to load', async () => { const { page, server } = getTestState(); const imgPath = '/img.png'; - let imgResponse = null; - server.setRoute(imgPath, (req, res) => (imgResponse = res)); + let imgResponse!: ServerResponse; + server.setRoute(imgPath, (_req, res) => { + return (imgResponse = res); + }); let loaded = false; const contentPromise = page .setContent(``) - .then(() => (loaded = true)); + .then(() => { + return (loaded = true); + }); await server.waitForRequest(imgPath); expect(loaded).toBe(false); imgResponse.end(); @@ -1276,29 +1461,41 @@ describe('Page', function () { const { page } = getTestState(); await page.setContent('
hello world
' + '\x7F'); - expect(await page.$eval('div', (div) => div.textContent)).toBe( - 'hello world' - ); + expect( + await page.$eval('div', (div) => { + return div.textContent; + }) + ).toBe('hello world'); }); it('should work with accents', async () => { const { page } = getTestState(); await page.setContent('
aberración
'); - expect(await page.$eval('div', (div) => div.textContent)).toBe( - 'aberración' - ); + expect( + await page.$eval('div', (div) => { + return div.textContent; + }) + ).toBe('aberración'); }); it('should work with emojis', async () => { const { page } = getTestState(); await page.setContent('
🐥
'); - expect(await page.$eval('div', (div) => div.textContent)).toBe('🐥'); + expect( + await page.$eval('div', (div) => { + return div.textContent; + }) + ).toBe('🐥'); }); it('should work with newline', async () => { const { page } = getTestState(); await page.setContent('
\n
'); - expect(await page.$eval('div', (div) => div.textContent)).toBe('\n'); + expect( + await page.$eval('div', (div) => { + return div.textContent; + }) + ).toBe('\n'); }); }); @@ -1310,14 +1507,24 @@ describe('Page', function () { await page.goto(server.PREFIX + '/csp.html'); await page .addScriptTag({ content: 'window.__injected = 42;' }) - .catch((error) => void error); - expect(await page.evaluate(() => globalThis.__injected)).toBe(undefined); + .catch((error) => { + return void error; + }); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(undefined); // By-pass CSP and try one more time. await page.setBypassCSP(true); await page.reload(); await page.addScriptTag({ content: 'window.__injected = 42;' }); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); }); it('should bypass CSP header', async () => { @@ -1328,14 +1535,24 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); await page .addScriptTag({ content: 'window.__injected = 42;' }) - .catch((error) => void error); - expect(await page.evaluate(() => globalThis.__injected)).toBe(undefined); + .catch((error) => { + return void error; + }); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(undefined); // By-pass CSP and try one more time. await page.setBypassCSP(true); await page.reload(); await page.addScriptTag({ content: 'window.__injected = 42;' }); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); }); it('should bypass after cross-process navigation', async () => { @@ -1344,11 +1561,19 @@ describe('Page', function () { await page.setBypassCSP(true); await page.goto(server.PREFIX + '/csp.html'); await page.addScriptTag({ content: 'window.__injected = 42;' }); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); await page.goto(server.CROSS_PROCESS_PREFIX + '/csp.html'); await page.addScriptTag({ content: 'window.__injected = 42;' }); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); }); it('should bypass CSP in iframes as well', async () => { const { page, server } = getTestState(); @@ -1356,17 +1581,21 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); { // Make sure CSP prohibits addScriptTag in an iframe. - const frame = await utils.attachFrame( + const frame = (await attachFrame( page, 'frame1', server.PREFIX + '/csp.html' - ); + ))!; await frame .addScriptTag({ content: 'window.__injected = 42;' }) - .catch((error) => void error); - expect(await frame.evaluate(() => globalThis.__injected)).toBe( - undefined - ); + .catch((error) => { + return void error; + }); + expect( + await frame.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(undefined); } // By-pass CSP and try one more time. @@ -1374,15 +1603,21 @@ describe('Page', function () { await page.reload(); { - const frame = await utils.attachFrame( + const frame = (await attachFrame( page, 'frame1', server.PREFIX + '/csp.html' - ); + ))!; await frame .addScriptTag({ content: 'window.__injected = 42;' }) - .catch((error) => void error); - expect(await frame.evaluate(() => globalThis.__injected)).toBe(42); + .catch((error) => { + return void error; + }); + expect( + await frame.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); } }); }); @@ -1391,12 +1626,12 @@ describe('Page', function () { it('should throw an error if no options are provided', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { // @ts-expect-error purposefully passing bad options await page.addScriptTag('/injectedfile.js'); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe( 'Provide an object with a `url`, `path` or `content` property' @@ -1409,7 +1644,11 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ url: '/injectedfile.js' }); expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); }); it('should work with a url and type=module', async () => { @@ -1417,7 +1656,11 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ url: '/es6/es6import.js', type: 'module' }); - expect(await page.evaluate(() => globalThis.__es6injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__es6injected; + }) + ).toBe(42); }); it('should work with a path and type=module', async () => { @@ -1429,7 +1672,11 @@ describe('Page', function () { type: 'module', }); await page.waitForFunction('window.__es6injected'); - expect(await page.evaluate(() => globalThis.__es6injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__es6injected; + }) + ).toBe(42); }); it('should work with a content and type=module', async () => { @@ -1441,18 +1688,22 @@ describe('Page', function () { type: 'module', }); await page.waitForFunction('window.__es6injected'); - expect(await page.evaluate(() => globalThis.__es6injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__es6injected; + }) + ).toBe(42); }); it('should throw an error if loading from url fail', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - let error = null; + let error!: Error; try { await page.addScriptTag({ url: '/nonexistfile.js' }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe('Loading script from /nonexistfile.js failed'); }); @@ -1465,7 +1716,11 @@ describe('Page', function () { path: path.join(__dirname, '../assets/injectedfile.js'), }); expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => globalThis.__injected)).toBe(42); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(42); }); it('should include sourcemap when path is provided', async () => { @@ -1475,9 +1730,9 @@ describe('Page', function () { await page.addScriptTag({ path: path.join(__dirname, '../assets/injectedfile.js'), }); - const result = await page.evaluate( - () => globalThis.__injectedError.stack - ); + const result = await page.evaluate(() => { + return (globalThis as any).__injectedError.stack; + }); expect(result).toContain(path.join('assets', 'injectedfile.js')); }); @@ -1489,7 +1744,11 @@ describe('Page', function () { content: 'window.__injected = 35;', }); expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => globalThis.__injected)).toBe(35); + expect( + await page.evaluate(() => { + return (globalThis as any).__injected; + }) + ).toBe(35); }); it('should add id when provided', async () => { @@ -1506,10 +1765,12 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/csp.html'); - let error = null; + let error!: Error; await page .addScriptTag({ content: 'window.__injected = 35;' }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); }); @@ -1517,10 +1778,12 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/csp.html'); - let error = null; + let error!: Error; await page .addScriptTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedfile.js' }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); }); }); @@ -1529,12 +1792,12 @@ describe('Page', function () { it('should throw an error if no options are provided', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { // @ts-expect-error purposefully passing bad input await page.addStyleTag('/injectedstyle.css'); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe( 'Provide an object with a `url`, `path` or `content` property' @@ -1558,11 +1821,11 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - let error = null; + let error!: Error; try { await page.addStyleTag({ url: '/nonexistfile.js' }); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toBe('Loading style from /nonexistfile.js failed'); }); @@ -1589,11 +1852,10 @@ describe('Page', function () { await page.addStyleTag({ path: path.join(__dirname, '../assets/injectedstyle.css'), }); - const styleHandle = await page.$('style'); - const styleContent = await page.evaluate( - (style: HTMLStyleElement) => style.innerHTML, - styleHandle - ); + const styleHandle = (await page.$('style'))!; + const styleContent = await page.evaluate((style: HTMLStyleElement) => { + return style.innerHTML; + }, styleHandle); expect(styleContent).toContain(path.join('assets', 'injectedstyle.css')); }); @@ -1618,10 +1880,12 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/csp.html'); - let error = null; + let error!: Error; await page .addStyleTag({ content: 'body { background-color: green; }' }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); } ); @@ -1630,12 +1894,14 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/csp.html'); - let error = null; + let error!: Error; await page .addStyleTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedstyle.css', }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); }); }); @@ -1658,8 +1924,10 @@ describe('Page', function () { await page.goto( 'data:text/html, ' ); - let error = null; - await page.evaluate('something').catch((error_) => (error = error_)); + let error!: Error; + await page.evaluate('something').catch((error_) => { + return (error = error_); + }); expect(error.message).toContain('something is not defined'); await page.setJavaScriptEnabled(true); @@ -1747,8 +2015,10 @@ describe('Page', function () { await page.goto(server.PREFIX + '/pdf.html'); - let error = null; - await page.pdf({ timeout: 1 }).catch((_error) => (error = _error)); + let error!: Error; + await page.pdf({ timeout: 1 }).catch((_error) => { + return (error = _error); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); }); @@ -1768,35 +2038,42 @@ describe('Page', function () { await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); - expect(await page.evaluate(() => globalThis.result.onInput)).toEqual([ - 'blue', - ]); - expect(await page.evaluate(() => globalThis.result.onChange)).toEqual([ - 'blue', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onInput; + }) + ).toEqual(['blue']); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onChange; + }) + ).toEqual(['blue']); }); it('should select only first option', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue', 'green', 'red'); - expect(await page.evaluate(() => globalThis.result.onInput)).toEqual([ - 'blue', - ]); - expect(await page.evaluate(() => globalThis.result.onChange)).toEqual([ - 'blue', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onInput; + }) + ).toEqual(['blue']); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onChange; + }) + ).toEqual(['blue']); }); it('should not throw when select causes navigation', async () => { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); - await page.$eval('select', (select) => - select.addEventListener( - 'input', - () => ((window as any).location = '/empty.html') - ) - ); + await page.$eval('select', (select) => { + return select.addEventListener('input', () => { + return ((window as any).location = '/empty.html'); + }); + }); await Promise.all([ page.select('select', 'blue'), page.waitForNavigation(), @@ -1807,18 +2084,20 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => globalThis.makeMultiple()); + await page.evaluate(() => { + return (globalThis as any).makeMultiple(); + }); await page.select('select', 'blue', 'green', 'red'); - expect(await page.evaluate(() => globalThis.result.onInput)).toEqual([ - 'blue', - 'green', - 'red', - ]); - expect(await page.evaluate(() => globalThis.result.onChange)).toEqual([ - 'blue', - 'green', - 'red', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onInput; + }) + ).toEqual(['blue', 'green', 'red']); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onChange; + }) + ).toEqual(['blue', 'green', 'red']); }); it('should respect event bubbling', async () => { const { page, server } = getTestState(); @@ -1826,18 +2105,24 @@ describe('Page', function () { await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); expect( - await page.evaluate(() => globalThis.result.onBubblingInput) + await page.evaluate(() => { + return (globalThis as any).result.onBubblingInput; + }) ).toEqual(['blue']); expect( - await page.evaluate(() => globalThis.result.onBubblingChange) + await page.evaluate(() => { + return (globalThis as any).result.onBubblingChange; + }) ).toEqual(['blue']); }); it('should throw when element is not a element.'); }); it('should return [] on no matched values', async () => { @@ -1851,14 +2136,14 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => globalThis.makeMultiple()); + await page.evaluate(() => { + return (globalThis as any).makeMultiple(); + }); const result = await page.select('select', 'blue', 'black', 'magenta'); expect( - result.reduce( - (accumulator, current) => - ['blue', 'black', 'magenta'].includes(current) && accumulator, - true - ) + result.reduce((accumulator, current) => { + return ['blue', 'black', 'magenta'].includes(current) && accumulator; + }, true) ).toEqual(true); }); it('should return an array of one element when multiple is not set', async () => { @@ -1885,15 +2170,19 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => globalThis.makeMultiple()); + await page.evaluate(() => { + return (globalThis as any).makeMultiple(); + }); await page.select('select', 'blue', 'black', 'magenta'); await page.select('select'); expect( - await page.$eval('select', (select: HTMLSelectElement) => - Array.from(select.options).every( - (option: HTMLOptionElement) => !option.selected - ) - ) + await page.$eval('select', (select) => { + return Array.from((select as HTMLSelectElement).options).every( + (option) => { + return !option.selected; + } + ); + }) ).toEqual(true); }); it('should deselect all options when passed no values for a select without multiple', async () => { @@ -1903,24 +2192,25 @@ describe('Page', function () { await page.select('select', 'blue', 'black', 'magenta'); await page.select('select'); expect( - await page.$eval( - 'select', - (select: HTMLSelectElement) => - Array.from(select.options).filter((option) => option.selected)[0] - .value - ) + await page.$eval('select', (select) => { + return Array.from((select as HTMLSelectElement).options).filter( + (option) => { + return option.selected; + } + )[0]!.value; + }) ).toEqual(''); }); it('should throw if passed in non-strings', async () => { const { page } = getTestState(); await page.setContent(''); - let error = null; + let error!: Error; try { // @ts-expect-error purposefully passing bad input await page.select('select', 12); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error.message).toContain('Values must be strings'); }); @@ -1931,14 +2221,21 @@ describe('Page', function () { const { page, server } = getTestState(); await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => (window.Event = null)); + await page.evaluate(() => { + // @ts-expect-error Expected. + return (window.Event = undefined); + }); await page.select('select', 'blue'); - expect(await page.evaluate(() => globalThis.result.onInput)).toEqual([ - 'blue', - ]); - expect(await page.evaluate(() => globalThis.result.onChange)).toEqual([ - 'blue', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onInput; + }) + ).toEqual(['blue']); + expect( + await page.evaluate(() => { + return (globalThis as any).result.onChange; + }) + ).toEqual(['blue']); } ); }); @@ -1947,22 +2244,30 @@ describe('Page', function () { itFailsFirefox('should work with window.close', async () => { const { page, context } = getTestState(); - const newPagePromise = new Promise((fulfill) => - context.once('targetcreated', (target) => fulfill(target.page())) - ); - await page.evaluate( - () => (window['newPage'] = window.open('about:blank')) - ); + const newPagePromise = new Promise((fulfill) => { + return context.once('targetcreated', (target) => { + return fulfill(target.page()); + }); + }); + await page.evaluate(() => { + return ((window as any)['newPage'] = window.open('about:blank')); + }); const newPage = await newPagePromise; - const closedPromise = new Promise((x) => newPage.on('close', x)); - await page.evaluate(() => window['newPage'].close()); + const closedPromise = new Promise((x) => { + return newPage.on('close', x); + }); + await page.evaluate(() => { + return (window as any)['newPage'].close(); + }); await closedPromise; }); it('should work with page.close', async () => { const { context } = getTestState(); const newPage = await context.newPage(); - const closedPromise = new Promise((x) => newPage.on('close', x)); + const closedPromise = new Promise((x) => { + return newPage.on('close', x); + }); await newPage.close(); await closedPromise; }); diff --git a/test/src/proxy.spec.ts b/test/src/proxy.spec.ts index 3c102fe6cb2..f6ddaf79aed 100644 --- a/test/src/proxy.spec.ts +++ b/test/src/proxy.spec.ts @@ -21,7 +21,7 @@ import { getTestState, describeFailsFirefox, itFailsWindows, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import type { Server, IncomingMessage, ServerResponse } from 'http'; import type { Browser } from '../../lib/cjs/puppeteer/common/Browser.js'; import type { AddressInfo } from 'net'; @@ -108,7 +108,7 @@ describeFailsFirefox('request proxy', () => { }); const page = await browser.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); @@ -129,7 +129,7 @@ describeFailsFirefox('request proxy', () => { }); const page = await browser.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); @@ -148,7 +148,7 @@ describeFailsFirefox('request proxy', () => { const context = await browser.createIncognitoBrowserContext(); const page = await context.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); @@ -170,7 +170,7 @@ describeFailsFirefox('request proxy', () => { const context = await browser.createIncognitoBrowserContext(); const page = await context.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); @@ -195,7 +195,7 @@ describeFailsFirefox('request proxy', () => { proxyServer: proxyServerUrl, }); const page = await context.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); @@ -217,7 +217,7 @@ describeFailsFirefox('request proxy', () => { proxyBypassList: [new URL(emptyPageUrl).host], }); const page = await context.newPage(); - const response = await page.goto(emptyPageUrl); + const response = (await page.goto(emptyPageUrl))!; expect(response.ok()).toBe(true); diff --git a/test/src/queryselector.spec.ts b/test/src/queryselector.spec.ts index 08f9363e72b..cdf3fa74848 100644 --- a/test/src/queryselector.spec.ts +++ b/test/src/queryselector.spec.ts @@ -18,7 +18,7 @@ import { getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; import { CustomQueryHandler } from '../../lib/cjs/puppeteer/common/QueryHandler.js'; describe('querySelector', function () { @@ -29,7 +29,9 @@ describe('querySelector', function () { const { page } = getTestState(); await page.setContent('
43543
'); - const idAttribute = await page.$eval('section', (e) => e.id); + const idAttribute = await page.$eval('section', (e) => { + return e.id; + }); expect(idAttribute).toBe('testAttribute'); }); it('should accept arguments', async () => { @@ -38,7 +40,9 @@ describe('querySelector', function () { await page.setContent('
hello
'); const text = await page.$eval( 'section', - (e, suffix) => e.textContent + suffix, + (e, suffix) => { + return e.textContent! + suffix; + }, ' world!' ); expect(text).toBe('hello world!'); @@ -47,10 +51,12 @@ describe('querySelector', function () { const { page } = getTestState(); await page.setContent('
hello
world
'); - const divHandle = await page.$('div'); + const divHandle = (await page.$('div'))!; const text = await page.$eval( 'section', - (e, div: HTMLElement) => e.textContent + div.textContent, + (e, div) => { + return e.textContent! + (div as HTMLElement).textContent!; + }, divHandle ); expect(text).toBe('hello world'); @@ -58,10 +64,14 @@ describe('querySelector', function () { it('should throw error if no element is found', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; await page - .$eval('section', (e) => e.id) - .catch((error_) => (error = error_)); + .$eval('section', (e) => { + return e.id; + }) + .catch((error_) => { + return (error = error_); + }); expect(error.message).toContain( 'failed to find element matching selector "section"' ); @@ -89,39 +99,43 @@ describe('querySelector', function () { }); it('should find first element in shadow', async () => { const { page } = getTestState(); - const div = await page.$('pierce/.foo'); - const text = await div.evaluate( - (element: Element) => element.textContent - ); + const div = (await page.$('pierce/.foo'))!; + const text = await div.evaluate((element: Element) => { + return element.textContent; + }); expect(text).toBe('Hello'); }); it('should find all elements in shadow', async () => { const { page } = getTestState(); const divs = await page.$$('pierce/.foo'); const text = await Promise.all( - divs.map((div) => - div.evaluate((element: Element) => element.textContent) - ) + divs.map((div) => { + return div.evaluate((element: Element) => { + return element.textContent; + }); + }) ); expect(text.join(' ')).toBe('Hello World'); }); it('should find first child element', async () => { const { page } = getTestState(); - const parentElement = await page.$('html > div'); - const childElement = await parentElement.$('pierce/div'); - const text = await childElement.evaluate( - (element: Element) => element.textContent - ); + const parentElement = (await page.$('html > div'))!; + const childElement = (await parentElement.$('pierce/div'))!; + const text = await childElement.evaluate((element: Element) => { + return element.textContent; + }); expect(text).toBe('Hello'); }); it('should find all child elements', async () => { const { page } = getTestState(); - const parentElement = await page.$('html > div'); + const parentElement = (await page.$('html > div'))!; const childElements = await parentElement.$$('pierce/div'); const text = await Promise.all( - childElements.map((div) => - div.evaluate((element: Element) => element.textContent) - ) + childElements.map((div) => { + return div.evaluate((element: Element) => { + return element.textContent; + }); + }) ); expect(text.join(' ')).toBe('Hello World'); }); @@ -137,7 +151,9 @@ describe('querySelector', function () { await page.setContent( '
hello
beautiful
world!
' ); - const divsCount = await page.$$eval('div', (divs) => divs.length); + const divsCount = await page.$$eval('div', (divs) => { + return divs.length; + }); expect(divsCount).toBe(3); }); it('should accept extra arguments', async () => { @@ -147,7 +163,9 @@ describe('querySelector', function () { ); const divsCountPlus5 = await page.$$eval( 'div', - (divs, two: number, three: number) => divs.length + two + three, + (divs, two, three) => { + return divs.length + (two as number) + (three as number); + }, 2, 3 ); @@ -158,14 +176,16 @@ describe('querySelector', function () { await page.setContent( '
2
2
1
3
' ); - const divHandle = await page.$('div'); + const divHandle = (await page.$('div'))!; const sum = await page.$$eval( 'section', - (sections, div: HTMLElement) => - sections.reduce( - (acc, section) => acc + Number(section.textContent), - 0 - ) + Number(div.textContent), + (sections, div) => { + return ( + sections.reduce((acc, section) => { + return acc + Number(section.textContent); + }, 0) + Number((div as HTMLElement).textContent) + ); + }, divHandle ); expect(sum).toBe(8); @@ -181,9 +201,11 @@ describe('querySelector', function () { } ` ); - const sum = await page.$$eval('section', (sections) => - sections.reduce((acc, section) => acc + Number(section.textContent), 0) - ); + const sum = await page.$$eval('section', (sections) => { + return sections.reduce((acc, section) => { + return acc + Number(section.textContent); + }, 0); + }); expect(sum).toBe(500500); }); }); @@ -193,13 +215,13 @@ describe('querySelector', function () { const { page } = getTestState(); await page.setContent('
test
'); - const element = await page.$('section'); + const element = (await page.$('section'))!; expect(element).toBeTruthy(); }); it('should return null for non-existing element', async () => { const { page } = getTestState(); - const element = await page.$('non-existing-element'); + const element = (await page.$('non-existing-element'))!; expect(element).toBe(null); }); }); @@ -211,9 +233,11 @@ describe('querySelector', function () { await page.setContent('
A

B
'); const elements = await page.$$('div'); expect(elements.length).toBe(2); - const promises = elements.map((element) => - page.evaluate((e: HTMLElement) => e.textContent, element) - ); + const promises = elements.map((element) => { + return page.evaluate((e: HTMLElement) => { + return e.textContent; + }, element); + }); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); it('should return empty array if nothing is found', async () => { @@ -231,7 +255,7 @@ describe('querySelector', function () { await page.setContent('
test
'); const elements = await page.$x('/html/body/section'); - expect(elements[0]).toBeTruthy(); + expect(elements[0]!).toBeTruthy(); expect(elements.length).toBe(1); }); it('should return empty array for non-existing element', async () => { @@ -257,13 +281,12 @@ describe('querySelector', function () { await page.setContent( '
A
' ); - const html = await page.$('html'); - const second = await html.$('.second'); + const html = (await page.$('html'))!; + const second = (await html.$('.second'))!; const inner = await second.$('.inner'); - const content = await page.evaluate( - (e: HTMLElement) => e.textContent, - inner - ); + const content = await page.evaluate((e: HTMLElement) => { + return e.textContent; + }, inner); expect(content).toBe('A'); }); @@ -273,7 +296,7 @@ describe('querySelector', function () { await page.setContent( '
B
' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const second = await html.$('.third'); expect(second).toBe(null); }); @@ -285,11 +308,10 @@ describe('querySelector', function () { await page.setContent( '
10
' ); - const tweet = await page.$('.tweet'); - const content = await tweet.$eval( - '.like', - (node: HTMLElement) => node.innerText - ); + const tweet = (await page.$('.tweet'))!; + const content = await tweet.$eval('.like', (node) => { + return (node as HTMLElement).innerText; + }); expect(content).toBe('100'); }); @@ -299,11 +321,10 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
a-child-div
'; await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const content = await elementHandle.$eval( - '.a', - (node: HTMLElement) => node.innerText - ); + const elementHandle = (await page.$('#myId'))!; + const content = await elementHandle.$eval('.a', (node) => { + return (node as HTMLElement).innerText; + }); expect(content).toBe('a-child-div'); }); @@ -313,10 +334,14 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
'; await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); + const elementHandle = (await page.$('#myId'))!; const errorMessage = await elementHandle - .$eval('.a', (node: HTMLElement) => node.innerText) - .catch((error) => error.message); + .$eval('.a', (node) => { + return (node as HTMLElement).innerText; + }) + .catch((error) => { + return error.message; + }); expect(errorMessage).toBe( `Error: failed to find element matching selector ".a"` ); @@ -329,10 +354,12 @@ describe('querySelector', function () { await page.setContent( '
' ); - const tweet = await page.$('.tweet'); - const content = await tweet.$$eval('.like', (nodes: HTMLElement[]) => - nodes.map((n) => n.innerText) - ); + const tweet = (await page.$('.tweet'))!; + const content = await tweet.$$eval('.like', (nodes) => { + return (nodes as HTMLElement[]).map((n) => { + return n.innerText; + }); + }); expect(content).toEqual(['100', '10']); }); @@ -342,10 +369,12 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
a1-child-div
a2-child-div
'; await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const content = await elementHandle.$$eval('.a', (nodes: HTMLElement[]) => - nodes.map((n) => n.innerText) - ); + const elementHandle = (await page.$('#myId'))!; + const content = await elementHandle.$$eval('.a', (nodes) => { + return (nodes as HTMLElement[]).map((n) => { + return n.innerText; + }); + }); expect(content).toEqual(['a1-child-div', 'a2-child-div']); }); @@ -355,11 +384,10 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
'; await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const nodesLength = await elementHandle.$$eval( - '.a', - (nodes) => nodes.length - ); + const elementHandle = (await page.$('#myId'))!; + const nodesLength = await elementHandle.$$eval('.a', (nodes) => { + return nodes.length; + }); expect(nodesLength).toBe(0); }); }); @@ -371,12 +399,14 @@ describe('querySelector', function () { await page.setContent( '
A

B
' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const elements = await html.$$('div'); expect(elements.length).toBe(2); - const promises = elements.map((element) => - page.evaluate((e: HTMLElement) => e.textContent, element) - ); + const promises = elements.map((element) => { + return page.evaluate((e: HTMLElement) => { + return e.textContent; + }, element); + }); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); @@ -386,7 +416,7 @@ describe('querySelector', function () { await page.setContent( 'A
B' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const elements = await html.$$('div'); expect(elements.length).toBe(0); }); @@ -400,13 +430,12 @@ describe('querySelector', function () { await page.setContent( '
A
' ); - const html = await page.$('html'); + 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: HTMLElement) => e.textContent, - inner[0] - ); + const inner = await second[0]!.$x(`./div[contains(@class, 'inner')]`); + const content = await page.evaluate((e: HTMLElement) => { + return e.textContent; + }, inner[0]!); expect(content).toBe('A'); }); @@ -416,7 +445,7 @@ describe('querySelector', function () { await page.setContent( '
B
' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const second = await html.$x(`/div[contains(@class, 'third')]`); expect(second).toEqual([]); }); @@ -426,8 +455,9 @@ describe('querySelector', function () { // handler that returns an array instead of a list of nodes. describe('QueryAll', function () { const handler: CustomQueryHandler = { - queryAll: (element: Element, selector: string) => - Array.from(element.querySelectorAll(selector)), + queryAll: (element, selector) => { + return Array.from(element.querySelectorAll(selector)); + }, }; before(() => { const { puppeteer } = getTestState(); @@ -446,12 +476,14 @@ describe('querySelector', function () { await page.setContent( '
A

B
' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const elements = await html.$$('allArray/div'); expect(elements.length).toBe(2); - const promises = elements.map((element) => - page.evaluate((e: HTMLElement) => e.textContent, element) - ); + const promises = elements.map((element) => { + return page.evaluate((e: HTMLElement) => { + return e.textContent; + }, element); + }); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); @@ -461,7 +493,7 @@ describe('querySelector', function () { await page.setContent( 'A
B' ); - const html = await page.$('html'); + const html = (await page.$('html'))!; const elements = await html.$$('allArray/div'); expect(elements.length).toBe(0); }); @@ -471,10 +503,9 @@ describe('querySelector', function () { await page.setContent( '
hello
beautiful
world!
' ); - const divsCount = await page.$$eval( - 'allArray/div', - (divs) => divs.length - ); + const divsCount = await page.$$eval('allArray/div', (divs) => { + return divs.length; + }); expect(divsCount).toBe(3); }); it('$$eval should accept extra arguments', async () => { @@ -484,7 +515,9 @@ describe('querySelector', function () { ); const divsCountPlus5 = await page.$$eval( 'allArray/div', - (divs, two: number, three: number) => divs.length + two + three, + (divs, two, three) => { + return divs.length + (two as number) + (three as number); + }, 2, 3 ); @@ -495,14 +528,16 @@ describe('querySelector', function () { await page.setContent( '
2
2
1
3
' ); - const divHandle = await page.$('div'); + const divHandle = (await page.$('div'))!; const sum = await page.$$eval( 'allArray/section', - (sections, div: HTMLElement) => - sections.reduce( - (acc, section) => acc + Number(section.textContent), - 0 - ) + Number(div.textContent), + (sections, div) => { + return ( + sections.reduce((acc, section) => { + return acc + Number(section.textContent); + }, 0) + Number((div as HTMLElement).textContent) + ); + }, divHandle ); expect(sum).toBe(8); @@ -518,9 +553,11 @@ describe('querySelector', function () { } ` ); - const sum = await page.$$eval('allArray/section', (sections) => - sections.reduce((acc, section) => acc + Number(section.textContent), 0) - ); + const sum = await page.$$eval('allArray/section', (sections) => { + return sections.reduce((acc, section) => { + return acc + Number(section.textContent); + }, 0); + }); expect(sum).toBe(500500); }); }); diff --git a/test/src/requestinterception-experimental.spec.ts b/test/src/requestinterception-experimental.spec.ts index 2365764bac1..689379cc9a8 100644 --- a/test/src/requestinterception-experimental.spec.ts +++ b/test/src/requestinterception-experimental.spec.ts @@ -23,9 +23,13 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions -import { ActionResult } from '../../lib/cjs/puppeteer/api-docs-entry.js'; -import { InterceptResolutionAction } from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; +} from './mocha-utils.js'; +import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; +import { + ActionResult, + HTTPRequest, + InterceptResolutionAction, +} from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; describe('request interception', function () { setupTestBrowserHooks(); @@ -67,8 +71,8 @@ describe('request interception', function () { } }); page.on('response', (response) => { - const { xaction } = response.headers(); - if (response.url().endsWith('.css') && !!xaction) { + const { xaction } = response!.headers(); + if (response!.url().endsWith('.css') && !!xaction) { actionResults.push(xaction as ActionResult); } }); @@ -78,22 +82,24 @@ describe('request interception', function () { } }); - const response = await (async () => { + const response = (await (async () => { if (expectedAction === 'continue') { const [serverRequest, response] = await Promise.all([ server.waitForRequest('/one-style.css'), page.goto(server.PREFIX + '/one-style.html'), ]); - actionResults.push(serverRequest.headers.xaction as ActionResult); + actionResults.push( + serverRequest.headers['xaction'] as ActionResult + ); return response; } else { return await page.goto(server.PREFIX + '/one-style.html'); } - })(); + })())!; expect(actionResults.length).toBe(1); - expect(actionResults[0]).toBe(expectedAction); - expect(response.ok()).toBe(true); + expect(actionResults[0]!).toBe(expectedAction); + expect(response!.ok()).toBe(true); }); } @@ -113,12 +119,12 @@ describe('request interception', function () { expect(request.isNavigationRequest()).toBe(true); expect(request.resourceType()).toBe('document'); expect(request.frame() === page.mainFrame()).toBe(true); - expect(request.frame().url()).toBe('about:blank'); + expect(request.frame()!.url()).toBe('about:blank'); request.continue({}, 0); }); - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); - expect(response.remoteAddress().port).toBe(server.PORT); + const response = (await page.goto(server.EMPTY_PAGE))!; + expect(response!.ok()).toBe(true); + expect(response!.remoteAddress().port).toBe(server.PORT); }); // @see https://github.com/puppeteer/puppeteer/pull/3105 it('should work when POST is redirected with 302', async () => { @@ -127,14 +133,18 @@ describe('request interception', function () { server.setRedirect('/rredirect', '/empty.html'); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); await page.setContent(`
`); await Promise.all([ - page.$eval('form', (form: HTMLFormElement) => form.submit()), + page.$eval('form', (form) => { + return (form as HTMLFormElement).submit(); + }), page.waitForNavigation(), ]); }); @@ -179,7 +189,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { if (!utils.isFavicon(request)) { requests.push(request); @@ -187,8 +197,8 @@ describe('request interception', function () { request.continue({}, 0); }); await page.goto(server.PREFIX + '/one-style.html'); - expect(requests[1].url()).toContain('/one-style.css'); - expect(requests[1].headers().referer).toContain('/one-style.html'); + expect(requests[1]!.url()).toContain('/one-style.css'); + expect(requests[1]!.headers()['referer']).toContain('/one-style.html'); }); it('should properly return navigation response when URL has cookies', async () => { const { page, server } = getTestState(); @@ -199,15 +209,19 @@ describe('request interception', function () { // Setup request interception. await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const response = await page.reload(); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); }); it('should stop intercepting', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - page.once('request', (request) => request.continue({}, 0)); + page.once('request', (request) => { + return request.continue({}, 0); + }); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(false); await page.goto(server.EMPTY_PAGE); @@ -224,7 +238,7 @@ describe('request interception', function () { request.continue({}, 0); }); const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); + expect(response!.ok()).toBe(true); }); // @see https://github.com/puppeteer/puppeteer/issues/4337 it('should work with redirect inside sync XHR', async () => { @@ -233,7 +247,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); server.setRedirect('/logo.png', '/pptr.png'); await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const status = await page.evaluate(async () => { const request = new XMLHttpRequest(); request.open('GET', '/logo.png', false); // `false` makes the request synchronous @@ -252,7 +268,7 @@ describe('request interception', function () { request.continue({}, 0); }); const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); + expect(response!.ok()).toBe(true); }); it('should be abortable', async () => { const { page, server } = getTestState(); @@ -266,10 +282,12 @@ describe('request interception', function () { } }); let failedRequests = 0; - page.on('requestfailed', () => ++failedRequests); + page.on('requestfailed', () => { + return ++failedRequests; + }); const response = await page.goto(server.PREFIX + '/one-style.html'); - expect(response.ok()).toBe(true); - expect(response.request().failure()).toBe(null); + expect(response!.ok()).toBe(true); + expect(response!.request().failure()).toBe(null); expect(failedRequests).toBe(1); }); it('should be able to access the error reason', async () => { @@ -294,11 +312,13 @@ describe('request interception', function () { page.on('request', (request) => { request.abort('internetdisconnected', 0); }); - let failedRequest = null; - page.on('requestfailed', (request) => (failedRequest = request)); + let failedRequest!: HTTPRequest; + page.on('requestfailed', (request) => { + return (failedRequest = request); + }); await page.goto(server.EMPTY_PAGE).catch(() => {}); expect(failedRequest).toBeTruthy(); - expect(failedRequest.failure().errorText).toBe( + expect(failedRequest.failure()!.errorText).toBe( 'net::ERR_INTERNET_DISCONNECTED' ); }); @@ -309,7 +329,9 @@ describe('request interception', function () { referer: 'http://google.com/', }); await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const [request] = await Promise.all([ server.waitForRequest('/grid.html'), page.goto(server.PREFIX + '/grid.html'), @@ -320,9 +342,13 @@ describe('request interception', function () { const { page, server, isChrome } = getTestState(); await page.setRequestInterception(true); - page.on('request', (request) => request.abort('failed', 0)); - let error = null; - await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); + page.on('request', (request) => { + return request.abort('failed', 0); + }); + let error!: Error; + await page.goto(server.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); if (isChrome) { expect(error.message).toContain('net::ERR_FAILED'); @@ -334,7 +360,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue({}, 0); requests.push(request); @@ -355,17 +381,17 @@ describe('request interception', function () { const response = await page.goto( server.PREFIX + '/non-existing-page.html' ); - expect(response.status()).toBe(200); - expect(response.url()).toContain('empty.html'); + expect(response!.status()).toBe(200); + expect(response!.url()).toContain('empty.html'); expect(requests.length).toBe(5); - expect(requests[2].resourceType()).toBe('document'); + expect(requests[2]!.resourceType()).toBe('document'); // Check redirect chain - const redirectChain = response.request().redirectChain(); + const redirectChain = response!.request().redirectChain(); expect(redirectChain.length).toBe(4); - expect(redirectChain[0].url()).toContain('/non-existing-page.html'); - expect(redirectChain[2].url()).toContain('/non-existing-page-3.html'); + expect(redirectChain[0]!.url()).toContain('/non-existing-page.html'); + expect(redirectChain[2]!.url()).toContain('/non-existing-page-3.html'); for (let i = 0; i < redirectChain.length; ++i) { - const request = redirectChain[i]; + const request = redirectChain[i]!; expect(request.isNavigationRequest()).toBe(true); expect(request.redirectChain().indexOf(request)).toBe(i); } @@ -374,7 +400,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue({}, 0); if (!utils.isFavicon(request)) { @@ -384,21 +410,21 @@ describe('request interception', function () { server.setRedirect('/one-style.css', '/two-style.css'); server.setRedirect('/two-style.css', '/three-style.css'); server.setRedirect('/three-style.css', '/four-style.css'); - server.setRoute('/four-style.css', (req, res) => - res.end('body {box-sizing: border-box; }') - ); + server.setRoute('/four-style.css', (_req, res) => { + return res.end('body {box-sizing: border-box; }'); + }); const response = await page.goto(server.PREFIX + '/one-style.html'); - expect(response.status()).toBe(200); - expect(response.url()).toContain('one-style.html'); + expect(response!.status()).toBe(200); + expect(response!.url()).toContain('one-style.html'); expect(requests.length).toBe(5); - expect(requests[0].resourceType()).toBe('document'); - expect(requests[1].resourceType()).toBe('stylesheet'); + expect(requests[0]!.resourceType()).toBe('document'); + expect(requests[1]!.resourceType()).toBe('stylesheet'); // Check redirect chain - const redirectChain = requests[1].redirectChain(); + const redirectChain = requests[1]!.redirectChain(); expect(redirectChain.length).toBe(3); - expect(redirectChain[0].url()).toContain('/one-style.css'); - expect(redirectChain[2].url()).toContain('/three-style.css'); + expect(redirectChain[0]!.url()).toContain('/one-style.css'); + expect(redirectChain[2]!.url()).toContain('/three-style.css'); }); it('should be able to abort redirects', async () => { const { page, server, isChrome } = getTestState(); @@ -416,9 +442,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async () => { try { - await fetch('/non-existing.json'); + return await fetch('/non-existing.json'); } catch (error) { - return error.message; + return (error as Error).message; } }); if (isChrome) { @@ -432,7 +458,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); let responseCount = 1; - server.setRoute('/zzz', (req, res) => res.end(responseCount++ * 11 + '')); + server.setRoute('/zzz', (_req, res) => { + return res.end(responseCount++ * 11 + ''); + }); await page.setRequestInterception(true); let spinner = false; @@ -445,69 +473,82 @@ describe('request interception', function () { spinner ? request.abort('failed', 0) : request.continue({}, 0); spinner = !spinner; }); - const results = await page.evaluate(() => - Promise.all([ + const results = await page.evaluate(() => { + return Promise.all([ fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), + .then((response) => { + return response!.text(); + }) + .catch(() => { + return 'FAILED'; + }), fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), + .then((response) => { + return response!.text(); + }) + .catch(() => { + return 'FAILED'; + }), fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), - ]) - ); + .then((response) => { + return response!.text(); + }) + .catch(() => { + return 'FAILED'; + }), + ]); + }); expect(results).toEqual(['11', 'FAILED', '22']); }); it('should navigate to dataURL and fire dataURL requests', async () => { const { page } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { requests.push(request); request.continue({}, 0); }); const dataURL = 'data:text/html,
yo
'; const response = await page.goto(dataURL); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); + expect(requests[0]!.url()).toBe(dataURL); }); it('should be able to fetch dataURL and fire dataURL requests', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { !utils.isFavicon(request) && requests.push(request); request.continue({}, 0); }); const dataURL = 'data:text/html,
yo
'; - const text = await page.evaluate( - (url: string) => fetch(url).then((r) => r.text()), - dataURL - ); + const text = await page.evaluate((url: string) => { + return fetch(url).then((r) => { + return r.text(); + }); + }, dataURL); expect(text).toBe('
yo
'); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); + expect(requests[0]!.url()).toBe(dataURL); }); it('should navigate to URL with hash and fire requests without hash', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { requests.push(request); request.continue({}, 0); }); const response = await page.goto(server.EMPTY_PAGE + '#hash'); - expect(response.status()).toBe(200); - expect(response.url()).toBe(server.EMPTY_PAGE); + expect(response!.status()).toBe(200); + expect(response!.url()).toBe(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); }); it('should work with encoded server', async () => { const { page, server } = getTestState(); @@ -515,20 +556,26 @@ describe('request interception', function () { // The requestWillBeSent will report encoded URL, whereas interception will // report URL as-is. @see crbug.com/759388 await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const response = await page.goto( server.PREFIX + '/some nonexisting page' ); - expect(response.status()).toBe(404); + expect(response!.status()).toBe(404); }); it('should work with badly encoded server', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); - page.on('request', (request) => request.continue({}, 0)); + server.setRoute('/malformed?rnd=%911', (_req, res) => { + return res.end(); + }); + page.on('request', (request) => { + return request.continue({}, 0); + }); const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); }); it('should work with encoded server - 2', async () => { const { page, server } = getTestState(); @@ -536,7 +583,7 @@ describe('request interception', function () { // The requestWillBeSent will report URL as-is, whereas interception will // report encoded URL for stylesheet. @see crbug.com/759388 await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue({}, 0); requests.push(request); @@ -544,39 +591,47 @@ describe('request interception', function () { const response = await page.goto( `data:text/html,` ); - expect(response.status()).toBe(200); + expect(response!.status()).toBe(200); expect(requests.length).toBe(2); - expect(requests[1].response().status()).toBe(404); + expect(requests[1]!.response()!.status()).toBe(404); }); it('should not throw "Invalid Interception Id" if the request was cancelled', async () => { const { page, server } = getTestState(); await page.setContent(''); await page.setRequestInterception(true); - let request = null; - page.on('request', async (r) => (request = r)); + let request!: HTTPRequest; + page.on('request', async (r) => { + return (request = r); + }); page.$eval( 'iframe', - (frame: HTMLIFrameElement, url: string) => (frame.src = url), + (frame, url) => { + return ((frame as HTMLIFrameElement).src = url as string); + }, server.EMPTY_PAGE ), // Wait for request interception. await utils.waitEvent(page, 'request'); // Delete frame to cause request to be canceled. - await page.$eval('iframe', (frame) => frame.remove()); - let error = null; - await request.continue({}, 0).catch((error_) => (error = error_)); - expect(error).toBe(null); + await page.$eval('iframe', (frame) => { + return frame.remove(); + }); + let error!: Error; + await request.continue({}, 0).catch((error_) => { + return (error = error_); + }); + expect(error).toBeUndefined(); }); it('should throw if interception is not enabled', async () => { const { page, server } = getTestState(); - let error = null; + let error!: Error; page.on('request', async (request) => { try { await request.continue({}, 0); } catch (error_) { - error = error_; + error = error_ as Error; } }); await page.goto(server.EMPTY_PAGE); @@ -606,10 +661,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(false); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const cached = []; - page.on('requestservedfromcache', (r) => cached.push(r)); + page.on('requestservedfromcache', (r) => { + return cached.push(r); + }); await page.reload(); expect(cached.length).toBe(0); @@ -622,10 +681,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); const cached = []; - page.on('requestservedfromcache', (r) => cached.push(r)); + page.on('requestservedfromcache', (r) => { + return cached.push(r); + }); await page.reload(); expect(cached.length).toBe(1); @@ -635,10 +698,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); await page.goto(server.PREFIX + '/cached/one-style-font.html'); - await page.waitForResponse((r) => r.url().endsWith('/one-style.woff')); + await page.waitForResponse((r) => { + return r.url().endsWith('/one-style.woff'); + }); }); }); @@ -647,7 +714,9 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - page.on('request', (request) => request.continue({}, 0)); + page.on('request', (request) => { + return request.continue({}, 0); + }); await page.goto(server.EMPTY_PAGE); }); it('should amend HTTP headers', async () => { @@ -662,7 +731,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')), + page.evaluate(() => { + return fetch('/sleep.zzz'); + }), ]); expect(request.headers['foo']).toBe('bar'); }); @@ -676,8 +747,10 @@ describe('request interception', function () { : undefined; request.continue({ url: redirectURL }, 0); }); - let consoleMessage = null; - page.on('console', (msg) => (consoleMessage = msg)); + let consoleMessage!: ConsoleMessage; + page.on('console', (msg) => { + return (consoleMessage = msg); + }); await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); expect(consoleMessage.text()).toBe('yellow'); @@ -693,7 +766,9 @@ describe('request interception', function () { }); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')), + page.evaluate(() => { + return fetch('/sleep.zzz'); + }), ]); expect(request.method).toBe('POST'); }); @@ -708,9 +783,9 @@ describe('request interception', function () { }); const [serverRequest] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => - fetch('/sleep.zzz', { method: 'POST', body: 'birdy' }) - ), + page.evaluate(() => { + return fetch('/sleep.zzz', { method: 'POST', body: 'birdy' }); + }), ]); expect(await serverRequest.postBody).toBe('doggo'); }); @@ -748,11 +823,13 @@ describe('request interception', function () { ); }); const response = await page.goto(server.EMPTY_PAGE); - expect(response.status()).toBe(201); - expect(response.headers().foo).toBe('bar'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect(response!.status()).toBe(201); + expect(response!.headers()['foo']).toBe('bar'); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should be able to access the response', async () => { const { page, server } = getTestState(); @@ -789,11 +866,13 @@ describe('request interception', function () { ); }); const response = await page.goto(server.EMPTY_PAGE); - expect(response.status()).toBe(422); - expect(response.statusText()).toBe('Unprocessable Entity'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect(response!.status()).toBe(422); + expect(response!.statusText()).toBe('Unprocessable Entity'); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should redirect', async () => { const { page, server } = getTestState(); @@ -815,11 +894,11 @@ describe('request interception', function () { ); }); const response = await page.goto(server.PREFIX + '/rrredirect'); - expect(response.request().redirectChain().length).toBe(1); - expect(response.request().redirectChain()[0].url()).toBe( + expect(response!.request().redirectChain().length).toBe(1); + expect(response!.request().redirectChain()[0]!.url()).toBe( server.PREFIX + '/rrredirect' ); - expect(response.url()).toBe(server.EMPTY_PAGE); + expect(response!.url()).toBe(server.EMPTY_PAGE); }); it('should allow mocking binary responses', async () => { const { page, server } = getTestState(); @@ -841,9 +920,11 @@ describe('request interception', function () { const img = document.createElement('img'); img.src = PREFIX + '/does-not-exist.png'; document.body.appendChild(img); - return new Promise((fulfill) => (img.onload = fulfill)); + return new Promise((fulfill) => { + return (img.onload = fulfill); + }); }, server.PREFIX); - const img = await page.$('img'); + const img = (await page.$('img'))!; expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); }); it('should stringify intercepted request response headers', async () => { @@ -863,12 +944,14 @@ describe('request interception', function () { ); }); const response = await page.goto(server.EMPTY_PAGE); - expect(response.status()).toBe(200); - const headers = response.headers(); - expect(headers.foo).toBe('true'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect(response!.status()).toBe(200); + const headers = response!.headers(); + expect(headers['foo']).toBe('true'); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should indicate already-handled if an intercept has been handled', async () => { const { page, server } = getTestState(); diff --git a/test/src/requestinterception.spec.ts b/test/src/requestinterception.spec.ts index f2bd9bf5cb6..c07fe938665 100644 --- a/test/src/requestinterception.spec.ts +++ b/test/src/requestinterception.spec.ts @@ -23,7 +23,9 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; +import { HTTPRequest } from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; +import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; describe('request interception', function () { setupTestBrowserHooks(); @@ -46,10 +48,10 @@ describe('request interception', function () { expect(request.isNavigationRequest()).toBe(true); expect(request.resourceType()).toBe('document'); expect(request.frame() === page.mainFrame()).toBe(true); - expect(request.frame().url()).toBe('about:blank'); + expect(request.frame()!.url()).toBe('about:blank'); request.continue(); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.ok()).toBe(true); expect(response.remoteAddress().port).toBe(server.PORT); }); @@ -60,14 +62,18 @@ describe('request interception', function () { server.setRedirect('/rredirect', '/empty.html'); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); await page.setContent(`
`); await Promise.all([ - page.$eval('form', (form: HTMLFormElement) => form.submit()), + page.$eval('form', (form) => { + return (form as HTMLFormElement).submit(); + }), page.waitForNavigation(), ]); }); @@ -109,7 +115,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { if (!utils.isFavicon(request)) { requests.push(request); @@ -117,8 +123,8 @@ describe('request interception', function () { request.continue(); }); await page.goto(server.PREFIX + '/one-style.html'); - expect(requests[1].url()).toContain('/one-style.css'); - expect(requests[1].headers().referer).toContain('/one-style.html'); + expect(requests[1]!.url()).toContain('/one-style.css'); + expect(requests[1]!.headers()['referer']).toContain('/one-style.html'); }); it('should properly return navigation response when URL has cookies', async () => { const { page, server } = getTestState(); @@ -129,15 +135,19 @@ describe('request interception', function () { // Setup request interception. await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); - const response = await page.reload(); + page.on('request', (request) => { + return request.continue(); + }); + const response = (await page.reload())!; expect(response.status()).toBe(200); }); it('should stop intercepting', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - page.once('request', (request) => request.continue()); + page.once('request', (request) => { + return request.continue(); + }); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(false); await page.goto(server.EMPTY_PAGE); @@ -153,7 +163,7 @@ describe('request interception', function () { expect(request.headers()['foo']).toBe('bar'); request.continue(); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.ok()).toBe(true); }); // @see https://github.com/puppeteer/puppeteer/issues/4337 @@ -163,7 +173,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); server.setRedirect('/logo.png', '/pptr.png'); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); const status = await page.evaluate(async () => { const request = new XMLHttpRequest(); request.open('GET', '/logo.png', false); // `false` makes the request synchronous @@ -181,7 +193,7 @@ describe('request interception', function () { expect(request.headers()['referer']).toBe(server.EMPTY_PAGE); request.continue(); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.ok()).toBe(true); }); it('should be abortable', async () => { @@ -196,8 +208,10 @@ describe('request interception', function () { } }); let failedRequests = 0; - page.on('requestfailed', () => ++failedRequests); - const response = await page.goto(server.PREFIX + '/one-style.html'); + page.on('requestfailed', () => { + return ++failedRequests; + }); + const response = (await page.goto(server.PREFIX + '/one-style.html'))!; expect(response.ok()).toBe(true); expect(response.request().failure()).toBe(null); expect(failedRequests).toBe(1); @@ -209,11 +223,13 @@ describe('request interception', function () { page.on('request', (request) => { request.abort('internetdisconnected'); }); - let failedRequest = null; - page.on('requestfailed', (request) => (failedRequest = request)); + let failedRequest!: HTTPRequest; + page.on('requestfailed', (request) => { + return (failedRequest = request); + }); await page.goto(server.EMPTY_PAGE).catch(() => {}); expect(failedRequest).toBeTruthy(); - expect(failedRequest.failure().errorText).toBe( + expect(failedRequest.failure()!.errorText).toBe( 'net::ERR_INTERNET_DISCONNECTED' ); }); @@ -224,7 +240,9 @@ describe('request interception', function () { referer: 'http://google.com/', }); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); const [request] = await Promise.all([ server.waitForRequest('/grid.html'), page.goto(server.PREFIX + '/grid.html'), @@ -235,9 +253,13 @@ describe('request interception', function () { const { page, server, isChrome } = getTestState(); await page.setRequestInterception(true); - page.on('request', (request) => request.abort()); - let error = null; - await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); + page.on('request', (request) => { + return request.abort(); + }); + let error!: Error; + await page.goto(server.EMPTY_PAGE).catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); if (isChrome) { expect(error.message).toContain('net::ERR_FAILED'); @@ -249,7 +271,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue(); requests.push(request); @@ -267,20 +289,20 @@ describe('request interception', function () { '/non-existing-page-4.html' ); server.setRedirect('/non-existing-page-4.html', '/empty.html'); - const response = await page.goto( + const response = (await page.goto( server.PREFIX + '/non-existing-page.html' - ); + ))!; expect(response.status()).toBe(200); expect(response.url()).toContain('empty.html'); expect(requests.length).toBe(5); - expect(requests[2].resourceType()).toBe('document'); + expect(requests[2]!.resourceType()).toBe('document'); // Check redirect chain const redirectChain = response.request().redirectChain(); expect(redirectChain.length).toBe(4); - expect(redirectChain[0].url()).toContain('/non-existing-page.html'); - expect(redirectChain[2].url()).toContain('/non-existing-page-3.html'); + expect(redirectChain[0]!.url()).toContain('/non-existing-page.html'); + expect(redirectChain[2]!.url()).toContain('/non-existing-page-3.html'); for (let i = 0; i < redirectChain.length; ++i) { - const request = redirectChain[i]; + const request = redirectChain[i]!; expect(request.isNavigationRequest()).toBe(true); expect(request.redirectChain().indexOf(request)).toBe(i); } @@ -289,7 +311,7 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue(); if (!utils.isFavicon(request)) { @@ -299,21 +321,21 @@ describe('request interception', function () { server.setRedirect('/one-style.css', '/two-style.css'); server.setRedirect('/two-style.css', '/three-style.css'); server.setRedirect('/three-style.css', '/four-style.css'); - server.setRoute('/four-style.css', (req, res) => - res.end('body {box-sizing: border-box; }') - ); + server.setRoute('/four-style.css', (_req, res) => { + return res.end('body {box-sizing: border-box; }'); + }); - const response = await page.goto(server.PREFIX + '/one-style.html'); + const response = (await page.goto(server.PREFIX + '/one-style.html'))!; expect(response.status()).toBe(200); expect(response.url()).toContain('one-style.html'); expect(requests.length).toBe(5); - expect(requests[0].resourceType()).toBe('document'); - expect(requests[1].resourceType()).toBe('stylesheet'); + expect(requests[0]!.resourceType()).toBe('document'); + expect(requests[1]!.resourceType()).toBe('stylesheet'); // Check redirect chain - const redirectChain = requests[1].redirectChain(); + const redirectChain = requests[1]!.redirectChain(); expect(redirectChain.length).toBe(3); - expect(redirectChain[0].url()).toContain('/one-style.css'); - expect(redirectChain[2].url()).toContain('/three-style.css'); + expect(redirectChain[0]!.url()).toContain('/one-style.css'); + expect(redirectChain[2]!.url()).toContain('/three-style.css'); }); it('should be able to abort redirects', async () => { const { page, server, isChrome } = getTestState(); @@ -331,9 +353,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async () => { try { - await fetch('/non-existing.json'); + return await fetch('/non-existing.json'); } catch (error) { - return error.message; + return (error as Error).message; } }); if (isChrome) { @@ -347,7 +369,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); let responseCount = 1; - server.setRoute('/zzz', (req, res) => res.end(responseCount++ * 11 + '')); + server.setRoute('/zzz', (_req, res) => { + return res.end(responseCount++ * 11 + ''); + }); await page.setRequestInterception(true); let spinner = false; @@ -360,69 +384,82 @@ describe('request interception', function () { spinner ? request.abort() : request.continue(); spinner = !spinner; }); - const results = await page.evaluate(() => - Promise.all([ + const results = await page.evaluate(() => { + return Promise.all([ fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), + .then((response) => { + return response.text(); + }) + .catch(() => { + return 'FAILED'; + }), fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), + .then((response) => { + return response.text(); + }) + .catch(() => { + return 'FAILED'; + }), fetch('/zzz') - .then((response) => response.text()) - .catch(() => 'FAILED'), - ]) - ); + .then((response) => { + return response.text(); + }) + .catch(() => { + return 'FAILED'; + }), + ]); + }); expect(results).toEqual(['11', 'FAILED', '22']); }); it('should navigate to dataURL and fire dataURL requests', async () => { const { page } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { requests.push(request); request.continue(); }); const dataURL = 'data:text/html,
yo
'; - const response = await page.goto(dataURL); + const response = (await page.goto(dataURL))!; expect(response.status()).toBe(200); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); + expect(requests[0]!.url()).toBe(dataURL); }); it('should be able to fetch dataURL and fire dataURL requests', async () => { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { !utils.isFavicon(request) && requests.push(request); request.continue(); }); const dataURL = 'data:text/html,
yo
'; - const text = await page.evaluate( - (url: string) => fetch(url).then((r) => r.text()), - dataURL - ); + const text = await page.evaluate((url: string) => { + return fetch(url).then((r) => { + return r.text(); + }); + }, dataURL); expect(text).toBe('
yo
'); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); + expect(requests[0]!.url()).toBe(dataURL); }); it('should navigate to URL with hash and fire requests without hash', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { requests.push(request); request.continue(); }); - const response = await page.goto(server.EMPTY_PAGE + '#hash'); + const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!; expect(response.status()).toBe(200); expect(response.url()).toBe(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); + expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); }); it('should work with encoded server', async () => { const { page, server } = getTestState(); @@ -430,19 +467,27 @@ describe('request interception', function () { // The requestWillBeSent will report encoded URL, whereas interception will // report URL as-is. @see crbug.com/759388 await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); - const response = await page.goto( + page.on('request', (request) => { + return request.continue(); + }); + const response = (await page.goto( server.PREFIX + '/some nonexisting page' - ); + ))!; expect(response.status()).toBe(404); }); it('should work with badly encoded server', async () => { const { page, server } = getTestState(); await page.setRequestInterception(true); - server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); - page.on('request', (request) => request.continue()); - const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); + server.setRoute('/malformed?rnd=%911', (_req, res) => { + return res.end(); + }); + page.on('request', (request) => { + return request.continue(); + }); + const response = (await page.goto( + server.PREFIX + '/malformed?rnd=%911' + ))!; expect(response.status()).toBe(200); }); it('should work with encoded server - 2', async () => { @@ -451,47 +496,55 @@ describe('request interception', function () { // The requestWillBeSent will report URL as-is, whereas interception will // report encoded URL for stylesheet. @see crbug.com/759388 await page.setRequestInterception(true); - const requests = []; + const requests: HTTPRequest[] = []; page.on('request', (request) => { request.continue(); requests.push(request); }); - const response = await page.goto( + const response = (await page.goto( `data:text/html,` - ); + ))!; expect(response.status()).toBe(200); expect(requests.length).toBe(2); - expect(requests[1].response().status()).toBe(404); + expect(requests[1]!.response()!.status()).toBe(404); }); it('should not throw "Invalid Interception Id" if the request was cancelled', async () => { const { page, server } = getTestState(); await page.setContent(''); await page.setRequestInterception(true); - let request = null; - page.on('request', async (r) => (request = r)); + let request!: HTTPRequest; + page.on('request', async (r) => { + return (request = r); + }); page.$eval( 'iframe', - (frame: HTMLIFrameElement, url: string) => (frame.src = url), + (frame, url) => { + return ((frame as HTMLIFrameElement).src = url as string); + }, server.EMPTY_PAGE ), // Wait for request interception. await utils.waitEvent(page, 'request'); // Delete frame to cause request to be canceled. - await page.$eval('iframe', (frame) => frame.remove()); - let error = null; - await request.continue().catch((error_) => (error = error_)); - expect(error).toBe(null); + await page.$eval('iframe', (frame) => { + return frame.remove(); + }); + let error!: Error; + await request.continue().catch((error_) => { + return (error = error_); + }); + expect(error).toBeUndefined(); }); it('should throw if interception is not enabled', async () => { const { page, server } = getTestState(); - let error = null; + let error!: Error; page.on('request', async (request) => { try { await request.continue(); } catch (error_) { - error = error_; + error = error_ as Error; } }); await page.goto(server.EMPTY_PAGE); @@ -521,10 +574,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(false); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); const cached = []; - page.on('requestservedfromcache', (r) => cached.push(r)); + page.on('requestservedfromcache', (r) => { + return cached.push(r); + }); await page.reload(); expect(cached.length).toBe(0); @@ -537,10 +594,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); const cached = []; - page.on('requestservedfromcache', (r) => cached.push(r)); + page.on('requestservedfromcache', (r) => { + return cached.push(r); + }); await page.reload(); expect(cached.length).toBe(1); @@ -550,10 +611,14 @@ describe('request interception', function () { await page.setRequestInterception(true); await page.setCacheEnabled(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); await page.goto(server.PREFIX + '/cached/one-style-font.html'); - await page.waitForResponse((r) => r.url().endsWith('/one-style.woff')); + await page.waitForResponse((r) => { + return r.url().endsWith('/one-style.woff'); + }); }); }); @@ -562,7 +627,9 @@ describe('request interception', function () { const { page, server } = getTestState(); await page.setRequestInterception(true); - page.on('request', (request) => request.continue()); + page.on('request', (request) => { + return request.continue(); + }); await page.goto(server.EMPTY_PAGE); }); it('should amend HTTP headers', async () => { @@ -577,7 +644,9 @@ describe('request interception', function () { await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')), + page.evaluate(() => { + return fetch('/sleep.zzz'); + }), ]); expect(request.headers['foo']).toBe('bar'); }); @@ -591,8 +660,10 @@ describe('request interception', function () { : undefined; request.continue({ url: redirectURL }); }); - let consoleMessage = null; - page.on('console', (msg) => (consoleMessage = msg)); + let consoleMessage!: ConsoleMessage; + page.on('console', (msg) => { + return (consoleMessage = msg); + }); await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); expect(consoleMessage.text()).toBe('yellow'); @@ -608,7 +679,9 @@ describe('request interception', function () { }); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')), + page.evaluate(() => { + return fetch('/sleep.zzz'); + }), ]); expect(request.method).toBe('POST'); }); @@ -623,9 +696,9 @@ describe('request interception', function () { }); const [serverRequest] = await Promise.all([ server.waitForRequest('/sleep.zzz'), - page.evaluate(() => - fetch('/sleep.zzz', { method: 'POST', body: 'birdy' }) - ), + page.evaluate(() => { + return fetch('/sleep.zzz', { method: 'POST', body: 'birdy' }); + }), ]); expect(await serverRequest.postBody).toBe('doggo'); }); @@ -646,7 +719,7 @@ describe('request interception', function () { it('should fail if the header value is invalid', async () => { const { page, server } = getTestState(); - let error; + let error!: Error; await page.setRequestInterception(true); page.on('request', async (request) => { await request @@ -656,7 +729,7 @@ describe('request interception', function () { }, }) .catch((error_) => { - error = error_; + error = error_ as Error; }); await request.continue(); }); @@ -679,12 +752,14 @@ describe('request interception', function () { body: 'Yo, page!', }); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(201); - expect(response.headers().foo).toBe('bar'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect(response.headers()['foo']).toBe('bar'); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should work with status code 422', async () => { const { page, server } = getTestState(); @@ -696,12 +771,14 @@ describe('request interception', function () { body: 'Yo, page!', }); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(422); expect(response.statusText()).toBe('Unprocessable Entity'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should redirect', async () => { const { page, server } = getTestState(); @@ -719,9 +796,9 @@ describe('request interception', function () { }, }); }); - const response = await page.goto(server.PREFIX + '/rrredirect'); + const response = (await page.goto(server.PREFIX + '/rrredirect'))!; expect(response.request().redirectChain().length).toBe(1); - expect(response.request().redirectChain()[0].url()).toBe( + expect(response.request().redirectChain()[0]!.url()).toBe( server.PREFIX + '/rrredirect' ); expect(response.url()).toBe(server.EMPTY_PAGE); @@ -741,13 +818,17 @@ describe('request interception', function () { body: 'Hello world', }); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; const cookies = await page.cookies(); - const firstCookie = cookies.find((cookie) => cookie.name === 'first'); - const secondCookie = cookies.find((cookie) => cookie.name === 'second'); + const firstCookie = cookies.find((cookie) => { + return cookie.name === 'first'; + }); + const secondCookie = cookies.find((cookie) => { + return cookie.name === 'second'; + }); expect(response.status()).toBe(200); - expect(response.headers().foo).toBe('bar'); - expect(response.headers().arr).toBe('1\n2'); + expect(response.headers()['foo']).toBe('bar'); + expect(response.headers()['arr']).toBe('1\n2'); // request.respond() will not trigger Network.responseReceivedExtraInfo // fail to get 'set-cookie' header from response expect(firstCookie?.value).toBe('1'); @@ -770,9 +851,11 @@ describe('request interception', function () { const img = document.createElement('img'); img.src = PREFIX + '/does-not-exist.png'; document.body.appendChild(img); - return new Promise((fulfill) => (img.onload = fulfill)); + return new Promise((fulfill) => { + return (img.onload = fulfill); + }); }, server.PREFIX); - const img = await page.$('img'); + const img = (await page.$('img'))!; expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); }); it('should stringify intercepted request response headers', async () => { @@ -788,18 +871,20 @@ describe('request interception', function () { body: 'Yo, page!', }); }); - const response = await page.goto(server.EMPTY_PAGE); + const response = (await page.goto(server.EMPTY_PAGE))!; expect(response.status()).toBe(200); const headers = response.headers(); - expect(headers.foo).toBe('true'); - expect(await page.evaluate(() => document.body.textContent)).toBe( - 'Yo, page!' - ); + expect(headers['foo']).toBe('true'); + expect( + await page.evaluate(() => { + return document.body.textContent; + }) + ).toBe('Yo, page!'); }); it('should fail if the header value is invalid', async () => { const { page, server } = getTestState(); - let error; + let error!: Error; await page.setRequestInterception(true); page.on('request', async (request) => { await request @@ -809,7 +894,7 @@ describe('request interception', function () { }, }) .catch((error_) => { - error = error_; + error = error_ as Error; }); await request.respond({ status: 200, diff --git a/test/src/screenshot.spec.ts b/test/src/screenshot.spec.ts index 42ffad744a0..0507bbb4b3e 100644 --- a/test/src/screenshot.spec.ts +++ b/test/src/screenshot.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describe('Screenshots', function () { setupTestBrowserHooks(); @@ -86,7 +86,7 @@ describe('Screenshots', function () { ); } const screenshots = await Promise.all(promises); - expect(screenshots[1]).toBeGolden('grid-cell-1.png'); + expect(screenshots[1]!).toBeGolden('grid-cell-1.png'); }); itFailsFirefox('should take fullPage screenshots', async () => { const { page, server } = getTestState(); @@ -114,7 +114,7 @@ describe('Screenshots', function () { const promises = []; for (let i = 0; i < N; ++i) { promises.push( - pages[i].screenshot({ + pages[i]!.screenshot({ clip: { x: 50 * i, y: 0, width: 50, height: 50 }, }) ); @@ -123,7 +123,11 @@ describe('Screenshots', function () { for (let i = 0; i < N; ++i) { expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); } - await Promise.all(pages.map((page) => page.close())); + await Promise.all( + pages.map((page) => { + return page.close(); + }) + ); }); itFailsFirefox('should allow transparency', async () => { const { page, server } = getTestState(); @@ -192,8 +196,10 @@ describe('Screenshots', function () { await page.setViewport({ width: 500, height: 500 }); await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); + await page.evaluate(() => { + return window.scrollBy(50, 100); + }); + const elementHandle = (await page.$('.box:nth-of-type(3)'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); }); @@ -212,7 +218,7 @@ describe('Screenshots', function () {
`); - const elementHandle = await page.$('div'); + const elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); }); @@ -238,17 +244,19 @@ describe('Screenshots', function () {
`); - const elementHandle = await page.$('div.to-screenshot'); + const elementHandle = (await page.$('div.to-screenshot'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden( 'screenshot-element-larger-than-viewport.png' ); expect( - await page.evaluate(() => ({ - w: window.innerWidth, - h: window.innerHeight, - })) + await page.evaluate(() => { + return { + w: window.innerWidth, + h: window.innerHeight, + }; + }) ).toEqual({ w: 500, h: 500 }); } ); @@ -273,7 +281,7 @@ describe('Screenshots', function () {
`); - const elementHandle = await page.$('div.to-screenshot'); + const elementHandle = (await page.$('div.to-screenshot'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden( 'screenshot-element-scrolled-into-view.png' @@ -290,7 +298,7 @@ describe('Screenshots', function () { height: 100px; background: green; transform: rotateZ(200deg);"> 
`); - const elementHandle = await page.$('div'); + const elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-rotate.png'); }); @@ -298,14 +306,15 @@ describe('Screenshots', function () { const { page } = getTestState(); await page.setContent('

remove this

'); - const elementHandle = await page.$('h1'); - await page.evaluate( - (element: HTMLElement) => element.remove(), - elementHandle - ); + const elementHandle = (await page.$('h1'))!; + await page.evaluate((element: HTMLElement) => { + return element.remove(); + }, elementHandle); const screenshotError = await elementHandle .screenshot() - .catch((error) => error); + .catch((error) => { + return error; + }); expect(screenshotError.message).toBe( 'Node is either not visible or not an HTMLElement' ); @@ -314,8 +323,10 @@ describe('Screenshots', function () { const { page } = getTestState(); await page.setContent('
'); - const div = await page.$('div'); - const error = await div.screenshot().catch((error_) => error_); + const div = (await page.$('div'))!; + const error = await div.screenshot().catch((error_) => { + return error_; + }); expect(error.message).toBe('Node has 0 height.'); }); it('should work for an element with fractional dimensions', async () => { @@ -324,7 +335,7 @@ describe('Screenshots', function () { await page.setContent( '
' ); - const elementHandle = await page.$('div'); + const elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-fractional.png'); }); @@ -334,7 +345,7 @@ describe('Screenshots', function () { await page.setContent( '
' ); - const elementHandle = await page.$('div'); + const elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png'); }); diff --git a/test/src/target.spec.ts b/test/src/target.spec.ts index b1656a642d3..04249aae43b 100644 --- a/test/src/target.spec.ts +++ b/test/src/target.spec.ts @@ -14,16 +14,18 @@ * limitations under the License. */ -import utils from './utils.js'; -const { waitEvent } = utils; import expect from 'expect'; +import { ServerResponse } from 'http'; +import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; +import { Target } from '../../lib/cjs/puppeteer/common/Target.js'; import { getTestState, + itFailsFirefox, setupTestBrowserHooks, setupTestPageAndContextHooks, - itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions -import { Target } from '../../lib/cjs/puppeteer/common/Target.js'; +} from './mocha-utils.js'; +import utils from './utils.js'; +const { waitEvent } = utils; describe('Target', function () { setupTestBrowserHooks(); @@ -35,11 +37,15 @@ describe('Target', function () { // The pages will be the testing page and the original newtab page const targets = browser.targets(); expect( - targets.some( - (target) => target.type() === 'page' && target.url() === 'about:blank' - ) + targets.some((target) => { + return target.type() === 'page' && target.url() === 'about:blank'; + }) + ).toBeTruthy(); + expect( + targets.some((target) => { + return target.type() === 'browser'; + }) ).toBeTruthy(); - expect(targets.some((target) => target.type() === 'browser')).toBeTruthy(); }); it('Browser.pages should return all of the pages', async () => { const { page, context } = getTestState(); @@ -53,7 +59,9 @@ describe('Target', function () { const { browser } = getTestState(); const targets = browser.targets(); - const browserTarget = targets.find((target) => target.type() === 'browser'); + const browserTarget = targets.find((target) => { + return target.type() === 'browser'; + }); expect(browserTarget).toBeTruthy(); }); it('should be able to use the default page in the browser', async () => { @@ -61,9 +69,13 @@ describe('Target', function () { // The pages will be the testing page and the original newtab page const allPages = await browser.pages(); - const originalPage = allPages.find((p) => p !== page); + const originalPage = allPages.find((p) => { + return p !== page; + })!; expect( - await originalPage.evaluate(() => ['Hello', 'world'].join(' ')) + await originalPage.evaluate(() => { + return ['Hello', 'world'].join(' '); + }) ).toBe('Hello world'); expect(await originalPage.$('body')).toBeTruthy(); }); @@ -72,21 +84,19 @@ describe('Target', function () { const [otherPage] = await Promise.all([ context - .waitForTarget((target) => - target - .page() - .then( - (page) => - page.url() === server.CROSS_PROCESS_PREFIX + '/empty.html' - ) - ) - .then((target) => target.page()), - page.evaluate( - (url: string) => window.open(url), - server.CROSS_PROCESS_PREFIX + '/empty.html' - ), + .waitForTarget((target) => { + return target.page().then((page) => { + return page!.url() === server.CROSS_PROCESS_PREFIX + '/empty.html'; + }); + }) + .then((target) => { + return target.page(); + }), + page.evaluate((url: string) => { + return window.open(url); + }, server.CROSS_PROCESS_PREFIX + '/empty.html'), ]); - expect(otherPage.url()).toEqual( + expect(otherPage!.url()).toEqual( server.CROSS_PROCESS_PREFIX + '/empty.html' ); expect(page).not.toEqual(otherPage); @@ -98,35 +108,41 @@ describe('Target', function () { const [otherPage] = await Promise.all([ context - .waitForTarget( - (target) => - target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html' - ) - .then((target) => target.page()), - page.evaluate( - (url: string) => window.open(url), - server.CROSS_PROCESS_PREFIX + '/empty.html' - ), + .waitForTarget((target) => { + return target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html'; + }) + .then((target) => { + return target.page(); + }), + page.evaluate((url: string) => { + return window.open(url); + }, server.CROSS_PROCESS_PREFIX + '/empty.html'), ]); - expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX); - expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe( - 'Hello world' - ); - expect(await otherPage.$('body')).toBeTruthy(); + expect(otherPage!.url()).toContain(server.CROSS_PROCESS_PREFIX); + expect( + await otherPage!.evaluate(() => { + return ['Hello', 'world'].join(' '); + }) + ).toBe('Hello world'); + expect(await otherPage!.$('body')).toBeTruthy(); let allPages = await context.pages(); expect(allPages).toContain(page); expect(allPages).toContain(otherPage); - const closePagePromise = new Promise((fulfill) => - context.once('targetdestroyed', (target) => fulfill(target.page())) - ); - await otherPage.close(); + const closePagePromise = new Promise((fulfill) => { + return context.once('targetdestroyed', (target) => { + return fulfill(target.page()); + }); + }); + await otherPage!.close(); expect(await closePagePromise).toBe(otherPage); - allPages = await Promise.all( - context.targets().map((target) => target.page()) - ); + allPages = (await Promise.all( + context.targets().map((target) => { + return target.page(); + }) + )) as Page[]; expect(allPages).toContain(page); expect(allPages).not.toContain(otherPage); } @@ -137,9 +153,11 @@ describe('Target', function () { const { page, server, context } = getTestState(); await page.goto(server.EMPTY_PAGE); - const createdTarget = new Promise((fulfill) => - context.once('targetcreated', (target) => fulfill(target)) - ); + const createdTarget = new Promise((fulfill) => { + return context.once('targetcreated', (target) => { + return fulfill(target); + }); + }); await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); @@ -148,14 +166,18 @@ describe('Target', function () { server.PREFIX + '/serviceworkers/empty/sw.js' ); - const destroyedTarget = new Promise((fulfill) => - context.once('targetdestroyed', (target) => fulfill(target)) - ); - await page.evaluate(() => - globalThis.registrationPromise.then((registration) => - registration.unregister() - ) - ); + const destroyedTarget = new Promise((fulfill) => { + return context.once('targetdestroyed', (target) => { + return fulfill(target); + }); + }); + await page.evaluate(() => { + return (globalThis as any).registrationPromise.then( + (registration: { unregister: () => any }) => { + return registration.unregister(); + } + ); + }); expect(await destroyedTarget).toBe(await createdTarget); } ); @@ -164,13 +186,15 @@ describe('Target', function () { await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); - const target = await context.waitForTarget( - (target) => target.type() === 'service_worker' - ); - const worker = await target.worker(); - expect(await worker.evaluate(() => self.toString())).toBe( - '[object ServiceWorkerGlobalScope]' - ); + const target = await context.waitForTarget((target) => { + return target.type() === 'service_worker'; + }); + const worker = (await target.worker())!; + expect( + await worker.evaluate(() => { + return self.toString(); + }) + ).toBe('[object ServiceWorkerGlobalScope]'); }); itFailsFirefox('should create a worker from a shared worker', async () => { const { page, server, context } = getTestState(); @@ -179,27 +203,33 @@ describe('Target', function () { await page.evaluate(() => { new SharedWorker('data:text/javascript,console.log("hi")'); }); - const target = await context.waitForTarget( - (target) => target.type() === 'shared_worker' - ); - const worker = await target.worker(); - expect(await worker.evaluate(() => self.toString())).toBe( - '[object SharedWorkerGlobalScope]' - ); + const target = await context.waitForTarget((target) => { + return target.type() === 'shared_worker'; + }); + const worker = (await target.worker())!; + expect( + await worker.evaluate(() => { + return self.toString(); + }) + ).toBe('[object SharedWorkerGlobalScope]'); }); itFailsFirefox('should report when a target url changes', async () => { const { page, server, context } = getTestState(); await page.goto(server.EMPTY_PAGE); - let changedTarget = new Promise((fulfill) => - context.once('targetchanged', (target) => fulfill(target)) - ); + let changedTarget = new Promise((fulfill) => { + return context.once('targetchanged', (target) => { + return fulfill(target); + }); + }); await page.goto(server.CROSS_PROCESS_PREFIX + '/'); expect((await changedTarget).url()).toBe(server.CROSS_PROCESS_PREFIX + '/'); - changedTarget = new Promise((fulfill) => - context.once('targetchanged', (target) => fulfill(target)) - ); + changedTarget = new Promise((fulfill) => { + return context.once('targetchanged', (target) => { + return fulfill(target); + }); + }); await page.goto(server.EMPTY_PAGE); expect((await changedTarget).url()).toBe(server.EMPTY_PAGE); }); @@ -207,20 +237,28 @@ describe('Target', function () { const { context } = getTestState(); let targetChanged = false; - const listener = () => (targetChanged = true); + const listener = () => { + return (targetChanged = true); + }; context.on('targetchanged', listener); - const targetPromise = new Promise((fulfill) => - context.once('targetcreated', (target) => fulfill(target)) - ); + const targetPromise = new Promise((fulfill) => { + return context.once('targetcreated', (target) => { + return fulfill(target); + }); + }); const newPagePromise = context.newPage(); const target = await targetPromise; expect(target.url()).toBe('about:blank'); const newPage = await newPagePromise; - const targetPromise2 = new Promise((fulfill) => - context.once('targetcreated', (target) => fulfill(target)) - ); - const evaluatePromise = newPage.evaluate(() => window.open('about:blank')); + const targetPromise2 = new Promise((fulfill) => { + return context.once('targetcreated', (target) => { + return fulfill(target); + }); + }); + const evaluatePromise = newPage.evaluate(() => { + return window.open('about:blank'); + }); const target2 = await targetPromise2; expect(target2.url()).toBe('about:blank'); await evaluatePromise; @@ -233,21 +271,22 @@ describe('Target', function () { async () => { const { page, server, context } = getTestState(); - let serverResponse = null; - server.setRoute('/one-style.css', (req, res) => (serverResponse = res)); + let serverResponse!: ServerResponse; + server.setRoute('/one-style.css', (_req, res) => { + return (serverResponse = res); + }); // Open a new page. Use window.open to connect to the page later. await Promise.all([ - page.evaluate( - (url: string) => window.open(url), - server.PREFIX + '/one-style.html' - ), + page.evaluate((url: string) => { + return window.open(url); + }, server.PREFIX + '/one-style.html'), server.waitForRequest('/one-style.css'), ]); // Connect to the opened page. - const target = await context.waitForTarget((target) => - target.url().includes('one-style.html') - ); - const newPage = await target.page(); + const target = await context.waitForTarget((target) => { + return target.url().includes('one-style.html'); + }); + const newPage = (await target.page())!; // Issue a redirect. serverResponse.writeHead(302, { location: '/injectedstyle.css' }); serverResponse.end(); @@ -262,12 +301,14 @@ describe('Target', function () { await page.goto(server.EMPTY_PAGE); const [createdTarget] = await Promise.all([ - new Promise((fulfill) => - context.once('targetcreated', (target) => fulfill(target)) - ), + new Promise((fulfill) => { + return context.once('targetcreated', (target) => { + return fulfill(target); + }); + }), page.goto(server.PREFIX + '/popup/window-open.html'), ]); - expect((await createdTarget.page()).url()).toBe( + expect((await createdTarget.page())!.url()).toBe( server.PREFIX + '/popup/popup.html' ); expect(createdTarget.opener()).toBe(page.target()); @@ -279,11 +320,13 @@ describe('Target', function () { const { browser, puppeteer, server } = getTestState(); let resolved = false; - const targetPromise = browser.waitForTarget( - (target) => target.url() === server.EMPTY_PAGE - ); + const targetPromise = browser.waitForTarget((target) => { + return target.url() === server.EMPTY_PAGE; + }); targetPromise - .then(() => (resolved = true)) + .then(() => { + return (resolved = true); + }) .catch((error) => { resolved = true; if (error instanceof puppeteer.errors.TimeoutError) { @@ -310,12 +353,19 @@ describe('Target', function () { it('should timeout waiting for a non-existent target', async () => { const { browser, server, puppeteer } = getTestState(); - let error = null; + let error!: Error; await browser - .waitForTarget((target) => target.url() === server.EMPTY_PAGE, { - timeout: 1, - }) - .catch((error_) => (error = error_)); + .waitForTarget( + (target) => { + return target.url() === server.EMPTY_PAGE; + }, + { + timeout: 1, + } + ) + .catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); }); diff --git a/test/src/touchscreen.spec.ts b/test/src/touchscreen.spec.ts index 36931d58526..f94f396ee34 100644 --- a/test/src/touchscreen.spec.ts +++ b/test/src/touchscreen.spec.ts @@ -20,7 +20,7 @@ import { setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; describeFailsFirefox('Touchscreen', function () { setupTestBrowserHooks(); @@ -28,22 +28,27 @@ describeFailsFirefox('Touchscreen', function () { it('should tap the button', async () => { const { puppeteer, page, server } = getTestState(); - const iPhone = puppeteer.devices['iPhone 6']; + const iPhone = puppeteer.devices['iPhone 6']!; await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/button.html'); await page.tap('button'); - expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); + expect( + await page.evaluate(() => { + return (globalThis as any).result; + }) + ).toBe('Clicked'); }); it('should report touches', async () => { const { puppeteer, page, server } = getTestState(); - const iPhone = puppeteer.devices['iPhone 6']; + const iPhone = puppeteer.devices['iPhone 6']!; await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/touches.html'); - const button = await page.$('button'); + const button = (await page.$('button'))!; await button.tap(); - expect(await page.evaluate(() => globalThis.getResult())).toEqual([ - 'Touchstart: 0', - 'Touchend: 0', - ]); + expect( + await page.evaluate(() => { + return (globalThis as any).getResult(); + }) + ).toEqual(['Touchstart: 0', 'Touchend: 0']); }); }); diff --git a/test/src/tracing.spec.ts b/test/src/tracing.spec.ts index 39907e8905b..e830cd07a27 100644 --- a/test/src/tracing.spec.ts +++ b/test/src/tracing.spec.ts @@ -17,12 +17,14 @@ import fs from 'fs'; import path from 'path'; import expect from 'expect'; -import { getTestState, describeChromeOnly } from './mocha-utils'; // eslint-disable-line import/extensions +import { getTestState, describeChromeOnly } from './mocha-utils.js'; +import { Browser } from '../../lib/cjs/puppeteer/common/Browser.js'; +import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; describeChromeOnly('Tracing', function () { - let outputFile; - let browser; - let page; + let outputFile!: string; + let browser!: Browser; + let page!: Page; /* we manually manage the browser here as we want a new browser for each * individual test, which isn't the default behaviour of getTestState() @@ -37,11 +39,8 @@ describeChromeOnly('Tracing', function () { afterEach(async () => { await browser.close(); - browser = null; - page = null; if (fs.existsSync(outputFile)) { fs.unlinkSync(outputFile); - outputFile = null; } }); it('should output a trace', async () => { @@ -93,10 +92,10 @@ describeChromeOnly('Tracing', function () { it('should throw if tracing on two pages', async () => { await page.tracing.start({ path: outputFile }); const newPage = await browser.newPage(); - let error = null; - await newPage.tracing - .start({ path: outputFile }) - .catch((error_) => (error = error_)); + let error!: Error; + await newPage.tracing.start({ path: outputFile }).catch((error_) => { + return (error = error_); + }); await newPage.close(); expect(error).toBeTruthy(); await page.tracing.stop(); @@ -106,7 +105,7 @@ describeChromeOnly('Tracing', function () { await page.tracing.start({ screenshots: true, path: outputFile }); await page.goto(server.PREFIX + '/grid.html'); - const trace = await page.tracing.stop(); + const trace = (await page.tracing.stop())!; const buf = fs.readFileSync(outputFile); expect(trace.toString()).toEqual(buf.toString()); }); @@ -138,18 +137,18 @@ describeChromeOnly('Tracing', function () { await page.tracing.start({ screenshots: true }); await page.goto(server.PREFIX + '/grid.html'); - const trace = await page.tracing.stop(); + const trace = (await page.tracing.stop())!; expect(trace.toString()).toContain('screenshot'); }); it('should properly fail if readProtocolStream errors out', async () => { await page.tracing.start({ path: __dirname }); - let error: Error = null; + let error!: Error; try { await page.tracing.stop(); } catch (error_) { - error = error_; + error = error_ as Error; } expect(error).toBeDefined(); }); diff --git a/test/src/utils.js b/test/src/utils.js deleted file mode 100644 index 60271411a62..00000000000 --- a/test/src/utils.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO (@jackfranklin): convert to TS and enable type checking. - -// @ts-nocheck -const path = require('path'); -const expect = require('expect'); -const GoldenUtils = require('./golden-utils.js'); -const PROJECT_ROOT = path.join(__dirname, '..', '..'); - -const utils = (module.exports = { - extendExpectWithToBeGolden: function (goldenDir, outputDir) { - expect.extend({ - toBeGolden: (testScreenshot, goldenFilePath) => { - const result = GoldenUtils.compare( - goldenDir, - outputDir, - testScreenshot, - goldenFilePath - ); - - return { - message: () => result.message, - pass: result.pass, - }; - }, - }); - }, - - /** - * @returns {string} - */ - projectRoot: function () { - return PROJECT_ROOT; - }, - - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - * @returns {!Frame} - */ - attachFrame: async function (page, frameId, url) { - const handle = await page.evaluateHandle(attachFrame, frameId, url); - return await handle.asElement().contentFrame(); - - async function attachFrame(frameId, url) { - const frame = document.createElement('iframe'); - frame.src = url; - frame.id = frameId; - document.body.appendChild(frame); - await new Promise((x) => (frame.onload = x)); - return frame; - } - }, - - isFavicon: function (request) { - return request.url().includes('favicon.ico'); - }, - - /** - * @param {!Page} page - * @param {string} frameId - */ - detachFrame: async function (page, frameId) { - await page.evaluate(detachFrame, frameId); - - function detachFrame(frameId) { - const frame = document.getElementById(frameId); - frame.remove(); - } - }, - - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - */ - navigateFrame: async function (page, frameId, url) { - await page.evaluate(navigateFrame, frameId, url); - - function navigateFrame(frameId, url) { - const frame = document.getElementById(frameId); - frame.src = url; - return new Promise((x) => (frame.onload = x)); - } - }, - - /** - * @param {!Frame} frame - * @param {string=} indentation - * @returns {Array} - */ - dumpFrames: function (frame, indentation) { - indentation = indentation || ''; - let description = frame.url().replace(/:\d{4}\//, ':/'); - if (frame.name()) { - description += ' (' + frame.name() + ')'; - } - const result = [indentation + description]; - for (const child of frame.childFrames()) { - result.push(...utils.dumpFrames(child, ' ' + indentation)); - } - return result; - }, - - /** - * @param {!EventEmitter} emitter - * @param {string} eventName - * @returns {!Promise} - */ - waitEvent: function (emitter, eventName, predicate = () => true) { - return new Promise((fulfill) => { - emitter.on(eventName, function listener(event) { - if (!predicate(event)) { - return; - } - emitter.removeListener(eventName, listener); - fulfill(event); - }); - }); - }, -}); diff --git a/test/src/utils.ts b/test/src/utils.ts new file mode 100644 index 00000000000..121c2dbe50d --- /dev/null +++ b/test/src/utils.ts @@ -0,0 +1,162 @@ +/** + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import expect from 'expect'; +import path from 'path'; +import { Frame } from '../../lib/cjs/puppeteer/common/FrameManager.js'; +import { Page } from '../../lib/cjs/puppeteer/common/Page.js'; +import { EventEmitter } from '../../lib/cjs/puppeteer/common/EventEmitter.js'; +import { compare } from './golden-utils.js'; + +const PROJECT_ROOT = path.join(__dirname, '..', '..'); + +export const extendExpectWithToBeGolden = ( + goldenDir: string, + outputDir: string +): void => { + expect.extend({ + toBeGolden: (testScreenshot: string | Buffer, goldenFilePath: string) => { + const result = compare( + goldenDir, + outputDir, + testScreenshot, + goldenFilePath + ); + + if (result.pass) { + return { + pass: true, + message: () => { + return void 0; + }, + }; + } else { + return { + pass: false, + message: () => { + return result.message; + }, + }; + } + }, + }); +}; + +export const projectRoot = (): string => { + return PROJECT_ROOT; +}; + +export const attachFrame = async ( + pageOrFrame: Page | Frame, + frameId: string, + url: string +): Promise => { + const handle = await pageOrFrame.evaluateHandle(attachFrame, frameId, url); + return (await handle.asElement()?.contentFrame()) ?? undefined; + + async function attachFrame(frameId: string, url: string) { + const frame = document.createElement('iframe'); + frame.src = url; + frame.id = frameId; + document.body.appendChild(frame); + await new Promise((x) => { + return (frame.onload = x); + }); + return frame; + } +}; + +export const isFavicon = (request: { + url: () => string | string[]; +}): boolean => { + return request.url().includes('favicon.ico'); +}; + +export async function detachFrame( + pageOrFrame: Page | Frame, + frameId: string +): Promise { + await pageOrFrame.evaluate(detachFrame, frameId); + + function detachFrame(frameId: string) { + const frame = document.getElementById(frameId) as HTMLIFrameElement; + frame.remove(); + } +} + +export async function navigateFrame( + pageOrFrame: Page | Frame, + frameId: string, + url: string +): Promise { + await pageOrFrame.evaluate(navigateFrame, frameId, url); + + function navigateFrame(frameId: string, url: any) { + const frame = document.getElementById(frameId) as HTMLIFrameElement; + frame.src = url; + return new Promise((x) => { + return (frame.onload = x); + }); + } +} + +export const dumpFrames = ( + frame: Frame, + indentation?: string +): Array => { + indentation = indentation || ''; + let description = frame.url().replace(/:\d{4}\//, ':/'); + if (frame.name()) { + description += ' (' + frame.name() + ')'; + } + const result = [indentation + description]; + for (const child of frame.childFrames()) { + result.push(...dumpFrames(child, ' ' + indentation)); + } + return result; +}; + +export const waitEvent = ( + emitter: EventEmitter, + eventName: string, + predicate: (event: any) => boolean = () => { + return true; + } +): Promise => { + return new Promise((fulfill) => { + emitter.on(eventName, function listener(event: any) { + if (!predicate(event)) { + return; + } + emitter.off(eventName, listener); + fulfill(event); + }); + }); +}; + +/** + * @deprecated Use exports directly. + */ +export default { + extendExpectWithToBeGolden, + waitEvent, + dumpFrames, + navigateFrame, + isFavicon, + attachFrame, + projectRoot, + detachFrame, +}; diff --git a/test/src/waittask.spec.ts b/test/src/waittask.spec.ts index 641e06d4cca..8b77c6fe2d5 100644 --- a/test/src/waittask.spec.ts +++ b/test/src/waittask.spec.ts @@ -14,15 +14,16 @@ * limitations under the License. */ -import utils from './utils.js'; -import sinon from 'sinon'; import expect from 'expect'; +import sinon from 'sinon'; +import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js'; import { getTestState, + itFailsFirefox, setupTestBrowserHooks, setupTestPageAndContextHooks, - itFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions +} from './mocha-utils.js'; +import { attachFrame, detachFrame } from './utils.js'; describe('waittask specs', function () { setupTestBrowserHooks(); @@ -33,13 +34,17 @@ describe('waittask specs', function () { * tests. Until we remove this method we still want to ensure we don't break * it. */ - beforeEach(() => sinon.stub(console, 'warn').callsFake(() => {})); + beforeEach(() => { + return sinon.stub(console, 'warn').callsFake(() => {}); + }); it('should wait for selector', async () => { const { page, server } = getTestState(); let found = false; - const waitFor = page.waitFor('div').then(() => (found = true)); + const waitFor = page.waitForSelector('div').then(() => { + return (found = true); + }); await page.goto(server.EMPTY_PAGE); expect(found).toBe(false); await page.goto(server.PREFIX + '/grid.html'); @@ -51,7 +56,9 @@ describe('waittask specs', function () { const { page, server } = getTestState(); let found = false; - const waitFor = page.waitFor('//div').then(() => (found = true)); + const waitFor = page.waitFor('//div').then(() => { + return (found = true); + }); await page.goto(server.EMPTY_PAGE); expect(found).toBe(false); await page.goto(server.PREFIX + '/grid.html'); @@ -74,8 +81,10 @@ describe('waittask specs', function () { const { page } = getTestState(); await page.setContent(`
some text
`); - let error = null; - await page.waitFor('/html/body/div').catch((error_) => (error = error_)); + let error!: Error; + await page.waitFor('/html/body/div').catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); }); it('should timeout', async () => { @@ -98,28 +107,31 @@ describe('waittask specs', function () { const { page } = getTestState(); await Promise.all([ - page.waitFor(() => window.innerWidth < 100), + page.waitFor(() => { + return window.innerWidth < 100; + }), page.setViewport({ width: 10, height: 10 }), ]); }); - it('should throw when unknown type', async () => { - const { page } = getTestState(); - - let error = null; - // @ts-expect-error purposefully passing bad type for test - await page.waitFor({ foo: 'bar' }).catch((error_) => (error = error_)); - expect(error.message).toContain('Unsupported target type'); - }); it('should wait for predicate with arguments', async () => { const { page } = getTestState(); - await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2); + await page.waitFor( + (arg1: number, arg2: number) => { + return arg1 !== arg2; + }, + {}, + 1, + 2 + ); }); it('should log a deprecation warning', async () => { const { page } = getTestState(); - await page.waitFor(() => true); + await page.waitFor(() => { + return true; + }); const consoleWarnStub = console.warn as sinon.SinonSpy; @@ -138,15 +150,19 @@ describe('waittask specs', function () { const { page } = getTestState(); const watchdog = page.waitForFunction('window.__FOO === 1'); - await page.evaluate(() => (globalThis.__FOO = 1)); + await page.evaluate(() => { + return ((globalThis as any).__FOO = 1); + }); await watchdog; }); it('should work when resolved right before execution context disposal', async () => { const { page } = getTestState(); - await page.evaluateOnNewDocument(() => (globalThis.__RELOADED = true)); + await page.evaluateOnNewDocument(() => { + return ((globalThis as any).__RELOADED = true); + }); await page.waitForFunction(() => { - if (!globalThis.__RELOADED) { + if (!(globalThis as any).__RELOADED) { window.location.reload(); } return true; @@ -159,13 +175,24 @@ describe('waittask specs', function () { const startTime = Date.now(); const polling = 100; const watchdog = page - .waitForFunction(() => globalThis.__FOO === 'hit', { polling }) - .then(() => (success = true)); - await page.evaluate(() => (globalThis.__FOO = 'hit')); + .waitForFunction( + () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling, + } + ) + .then(() => { + return (success = true); + }); + await page.evaluate(() => { + return ((globalThis as any).__FOO = 'hit'); + }); expect(success).toBe(false); - await page.evaluate(() => - document.body.appendChild(document.createElement('div')) - ); + await page.evaluate(() => { + return document.body.appendChild(document.createElement('div')); + }); await watchdog; expect(Date.now() - startTime).not.toBeLessThan(polling / 2); }); @@ -175,13 +202,24 @@ describe('waittask specs', function () { const startTime = Date.now(); const polling = 100; const watchdog = page - .waitForFunction(async () => globalThis.__FOO === 'hit', { polling }) - .then(() => (success = true)); - await page.evaluate(async () => (globalThis.__FOO = 'hit')); + .waitForFunction( + async () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling, + } + ) + .then(() => { + return (success = true); + }); + await page.evaluate(async () => { + return ((globalThis as any).__FOO = 'hit'); + }); expect(success).toBe(false); - await page.evaluate(async () => - document.body.appendChild(document.createElement('div')) - ); + await page.evaluate(async () => { + return document.body.appendChild(document.createElement('div')); + }); await watchdog; expect(Date.now() - startTime).not.toBeLessThan(polling / 2); }); @@ -190,15 +228,24 @@ describe('waittask specs', function () { let success = false; const watchdog = page - .waitForFunction(() => globalThis.__FOO === 'hit', { - polling: 'mutation', - }) - .then(() => (success = true)); - await page.evaluate(() => (globalThis.__FOO = 'hit')); + .waitForFunction( + () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling: 'mutation', + } + ) + .then(() => { + return (success = true); + }); + await page.evaluate(() => { + return ((globalThis as any).__FOO = 'hit'); + }); expect(success).toBe(false); - await page.evaluate(() => - document.body.appendChild(document.createElement('div')) - ); + await page.evaluate(() => { + return document.body.appendChild(document.createElement('div')); + }); await watchdog; }); it('should poll on mutation async', async () => { @@ -206,36 +253,56 @@ describe('waittask specs', function () { let success = false; const watchdog = page - .waitForFunction(async () => globalThis.__FOO === 'hit', { - polling: 'mutation', - }) - .then(() => (success = true)); - await page.evaluate(async () => (globalThis.__FOO = 'hit')); + .waitForFunction( + async () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling: 'mutation', + } + ) + .then(() => { + return (success = true); + }); + await page.evaluate(async () => { + return ((globalThis as any).__FOO = 'hit'); + }); expect(success).toBe(false); - await page.evaluate(async () => - document.body.appendChild(document.createElement('div')) - ); + await page.evaluate(async () => { + return document.body.appendChild(document.createElement('div')); + }); await watchdog; }); it('should poll on raf', async () => { const { page } = getTestState(); - const watchdog = page.waitForFunction(() => globalThis.__FOO === 'hit', { - polling: 'raf', + const watchdog = page.waitForFunction( + () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling: 'raf', + } + ); + await page.evaluate(() => { + return ((globalThis as any).__FOO = 'hit'); }); - await page.evaluate(() => (globalThis.__FOO = 'hit')); await watchdog; }); it('should poll on raf async', async () => { const { page } = getTestState(); const watchdog = page.waitForFunction( - async () => globalThis.__FOO === 'hit', + async () => { + return (globalThis as any).__FOO === 'hit'; + }, { polling: 'raf', } ); - await page.evaluate(async () => (globalThis.__FOO = 'hit')); + await page.evaluate(async () => { + return ((globalThis as any).__FOO = 'hit'); + }); await watchdog; }); itFailsFirefox('should work with strict CSP policy', async () => { @@ -243,100 +310,148 @@ describe('waittask specs', function () { server.setCSP('/empty.html', 'script-src ' + server.PREFIX); await page.goto(server.EMPTY_PAGE); - let error = null; + let error!: Error; await Promise.all([ page - .waitForFunction(() => globalThis.__FOO === 'hit', { polling: 'raf' }) - .catch((error_) => (error = error_)), - page.evaluate(() => (globalThis.__FOO = 'hit')), + .waitForFunction( + () => { + return (globalThis as any).__FOO === 'hit'; + }, + { + polling: 'raf', + } + ) + .catch((error_) => { + return (error = error_); + }), + page.evaluate(() => { + return ((globalThis as any).__FOO = 'hit'); + }), ]); - expect(error).toBe(null); + expect(error).toBeUndefined(); }); it('should throw on bad polling value', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { - await page.waitForFunction(() => !!document.body, { - polling: 'unknown', - }); + await page.waitForFunction( + () => { + return !!document.body; + }, + { + polling: 'unknown', + } + ); } catch (error_) { - error = error_; + if (isErrorLike(error_)) { + error = error_ as Error; + } } - expect(error).toBeTruthy(); - expect(error.message).toContain('polling'); + expect(error?.message).toContain('polling'); }); it('should throw negative polling interval', async () => { const { page } = getTestState(); - let error = null; + let error!: Error; try { - await page.waitForFunction(() => !!document.body, { polling: -10 }); + await page.waitForFunction( + () => { + return !!document.body; + }, + { polling: -10 } + ); } catch (error_) { - error = error_; + if (isErrorLike(error_)) { + error = error_ as Error; + } } - expect(error).toBeTruthy(); - expect(error.message).toContain('Cannot poll with non-positive interval'); + expect(error?.message).toContain( + 'Cannot poll with non-positive interval' + ); }); it('should return the success value as a JSHandle', async () => { const { page } = getTestState(); - expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5); + expect( + await ( + await page.waitForFunction(() => { + return 5; + }) + ).jsonValue() + ).toBe(5); }); it('should return the window as a success value', async () => { const { page } = getTestState(); - expect(await page.waitForFunction(() => window)).toBeTruthy(); + expect( + await page.waitForFunction(() => { + return window; + }) + ).toBeTruthy(); }); it('should accept ElementHandle arguments', async () => { const { page } = getTestState(); await page.setContent('
'); - const div = await page.$('div'); + const div = (await page.$('div'))!; let resolved = false; const waitForFunction = page .waitForFunction( - (element) => element.localName === 'div' && !element.parentElement, + (element: Element) => { + return element.localName === 'div' && !element.parentElement; + }, {}, div ) - .then(() => (resolved = true)); + .then(() => { + return (resolved = true); + }); expect(resolved).toBe(false); - await page.evaluate((element: HTMLElement) => element.remove(), div); + await page.evaluate((element: HTMLElement) => { + return element.remove(); + }, div); await waitForFunction; }); it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; - await page - .waitForFunction('false', { timeout: 10 }) - .catch((error_) => (error = error_)); - expect(error).toBeTruthy(); - expect(error.message).toContain('waiting for function failed: timeout'); + let error!: Error; + await page.waitForFunction('false', { timeout: 10 }).catch((error_) => { + return (error = error_); + }); + expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); + expect(error?.message).toContain('waiting for function failed: timeout'); }); it('should respect default timeout', async () => { const { page, puppeteer } = getTestState(); page.setDefaultTimeout(1); - let error = null; - await page.waitForFunction('false').catch((error_) => (error = error_)); + let error!: Error; + await page.waitForFunction('false').catch((error_) => { + return (error = error_); + }); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); - expect(error.message).toContain('waiting for function failed: timeout'); + expect(error?.message).toContain('waiting for function failed: timeout'); }); it('should disable timeout when its set to 0', async () => { const { page } = getTestState(); const watchdog = page.waitForFunction( () => { - globalThis.__counter = (globalThis.__counter || 0) + 1; - return globalThis.__injected; + (globalThis as any).__counter = + ((globalThis as any).__counter || 0) + 1; + return (globalThis as any).__injected; }, { timeout: 0, polling: 10 } ); - await page.waitForFunction(() => globalThis.__counter > 10); - await page.evaluate(() => (globalThis.__injected = true)); + await page.waitForFunction(() => { + return (globalThis as any).__counter > 10; + }); + await page.evaluate(() => { + return ((globalThis as any).__injected = true); + }); await watchdog; }); it('should survive cross-process navigation', async () => { @@ -345,24 +460,32 @@ describe('waittask specs', function () { let fooFound = false; const waitForFunction = page .waitForFunction('globalThis.__FOO === 1') - .then(() => (fooFound = true)); + .then(() => { + return (fooFound = true); + }); await page.goto(server.EMPTY_PAGE); expect(fooFound).toBe(false); await page.reload(); expect(fooFound).toBe(false); await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html'); expect(fooFound).toBe(false); - await page.evaluate(() => (globalThis.__FOO = 1)); + await page.evaluate(() => { + return ((globalThis as any).__FOO = 1); + }); await waitForFunction; expect(fooFound).toBe(true); }); it('should survive navigations', async () => { const { page, server } = getTestState(); - const watchdog = page.waitForFunction(() => globalThis.__done); + const watchdog = page.waitForFunction(() => { + return (globalThis as any).__done; + }); await page.goto(server.EMPTY_PAGE); await page.goto(server.PREFIX + '/consolelog.html'); - await page.evaluate(() => (globalThis.__done = true)); + await page.evaluate(() => { + return ((globalThis as any).__done = true); + }); await watchdog; }); }); @@ -401,8 +524,9 @@ describe('waittask specs', function () { }); describe('Frame.waitForSelector', function () { - const addElement = (tag) => - document.body.appendChild(document.createElement(tag)); + const addElement = (tag: string) => { + return document.body.appendChild(document.createElement(tag)); + }; it('should immediately resolve promise if node exists', async () => { const { page, server } = getTestState(); @@ -417,13 +541,18 @@ describe('waittask specs', function () { itFailsFirefox('should work with removed MutationObserver', async () => { const { page } = getTestState(); - await page.evaluate(() => delete window.MutationObserver); + await page.evaluate(() => { + // @ts-expect-error We want to remove it for the test. + return delete window.MutationObserver; + }); const [handle] = await Promise.all([ page.waitForSelector('.zombo'), page.setContent(`
anything
`), ]); expect( - await page.evaluate((x: HTMLElement) => x.textContent, handle) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, handle) ).toBe('anything'); }); @@ -435,10 +564,8 @@ describe('waittask specs', function () { const watchdog = frame.waitForSelector('div'); await frame.evaluate(addElement, 'br'); await frame.evaluate(addElement, 'div'); - const eHandle = await watchdog; - const tagName = await eHandle - .getProperty('tagName') - .then((e) => e.jsonValue()); + const eHandle = (await watchdog)!; + const tagName = await (await eHandle.getProperty('tagName')).jsonValue(); expect(tagName).toBe('DIV'); }); @@ -448,10 +575,10 @@ describe('waittask specs', function () { await page.goto(server.EMPTY_PAGE); const watchdog = page.waitForSelector('h3 div'); await page.evaluate(addElement, 'span'); - await page.evaluate( - () => - (document.querySelector('span').innerHTML = '

') - ); + await page.evaluate(() => { + return (document.querySelector('span')!.innerHTML = + '

'); + }); await watchdog; }); @@ -461,43 +588,43 @@ describe('waittask specs', function () { const { page, server } = getTestState(); await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const otherFrame = page.frames()[1]; + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + const otherFrame = page.frames()[1]!; const watchdog = page.waitForSelector('div'); await otherFrame.evaluate(addElement, 'div'); await page.evaluate(addElement, 'div'); const eHandle = await watchdog; - expect(eHandle.executionContext().frame()).toBe(page.mainFrame()); + expect(eHandle?.executionContext().frame()).toBe(page.mainFrame()); } ); itFailsFirefox('should run in specified frame', async () => { const { page, server } = getTestState(); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - const frame1 = page.frames()[1]; - const frame2 = page.frames()[2]; + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + await attachFrame(page, 'frame2', server.EMPTY_PAGE); + const frame1 = page.frames()[1]!; + const frame2 = page.frames()[2]!; const waitForSelectorPromise = frame2.waitForSelector('div'); await frame1.evaluate(addElement, 'div'); await frame2.evaluate(addElement, 'div'); const eHandle = await waitForSelectorPromise; - expect(eHandle.executionContext().frame()).toBe(frame2); + expect(eHandle?.executionContext().frame()).toBe(frame2); }); itFailsFirefox('should throw when frame is detached', async () => { const { page, server } = getTestState(); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]; - let waitError = null; - const waitPromise = frame - .waitForSelector('.box') - .catch((error) => (waitError = error)); - await utils.detachFrame(page, 'frame1'); + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame = page.frames()[1]!; + let waitError: Error | undefined; + const waitPromise = frame.waitForSelector('.box').catch((error) => { + return (waitError = error); + }); + await detachFrame(page, 'frame1'); await waitPromise; expect(waitError).toBeTruthy(); - expect(waitError.message).toContain( + expect(waitError?.message).toContain( 'waitForFunction failed: frame got detached.' ); }); @@ -505,9 +632,9 @@ describe('waittask specs', function () { const { page, server } = getTestState(); let boxFound = false; - const waitForSelector = page - .waitForSelector('.box') - .then(() => (boxFound = true)); + const waitForSelector = page.waitForSelector('.box').then(() => { + return (boxFound = true); + }); await page.goto(server.EMPTY_PAGE); expect(boxFound).toBe(false); await page.reload(); @@ -522,18 +649,22 @@ describe('waittask specs', function () { let divFound = false; const waitForSelector = page .waitForSelector('div', { visible: true }) - .then(() => (divFound = true)); + .then(() => { + return (divFound = true); + }); await page.setContent( `
1
` ); expect(divFound).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('display') - ); + await page.evaluate(() => { + return document.querySelector('div')?.style.removeProperty('display'); + }); expect(divFound).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('visibility') - ); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.removeProperty('visibility'); + }); expect(await waitForSelector).toBe(true); expect(divFound).toBe(true); }); @@ -543,18 +674,22 @@ describe('waittask specs', function () { let divVisible = false; const waitForSelector = page .waitForSelector('div#inner', { visible: true }) - .then(() => (divVisible = true)); + .then(() => { + return (divVisible = true); + }); await page.setContent( `
hi
` ); expect(divVisible).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('display') - ); + await page.evaluate(() => { + return document.querySelector('div')?.style.removeProperty('display'); + }); expect(divVisible).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.removeProperty('visibility') - ); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.removeProperty('visibility'); + }); expect(await waitForSelector).toBe(true); expect(divVisible).toBe(true); }); @@ -565,12 +700,16 @@ describe('waittask specs', function () { await page.setContent(`
`); const waitForSelector = page .waitForSelector('div', { hidden: true }) - .then(() => (divHidden = true)); + .then(() => { + return (divHidden = true); + }); await page.waitForSelector('div'); // do a round trip expect(divHidden).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.setProperty('visibility', 'hidden') - ); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.setProperty('visibility', 'hidden'); + }); expect(await waitForSelector).toBe(true); expect(divHidden).toBe(true); }); @@ -581,12 +720,16 @@ describe('waittask specs', function () { await page.setContent(`
`); const waitForSelector = page .waitForSelector('div', { hidden: true }) - .then(() => (divHidden = true)); + .then(() => { + return (divHidden = true); + }); await page.waitForSelector('div'); // do a round trip expect(divHidden).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.setProperty('display', 'none') - ); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.setProperty('display', 'none'); + }); expect(await waitForSelector).toBe(true); expect(divHidden).toBe(true); }); @@ -597,10 +740,14 @@ describe('waittask specs', function () { let divRemoved = false; const waitForSelector = page .waitForSelector('div', { hidden: true }) - .then(() => (divRemoved = true)); + .then(() => { + return (divRemoved = true); + }); await page.waitForSelector('div'); // do a round trip expect(divRemoved).toBe(false); - await page.evaluate(() => document.querySelector('div').remove()); + await page.evaluate(() => { + return document.querySelector('div')?.remove(); + }); expect(await waitForSelector).toBe(true); expect(divRemoved).toBe(true); }); @@ -615,26 +762,27 @@ describe('waittask specs', function () { it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; - await page - .waitForSelector('div', { timeout: 10 }) - .catch((error_) => (error = error_)); - expect(error).toBeTruthy(); - expect(error.message).toContain( + let error!: Error; + await page.waitForSelector('div', { timeout: 10 }).catch((error_) => { + return (error = error_); + }); + expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); + expect(error?.message).toContain( 'waiting for selector `div` failed: timeout' ); - expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); it('should have an error message specifically for awaiting an element to be hidden', async () => { const { page } = getTestState(); await page.setContent(`
`); - let error = null; + let error!: Error; await page .waitForSelector('div', { hidden: true, timeout: 10 }) - .catch((error_) => (error = error_)); + .catch((error_) => { + return (error = error_); + }); expect(error).toBeTruthy(); - expect(error.message).toContain( + expect(error?.message).toContain( 'waiting for selector `div` to be hidden failed: timeout' ); }); @@ -643,14 +791,14 @@ describe('waittask specs', function () { const { page } = getTestState(); let divFound = false; - const waitForSelector = page - .waitForSelector('.zombo') - .then(() => (divFound = true)); + const waitForSelector = page.waitForSelector('.zombo').then(() => { + return (divFound = true); + }); await page.setContent(`
`); expect(divFound).toBe(false); - await page.evaluate( - () => (document.querySelector('div').className = 'zombo') - ); + await page.evaluate(() => { + return (document.querySelector('div')!.className = 'zombo'); + }); expect(await waitForSelector).toBe(true); }); it('should return the element handle', async () => { @@ -659,28 +807,28 @@ describe('waittask specs', function () { const waitForSelector = page.waitForSelector('.zombo'); await page.setContent(`
anything
`); expect( - await page.evaluate( - (x: HTMLElement) => x.textContent, - await waitForSelector - ) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, await waitForSelector) ).toBe('anything'); }); it('should have correct stack trace for timeout', async () => { const { page } = getTestState(); - let error; - await page - .waitForSelector('.zombo', { timeout: 10 }) - .catch((error_) => (error = error_)); - expect(error.stack).toContain('waiting for selector `.zombo` failed'); + let error!: Error; + await page.waitForSelector('.zombo', { timeout: 10 }).catch((error_) => { + return (error = error_); + }); + expect(error?.stack).toContain('waiting for selector `.zombo` failed'); // The extension is ts here as Mocha maps back via sourcemaps. - expect(error.stack).toContain('waittask.spec.ts'); + expect(error?.stack).toContain('waittask.spec.ts'); }); }); describe('Frame.waitForXPath', function () { - const addElement = (tag) => - document.body.appendChild(document.createElement(tag)); + const addElement = (tag: string) => { + return document.body.appendChild(document.createElement(tag)); + }; it('should support some fancy xpath', async () => { const { page } = getTestState(); @@ -690,51 +838,51 @@ describe('waittask specs', function () { '//p[normalize-space(.)="hello world"]' ); expect( - await page.evaluate( - (x: HTMLElement) => x.textContent, - await waitForXPath - ) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, await waitForXPath) ).toBe('hello world '); }); it('should respect timeout', async () => { const { page, puppeteer } = getTestState(); - let error = null; - await page - .waitForXPath('//div', { timeout: 10 }) - .catch((error_) => (error = error_)); - expect(error).toBeTruthy(); - expect(error.message).toContain( + let error!: Error; + await page.waitForXPath('//div', { timeout: 10 }).catch((error_) => { + return (error = error_); + }); + expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); + expect(error?.message).toContain( 'waiting for XPath `//div` failed: timeout' ); - expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); itFailsFirefox('should run in specified frame', async () => { const { page, server } = getTestState(); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - const frame1 = page.frames()[1]; - const frame2 = page.frames()[2]; + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + await attachFrame(page, 'frame2', server.EMPTY_PAGE); + const frame1 = page.frames()[1]!; + const frame2 = page.frames()[2]!; const waitForXPathPromise = frame2.waitForXPath('//div'); await frame1.evaluate(addElement, 'div'); await frame2.evaluate(addElement, 'div'); const eHandle = await waitForXPathPromise; - expect(eHandle.executionContext().frame()).toBe(frame2); + expect(eHandle?.executionContext().frame()).toBe(frame2); }); itFailsFirefox('should throw when frame is detached', async () => { const { page, server } = getTestState(); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]; - let waitError = null; + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame = page.frames()[1]!; + let waitError: Error | undefined; const waitPromise = frame .waitForXPath('//*[@class="box"]') - .catch((error) => (waitError = error)); - await utils.detachFrame(page, 'frame1'); + .catch((error) => { + return (waitError = error); + }); + await detachFrame(page, 'frame1'); await waitPromise; expect(waitError).toBeTruthy(); - expect(waitError.message).toContain( + expect(waitError?.message).toContain( 'waitForFunction failed: frame got detached.' ); }); @@ -745,12 +893,16 @@ describe('waittask specs', function () { await page.setContent(`
`); const waitForXPath = page .waitForXPath('//div', { hidden: true }) - .then(() => (divHidden = true)); + .then(() => { + return (divHidden = true); + }); await page.waitForXPath('//div'); // do a round trip expect(divHidden).toBe(false); - await page.evaluate(() => - document.querySelector('div').style.setProperty('display', 'none') - ); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.setProperty('display', 'none'); + }); expect(await waitForXPath).toBe(true); expect(divHidden).toBe(true); }); @@ -760,10 +912,9 @@ describe('waittask specs', function () { const waitForXPath = page.waitForXPath('//*[@class="zombo"]'); await page.setContent(`
anything
`); expect( - await page.evaluate( - (x: HTMLElement) => x.textContent, - await waitForXPath - ) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, await waitForXPath) ).toBe('anything'); }); it('should allow you to select a text node', async () => { @@ -771,7 +922,7 @@ describe('waittask specs', function () { await page.setContent(`
some text
`); const text = await page.waitForXPath('//div/text()'); - expect(await (await text.getProperty('nodeType')).jsonValue()).toBe( + expect(await (await text!.getProperty('nodeType')!).jsonValue()).toBe( 3 /* Node.TEXT_NODE */ ); }); @@ -781,10 +932,9 @@ describe('waittask specs', function () { await page.setContent(`
some text
`); const waitForXPath = page.waitForXPath('/html/body/div'); expect( - await page.evaluate( - (x: HTMLElement) => x.textContent, - await waitForXPath - ) + await page.evaluate((x: HTMLElement) => { + return x.textContent; + }, await waitForXPath) ).toBe('some text'); }); }); diff --git a/test/src/worker.spec.ts b/test/src/worker.spec.ts index fde54500e5b..f2869ccc648 100644 --- a/test/src/worker.spec.ts +++ b/test/src/worker.spec.ts @@ -15,16 +15,15 @@ */ import expect from 'expect'; +import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; +import { WebWorker } from '../../lib/cjs/puppeteer/common/WebWorker.js'; import { + describeFailsFirefox, getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, - describeFailsFirefox, -} from './mocha-utils'; // eslint-disable-line import/extensions -import utils from './utils.js'; -import { WebWorker } from '../../lib/cjs/puppeteer/common/WebWorker.js'; -import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; -const { waitEvent } = utils; +} from './mocha-utils.js'; +import { waitEvent } from './utils.js'; describeFailsFirefox('Workers', function () { setupTestBrowserHooks(); @@ -33,15 +32,19 @@ describeFailsFirefox('Workers', function () { const { page, server } = getTestState(); await Promise.all([ - new Promise((x) => page.once('workercreated', x)), + new Promise((x) => { + return page.once('workercreated', x); + }), page.goto(server.PREFIX + '/worker/worker.html'), ]); - const worker = page.workers()[0]; - expect(worker.url()).toContain('worker.js'); + const worker = page.workers()[0]!; + expect(worker?.url()).toContain('worker.js'); - expect(await worker.evaluate(() => globalThis.workerFunction())).toBe( - 'worker function result' - ); + expect( + await worker?.evaluate(() => { + return (globalThis as any).workerFunction(); + }) + ).toBe('worker function result'); await page.goto(server.EMPTY_PAGE); expect(page.workers().length).toBe(0); @@ -49,25 +52,26 @@ describeFailsFirefox('Workers', function () { it('should emit created and destroyed events', async () => { const { page } = getTestState(); - const workerCreatedPromise = new Promise((x) => - page.once('workercreated', x) - ); - const workerObj = await page.evaluateHandle( - () => new Worker('data:text/javascript,1') - ); + const workerCreatedPromise = new Promise((x) => { + return page.once('workercreated', x); + }); + const workerObj = await page.evaluateHandle(() => { + return new Worker('data:text/javascript,1'); + }); const worker = await workerCreatedPromise; - const workerThisObj = await worker.evaluateHandle(() => this); - const workerDestroyedPromise = new Promise((x) => - page.once('workerdestroyed', x) - ); - await page.evaluate( - (workerObj: Worker) => workerObj.terminate(), - workerObj - ); + const workerThisObj = await worker.evaluateHandle(() => { + return this; + }); + const workerDestroyedPromise = new Promise((x) => { + return page.once('workerdestroyed', x); + }); + await page.evaluate((workerObj: Worker) => { + return workerObj.terminate(); + }, workerObj); expect(await workerDestroyedPromise).toBe(worker); - const error = await workerThisObj - .getProperty('self') - .catch((error) => error); + const error = await workerThisObj.getProperty('self').catch((error) => { + return error; + }); expect(error.message).toContain('Most likely the worker has been closed.'); }); it('should report console logs', async () => { @@ -75,7 +79,9 @@ describeFailsFirefox('Workers', function () { const [message] = await Promise.all([ waitEvent(page, 'console'), - page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)), + page.evaluate(() => { + return new Worker(`data:text/javascript,console.log(1)`); + }), ]); expect(message.text()).toBe('1'); expect(message.location()).toEqual({ @@ -87,39 +93,42 @@ describeFailsFirefox('Workers', function () { it('should have JSHandles for console logs', async () => { const { page } = getTestState(); - const logPromise = new Promise((x) => - page.on('console', x) - ); - await page.evaluate( - () => new Worker(`data:text/javascript,console.log(1,2,3,this)`) - ); + const logPromise = new Promise((x) => { + return page.on('console', x); + }); + await page.evaluate(() => { + return new Worker(`data:text/javascript,console.log(1,2,3,this)`); + }); const log = await logPromise; expect(log.text()).toBe('1 2 3 JSHandle@object'); expect(log.args().length).toBe(4); - expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe( + expect(await (await log.args()[3]!.getProperty('origin')).jsonValue()).toBe( 'null' ); }); it('should have an execution context', async () => { const { page } = getTestState(); - const workerCreatedPromise = new Promise((x) => - page.once('workercreated', x) - ); - await page.evaluate( - () => new Worker(`data:text/javascript,console.log(1)`) - ); + const workerCreatedPromise = new Promise((x) => { + return page.once('workercreated', x); + }); + await page.evaluate(() => { + return new Worker(`data:text/javascript,console.log(1)`); + }); const worker = await workerCreatedPromise; expect(await (await worker.executionContext()).evaluate('1+1')).toBe(2); }); it('should report errors', async () => { const { page } = getTestState(); - const errorPromise = new Promise((x) => page.on('pageerror', x)); - await page.evaluate( - () => - new Worker(`data:text/javascript, throw new Error('this is my error');`) - ); + const errorPromise = new Promise((x) => { + return page.on('pageerror', x); + }); + await page.evaluate(() => { + return new Worker( + `data:text/javascript, throw new Error('this is my error');` + ); + }); const errorLog = await errorPromise; expect(errorLog.message).toContain('this is my error'); }); diff --git a/test/tsconfig.json b/test/tsconfig.json index abe9d11796b..f7bdf270f7d 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,17 +1,11 @@ { + "extends": "../tsconfig.base.json", "compilerOptions": { - "esModuleInterop": true, - "allowJs": true, - "checkJs": false, - "target": "esnext", - "module": "CommonJS", - "moduleResolution": "node", "declaration": false, "declarationMap": false, - "resolveJsonModule": true, - "sourceMap": true, - "rootDir": "src", - "outDir": "build" + "module": "CommonJS", + "outDir": "build", + "rootDir": "src" }, "include": ["src"], "references": [ diff --git a/utils/testserver/src/index.ts b/utils/testserver/src/index.ts index 203b113a6f3..cdcff72d93b 100644 --- a/utils/testserver/src/index.ts +++ b/utils/testserver/src/index.ts @@ -43,10 +43,10 @@ interface Subscriber { type TestIncomingMessage = IncomingMessage & { postBody?: Promise }; export class TestServer { - PORT?: number; - PREFIX?: string; - CROSS_PROCESS_PREFIX?: string; - EMPTY_PAGE?: string; + PORT!: number; + PREFIX!: string; + CROSS_PROCESS_PREFIX!: string; + EMPTY_PAGE!: string; #dirPath: string; #server: HttpsServer | HttpServer; diff --git a/utils/testserver/tsconfig.json b/utils/testserver/tsconfig.json index 16efc53207a..c2576c2564f 100644 --- a/utils/testserver/tsconfig.json +++ b/utils/testserver/tsconfig.json @@ -1,11 +1,11 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "composite": true, "allowJs": true, - "module": "CommonJS" + "composite": true, + "module": "CommonJS", + "outDir": "lib", + "rootDir": "src" }, "include": ["src"] }