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).
This commit is contained in:
parent
80373f7a12
commit
570087ea94
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,7 +3,7 @@
|
||||
/.dev_profile*
|
||||
/.local-chromium/
|
||||
/.local-firefox/
|
||||
/test/test-user-data-dir*
|
||||
/test/output-*/
|
||||
build/
|
||||
coverage
|
||||
coverage/
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1527,6 +1527,7 @@ const devices: Device[] = [
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -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<string, typeof CustomError>;
|
||||
export interface PuppeteerErrors {
|
||||
TimeoutError: typeof TimeoutError;
|
||||
ProtocolError: typeof ProtocolError;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const puppeteerErrors: PuppeteerErrors = {
|
||||
export const puppeteerErrors: PuppeteerErrors = Object.freeze({
|
||||
TimeoutError,
|
||||
};
|
||||
ProtocolError,
|
||||
});
|
||||
|
@ -2293,7 +2293,9 @@ export class Page extends EventEmitter {
|
||||
*/
|
||||
async emulateMediaType(type?: string): Promise<void> {
|
||||
assert(
|
||||
type === 'screen' || type === 'print' || type === null,
|
||||
type === 'screen' ||
|
||||
type === 'print' ||
|
||||
(type ?? undefined) === undefined,
|
||||
'Unsupported media type: ' + type
|
||||
);
|
||||
await this.#client.send('Emulation.setEmulatedMedia', {
|
||||
|
@ -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<typeof fetch> => {
|
||||
return globalThis.fetch || (await import('cross-fetch')).fetch;
|
||||
return (globalThis as any).fetch || (await import('cross-fetch')).fetch;
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
rules: {
|
||||
'arrow-body-style': ['error', 'always'],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
|
4
test/fixtures/dumpio.js
vendored
4
test/fixtures/dumpio.js
vendored
@ -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();
|
||||
})();
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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', () => {
|
||||
|
@ -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 {
|
||||
|
@ -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 () {
|
||||
'<a href="" role="slider" aria-orientation="vertical">11</a>'
|
||||
);
|
||||
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('<input type="number" aria-autocomplete="list" />');
|
||||
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 () {
|
||||
'<div role="grid" tabIndex=-1 aria-multiselectable=true>hey</div>'
|
||||
);
|
||||
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 () {
|
||||
'<div role="grid" tabIndex=-1 aria-keyshortcuts="foo">hey</div>'
|
||||
);
|
||||
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(`
|
||||
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
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(`<button>My Button</button>`);
|
||||
|
||||
const button = await page.$<HTMLButtonElement>('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(`<input title="My Input" value="My Value">`);
|
||||
|
||||
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 () {
|
||||
</div>
|
||||
`);
|
||||
|
||||
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(`<button>My Button</button>`);
|
||||
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(`<div><button>My Button</button></div>`);
|
||||
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;
|
||||
|
@ -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(
|
||||
'<div id="div"><button id="btn" role="button">Submit</button></div>'
|
||||
);
|
||||
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(
|
||||
'<div id="div"><button id="btn" role="button">Submit</button></div>'
|
||||
);
|
||||
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', () => {
|
||||
<div role="menu" id="mnu2" aria-label="menu div"></div>
|
||||
`
|
||||
);
|
||||
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', () => {
|
||||
<div role="menu" id="mnu2" aria-label="menu-label2">menu div</div>
|
||||
`
|
||||
);
|
||||
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', () => {
|
||||
<div role="menu" id="mnu2" aria-label="menu-label2">menu div</div>
|
||||
`
|
||||
);
|
||||
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 = `<div><button>test</button></div>`)
|
||||
);
|
||||
const element = await page.$('div');
|
||||
await element.waitForSelector('aria/test');
|
||||
await page.evaluate(() => {
|
||||
return (document.body.innerHTML = `<div><button>test</button></div>`);
|
||||
});
|
||||
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(`<h1>anything</h1>`),
|
||||
]);
|
||||
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 =
|
||||
'<h3><div aria-label="name"></div></h3>')
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
return (document.querySelector('span')!.innerHTML =
|
||||
'<h3><div aria-label="name"></div></h3>');
|
||||
});
|
||||
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(
|
||||
`<div aria-label='name' style='display: none; visibility: hidden;'>1</div>`
|
||||
);
|
||||
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(
|
||||
`<div style='display: none; visibility: hidden;'><div aria-label="inner">hi</div></div>`
|
||||
);
|
||||
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(`<div role='main' style='display: block;'></div>`);
|
||||
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(`<div role='main'></div>`);
|
||||
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(`<div aria-label='notZombo'></div>`);
|
||||
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(`<div aria-label='zombo'>anything</div>`);
|
||||
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');
|
||||
|
@ -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();
|
||||
|
@ -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()]);
|
||||
|
@ -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(`
|
||||
<script>
|
||||
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
|
||||
</script>
|
||||
`)
|
||||
);
|
||||
`);
|
||||
});
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
@ -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 () {
|
||||
</svg>
|
||||
`);
|
||||
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 () {
|
||||
<span onclick='javascript:window.CLICKED=42'></span>
|
||||
`);
|
||||
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 () {
|
||||
<span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>
|
||||
`);
|
||||
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('<div style="width:100px;height:100px">spacer</div>');
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
@ -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<void>>((src) => {
|
||||
let fulfill;
|
||||
const promise = new Promise<void>((x) => (fulfill = x));
|
||||
const iframe = document.createElement('iframe');
|
||||
let fulfill!: () => void;
|
||||
const promise = new Promise<void>((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<void>>((src) => {
|
||||
let fulfill;
|
||||
const promise = new Promise<void>((x) => (fulfill = x));
|
||||
let fulfill!: () => void;
|
||||
const promise = new Promise<void>((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(
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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!'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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('<div style="display:none">hi</div>');
|
||||
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(
|
||||
'<div style="width: 100px; height: 100px">hello</div>'
|
||||
);
|
||||
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 () {
|
||||
<rect id="theRect" x="30" y="50" width="200" height="300"></rect>
|
||||
</svg>
|
||||
`);
|
||||
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<HTMLElement>('#frame1');
|
||||
const frame = document.querySelector<HTMLElement>('#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('<div style="display:none">hi</div>');
|
||||
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<ElementHandle>(
|
||||
const buttonHandle = await page.evaluateHandle<ElementHandle>(() => {
|
||||
// @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<ElementHandle>(
|
||||
() => document.querySelector('button').firstChild
|
||||
);
|
||||
let error = null;
|
||||
await buttonTextNode.click().catch((error_) => (error = error_));
|
||||
const buttonTextNode = await page.evaluateHandle<ElementHandle>(() => {
|
||||
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<br>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(
|
||||
'<div id="not-foo"></div><div class="bar">bar2</div><div class="foo">Foo1</div>'
|
||||
);
|
||||
let element = await waitFor;
|
||||
let element = (await waitFor)!;
|
||||
expect(element).toBeDefined();
|
||||
|
||||
const innerWaitFor = element.waitForSelector('.bar');
|
||||
await element.evaluate((el) => {
|
||||
el.innerHTML = '<div class="bar">bar1</div>';
|
||||
});
|
||||
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 () {
|
||||
</div>`
|
||||
);
|
||||
|
||||
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 () {
|
||||
'<div id="not-foo"></div><div class="foo">Foo1</div><div class="foo baz">Foo2</div>'
|
||||
);
|
||||
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 () {
|
||||
'<div id="not-foo"></div><div class="foo">Foo1</div><div class="foo baz">Foo2</div>'
|
||||
);
|
||||
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(
|
||||
'<div id="not-foo"></div><div class="bar">bar2</div><div class="foo">Foo1</div>'
|
||||
);
|
||||
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 = '<div class="bar">bar1</div>';
|
||||
});
|
||||
|
||||
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 () {
|
||||
'<div id="not-foo"></div><div class="foo"><div id="nested-foo" class="foo"/></div><div class="foo baz">Foo2</div>'
|
||||
);
|
||||
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 () {
|
||||
'<div id="not-foo"></div><div class="foo">text</div><div class="foo baz">content</div>'
|
||||
);
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
@ -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')
|
||||
|
@ -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<string>()
|
||||
.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('<section>42</section>');
|
||||
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('<section>39</section>');
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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<string>(
|
||||
(x) => (wsEndPointCallback = x)
|
||||
);
|
||||
let wsEndPointCallback: (value: string) => void;
|
||||
const wsEndPointPromise = new Promise<string>((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);
|
||||
});
|
||||
|
@ -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:<PORT>/frames/nested-frames.html',
|
||||
' http://localhost:<PORT>/frames/two-frames.html (2frames)',
|
||||
' http://localhost:<PORT>/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]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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 = `<link rel="stylesheet" href="file://${diffStylePath}">` + html;
|
||||
const result = diff.diffLines(expected, actual);
|
||||
const html = result.reduce((text, change) => {
|
||||
text += change.added
|
||||
? `<span class='ins'>${change.value}</span>`
|
||||
: change.removed
|
||||
? `<span class='del'>${change.value}</span>`
|
||||
: change.value;
|
||||
return text;
|
||||
}, `<link rel="stylesheet" href="file://${path.join(__dirname, 'diffstyle.css')}">`);
|
||||
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));
|
||||
}
|
||||
};
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
@ -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(`<input type=file>`);
|
||||
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(`<input type=file>`);
|
||||
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(`<input type=file>`);
|
||||
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(`<input type=file>`);
|
||||
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(`<input type=file>`);
|
||||
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(`<input type=file>`);
|
||||
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(
|
||||
|
@ -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<Record<string, string>>();
|
||||
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<string[]>();
|
||||
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<string>();
|
||||
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('<div>ee!</div>');
|
||||
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: {
|
||||
|
@ -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');
|
||||
|
@ -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:<PORT>/frames/nested-frames.html',
|
||||
' http://localhost:<PORT>/frames/two-frames.html (2frames)',
|
||||
@ -740,7 +781,11 @@ describe('Launcher specs', function () {
|
||||
' http://localhost:<PORT>/frames/frame.html (dos)',
|
||||
' http://localhost:<PORT>/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<Page>((x) =>
|
||||
browserOne.once('targetcreated', (target) => x(target.page()))
|
||||
),
|
||||
new Promise<Page>((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'),
|
||||
|
@ -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<void> => {
|
||||
state.server.reset();
|
||||
state.httpsServer.reset();
|
||||
state.server!.reset();
|
||||
state.httpsServer!.reset();
|
||||
},
|
||||
|
||||
afterAll: [
|
||||
async (): Promise<void> => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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<KeyInput, string>([
|
||||
['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 };
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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,<div>yo</div>';
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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<void>((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<HTTPResponse>((resolve) =>
|
||||
page.on('response', (response) => resolve(response))
|
||||
);
|
||||
const responsePromise = new Promise<HTTPResponse>((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();
|
||||
|
@ -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';
|
||||
});
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
||||
|
@ -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('<section id="testAttribute">43543</section>');
|
||||
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('<section>hello</section>');
|
||||
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('<section>hello</section><div> world</div>');
|
||||
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(
|
||||
'<div>hello</div><div>beautiful</div><div>world!</div>'
|
||||
);
|
||||
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(
|
||||
'<section>2</section><section>2</section><section>1</section><div>3</div>'
|
||||
);
|
||||
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('<section>test</section>');
|
||||
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('<div>A</div><br/><div>B</div>');
|
||||
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('<section>test</section>');
|
||||
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(
|
||||
'<html><body><div class="second"><div class="inner">A</div></div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><div class="second"><div class="inner">B</div></div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><div class="tweet"><div class="like">100</div><div class="retweets">10</div></div></body></html>'
|
||||
);
|
||||
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 =
|
||||
'<div class="a">not-a-child-div</div><div id="myId"><div class="a">a-child-div</div></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 =
|
||||
'<div class="a">not-a-child-div</div><div id="myId"></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(
|
||||
'<html><body><div class="tweet"><div class="like">100</div><div class="like">10</div></div></body></html>'
|
||||
);
|
||||
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 =
|
||||
'<div class="a">not-a-child-div</div><div id="myId"><div class="a">a1-child-div</div><div class="a">a2-child-div</div></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 =
|
||||
'<div class="a">not-a-child-div</div><div id="myId"></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(
|
||||
'<html><body><div>A</div><br/><div>B</div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><span>A</span><br/><span>B</span></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><div class="second"><div class="inner">A</div></div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><div class="second"><div class="inner">B</div></div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><div>A</div><br/><div>B</div></body></html>'
|
||||
);
|
||||
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(
|
||||
'<html><body><span>A</span><br/><span>B</span></body></html>'
|
||||
);
|
||||
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(
|
||||
'<div>hello</div><div>beautiful</div><div>world!</div>'
|
||||
);
|
||||
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(
|
||||
'<section>2</section><section>2</section><section>1</section><div>3</div>'
|
||||
);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
@ -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(`
|
||||
<form action='/rredirect' method='post'>
|
||||
<input type="hidden" id="foo" name="foo" value="FOOBAR">
|
||||
</form>
|
||||
`);
|
||||
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,<div>yo</div>';
|
||||
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,<div>yo</div>';
|
||||
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('<div>yo</div>');
|
||||
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,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>`
|
||||
);
|
||||
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('<iframe></iframe>');
|
||||
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();
|
||||
|
@ -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(`
|
||||
<form action='/rredirect' method='post'>
|
||||
<input type="hidden" id="foo" name="foo" value="FOOBAR">
|
||||
</form>
|
||||
`);
|
||||
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,<div>yo</div>';
|
||||
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,<div>yo</div>';
|
||||
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('<div>yo</div>');
|
||||
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,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>`
|
||||
);
|
||||
))!;
|
||||
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('<iframe></iframe>');
|
||||
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,
|
||||
|
@ -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 () {
|
||||
</style>
|
||||
<div></div>
|
||||
`);
|
||||
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 () {
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
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 () {
|
||||
<div class="above"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
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);"> </div>`);
|
||||
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('<h1>remove this</h1>');
|
||||
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('<div style="width: 50px; height: 0"></div>');
|
||||
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(
|
||||
'<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>'
|
||||
);
|
||||
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(
|
||||
'<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>'
|
||||
);
|
||||
const elementHandle = await page.$('div');
|
||||
const elementHandle = (await page.$('div'))!;
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png');
|
||||
});
|
||||
|
@ -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<Target>((fulfill) =>
|
||||
context.once('targetcreated', (target) => fulfill(target))
|
||||
);
|
||||
const createdTarget = new Promise<Target>((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<Target>((fulfill) =>
|
||||
context.once('targetchanged', (target) => fulfill(target))
|
||||
);
|
||||
let changedTarget = new Promise<Target>((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<Target>((fulfill) =>
|
||||
context.once('targetcreated', (target) => fulfill(target))
|
||||
);
|
||||
const targetPromise = new Promise<Target>((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<Target>((fulfill) =>
|
||||
context.once('targetcreated', (target) => fulfill(target))
|
||||
);
|
||||
const evaluatePromise = newPage.evaluate(() => window.open('about:blank'));
|
||||
const targetPromise2 = new Promise<Target>((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<Target>((fulfill) =>
|
||||
context.once('targetcreated', (target) => fulfill(target))
|
||||
),
|
||||
new Promise<Target>((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);
|
||||
});
|
||||
});
|
||||
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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<string>}
|
||||
*/
|
||||
dumpFrames: function (frame, indentation) {
|
||||
indentation = indentation || '';
|
||||
let description = frame.url().replace(/:\d{4}\//, ':<PORT>/');
|
||||
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<!Object>}
|
||||
*/
|
||||
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);
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
162
test/src/utils.ts
Normal file
162
test/src/utils.ts
Normal file
@ -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<Frame | undefined> => {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<string> => {
|
||||
indentation = indentation || '';
|
||||
let description = frame.url().replace(/:\d{4}\//, ':<PORT>/');
|
||||
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<any> => {
|
||||
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,
|
||||
};
|
@ -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(`<div>some text</div>`);
|
||||
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('<div></div>');
|
||||
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(`<div class='zombo'>anything</div>`),
|
||||
]);
|
||||
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 = '<h3><div></div></h3>')
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
return (document.querySelector('span')!.innerHTML =
|
||||
'<h3><div></div></h3>');
|
||||
});
|
||||
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(
|
||||
`<div style='display: none; visibility: hidden;'>1</div>`
|
||||
);
|
||||
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(
|
||||
`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`
|
||||
);
|
||||
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(`<div style='display: block;'></div>`);
|
||||
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(`<div style='display: block;'></div>`);
|
||||
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(`<div></div>`);
|
||||
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(`<div class='notZombo'></div>`);
|
||||
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(`<div class='zombo'>anything</div>`);
|
||||
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(`<div style='display: block;'></div>`);
|
||||
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(`<div class='zombo'>anything</div>`);
|
||||
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(`<div>some text</div>`);
|
||||
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(`<div>some text</div>`);
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
@ -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<WebWorker>((x) =>
|
||||
page.once('workercreated', x)
|
||||
);
|
||||
const workerObj = await page.evaluateHandle(
|
||||
() => new Worker('data:text/javascript,1')
|
||||
);
|
||||
const workerCreatedPromise = new Promise<WebWorker>((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<ConsoleMessage>((x) =>
|
||||
page.on('console', x)
|
||||
);
|
||||
await page.evaluate(
|
||||
() => new Worker(`data:text/javascript,console.log(1,2,3,this)`)
|
||||
);
|
||||
const logPromise = new Promise<ConsoleMessage>((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<WebWorker>((x) =>
|
||||
page.once('workercreated', x)
|
||||
);
|
||||
await page.evaluate(
|
||||
() => new Worker(`data:text/javascript,console.log(1)`)
|
||||
);
|
||||
const workerCreatedPromise = new Promise<WebWorker>((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<Error>((x) => page.on('pageerror', x));
|
||||
await page.evaluate(
|
||||
() =>
|
||||
new Worker(`data:text/javascript, throw new Error('this is my error');`)
|
||||
);
|
||||
const errorPromise = new Promise<Error>((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');
|
||||
});
|
||||
|
@ -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": [
|
||||
|
@ -43,10 +43,10 @@ interface Subscriber {
|
||||
type TestIncomingMessage = IncomingMessage & { postBody?: Promise<string> };
|
||||
|
||||
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;
|
||||
|
@ -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"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user