chore: migrate src/JSHandle to TS (#5703)

* chore: migrate src/JSHandle to TS

There's a few TODOs in here that all depend on typing the
`ExecutionContext.evaluateHandle` properly so that you can properly
declare what types you're expecting back. Once I've done that file (it's
next on my list) I will loop back and improve the types here, fixing
these TODOs.

* Fix doclint for {}
This commit is contained in:
Jack Franklin 2020-04-21 12:11:06 +01:00 committed by GitHub
parent 42893d8755
commit 8d5d76ed70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 207 deletions

View File

@ -17,6 +17,9 @@
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {ElementHandle} = require('./JSHandle');
/**
* @typedef {Object} SerializedAXNode
@ -64,7 +67,7 @@ class Accessibility {
}
/**
* @param {{interestingOnly?: boolean, root?: ?Puppeteer.ElementHandle}=} options
* @param {{interestingOnly?: boolean, root?: ?ElementHandle}=} options
* @return {!Promise<!SerializedAXNode>}
*/
async snapshot(options = {}) {

View File

@ -18,6 +18,9 @@ const fs = require('fs');
const {helper, assert} = require('./helper');
const {LifecycleWatcher} = require('./LifecycleWatcher');
const {TimeoutError} = require('./Errors');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {JSHandle, ElementHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
@ -39,7 +42,7 @@ class DOMWorld {
this._frame = frame;
this._timeoutSettings = timeoutSettings;
/** @type {?Promise<!Puppeteer.ElementHandle>} */
/** @type {?Promise<!ElementHandle>} */
this._documentPromise = null;
/** @type {!Promise<!Puppeteer.ExecutionContext>} */
this._contextPromise;
@ -100,7 +103,7 @@ class DOMWorld {
/**
* @param {Function|string} pageFunction
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
async evaluateHandle(pageFunction, ...args) {
const context = await this.executionContext();
@ -119,7 +122,7 @@ class DOMWorld {
/**
* @param {string} selector
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async $(selector) {
const document = await this._document();
@ -128,7 +131,7 @@ class DOMWorld {
}
/**
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async _document() {
if (this._documentPromise)
@ -142,7 +145,7 @@ class DOMWorld {
/**
* @param {string} expression
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $x(expression) {
const document = await this._document();
@ -175,7 +178,7 @@ class DOMWorld {
/**
* @param {string} selector
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
const document = await this._document();
@ -225,7 +228,7 @@ class DOMWorld {
/**
* @param {!{url?: string, path?: string, content?: string, type?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addScriptTag(options) {
const {
@ -296,7 +299,7 @@ class DOMWorld {
/**
* @param {!{url?: string, path?: string, content?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addStyleTag(options) {
const {
@ -431,7 +434,7 @@ class DOMWorld {
/**
* @param {string} selector
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
waitForSelector(selector, options) {
return this._waitForSelectorOrXPath(selector, false, options);
@ -440,7 +443,7 @@ class DOMWorld {
/**
* @param {string} xpath
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
waitForXPath(xpath, options) {
return this._waitForSelectorOrXPath(xpath, true, options);
@ -449,7 +452,7 @@ class DOMWorld {
/**
* @param {Function|string} pageFunction
* @param {!{polling?: string|number, timeout?: number}=} options
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
waitForFunction(pageFunction, options = {}, ...args) {
const {
@ -470,7 +473,7 @@ class DOMWorld {
* @param {string} selectorOrXPath
* @param {boolean} isXPath
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async _waitForSelectorOrXPath(selectorOrXPath, isXPath, options = {}) {
const {
@ -568,7 +571,7 @@ class WaitTask {
async rerun() {
const runCount = ++this._runCount;
/** @type {?Puppeteer.JSHandle} */
/** @type {?JSHandle} */
let success = null;
let error = null;
try {

View File

@ -15,10 +15,12 @@
*/
const {helper, assert} = require('./helper');
const {createJSHandle, JSHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {createJSHandle, JSHandle, ElementHandle} = require('./JSHandle');
const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
@ -187,19 +189,19 @@ class ExecutionContext {
/**
* @param {Protocol.DOM.BackendNodeId} backendNodeId
* @return {Promise<Puppeteer.ElementHandle>}
* @return {Promise<ElementHandle>}
*/
async _adoptBackendNodeId(backendNodeId) {
const {object} = await this._client.send('DOM.resolveNode', {
backendNodeId: backendNodeId,
executionContextId: this._contextId,
});
return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object));
return /** @type {ElementHandle}*/(createJSHandle(this, object));
}
/**
* @param {Puppeteer.ElementHandle} elementHandle
* @return {Promise<Puppeteer.ElementHandle>}
* @param {ElementHandle} elementHandle
* @return {Promise<ElementHandle>}
*/
async _adoptElementHandle(elementHandle) {
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');

View File

@ -27,6 +27,9 @@ const {TimeoutSettings} = require('./TimeoutSettings');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {JSHandle, ElementHandle} = require('./JSHandle');
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
@ -432,7 +435,7 @@ class Frame {
/**
* @param {Function|string} pageFunction
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
async evaluateHandle(pageFunction, ...args) {
return this._mainWorld.evaluateHandle(pageFunction, ...args);
@ -449,7 +452,7 @@ class Frame {
/**
* @param {string} selector
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async $(selector) {
return this._mainWorld.$(selector);
@ -457,7 +460,7 @@ class Frame {
/**
* @param {string} expression
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $x(expression) {
return this._mainWorld.$x(expression);
@ -485,7 +488,7 @@ class Frame {
/**
* @param {string} selector
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
return this._mainWorld.$$(selector);
@ -543,7 +546,7 @@ class Frame {
/**
* @param {!{url?: string, path?: string, content?: string, type?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addScriptTag(options) {
return this._mainWorld.addScriptTag(options);
@ -551,7 +554,7 @@ class Frame {
/**
* @param {!{url?: string, path?: string, content?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addStyleTag(options) {
return this._mainWorld.addStyleTag(options);
@ -608,7 +611,7 @@ class Frame {
* @param {(string|number|Function)} selectorOrFunctionOrTimeout
* @param {!Object=} options
* @param {!Array<*>} args
* @return {!Promise<?Puppeteer.JSHandle>}
* @return {!Promise<?JSHandle>}
*/
waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
const xPathPattern = '//';
@ -629,7 +632,7 @@ class Frame {
/**
* @param {string} selector
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async waitForSelector(selector, options) {
const handle = await this._secondaryWorld.waitForSelector(selector, options);
@ -644,7 +647,7 @@ class Frame {
/**
* @param {string} xpath
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async waitForXPath(xpath, options) {
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
@ -659,7 +662,7 @@ class Frame {
/**
* @param {Function|string} pageFunction
* @param {!{polling?: string|number, timeout?: number}=} options
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
waitForFunction(pageFunction, options = {}, ...args) {
return this._mainWorld.waitForFunction(pageFunction, options, ...args);

View File

@ -14,12 +14,19 @@
* limitations under the License.
*/
const {helper, assert, debugError} = require('./helper');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
import {helper, assert, debugError} from './helper';
import {CDPSession} from './Connection';
function createJSHandle(context, remoteObject) {
interface BoxModel {
content: Array<{x: number; y: number}>;
padding: Array<{x: number; y: number}>;
border: Array<{x: number; y: number}>;
margin: Array<{x: number; y: number}>;
width: number;
height: number;
}
export function createJSHandle(context: Puppeteer.ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject): JSHandle {
const frame = context.frame();
if (remoteObject.subtype === 'node' && frame) {
const frameManager = frame._frameManager;
@ -28,49 +35,31 @@ function createJSHandle(context, remoteObject) {
return new JSHandle(context, context._client, remoteObject);
}
class JSHandle {
/**
* @param {!Puppeteer.ExecutionContext} context
* @param {!CDPSession} client
* @param {!Protocol.Runtime.RemoteObject} remoteObject
*/
constructor(context, client, remoteObject) {
export class JSHandle {
_context: Puppeteer.ExecutionContext;
_client: CDPSession;
_remoteObject: Protocol.Runtime.RemoteObject;
_disposed = false;
constructor(context: Puppeteer.ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject) {
this._context = context;
this._client = client;
this._remoteObject = remoteObject;
this._disposed = false;
}
/**
* @return {!Puppeteer.ExecutionContext}
*/
executionContext() {
executionContext(): Puppeteer.ExecutionContext {
return this._context;
}
/**
* @param {Function|String} pageFunction
* @param {!Array<*>} args
* @return {!Promise<(!Object|undefined)>}
*/
async evaluate(pageFunction, ...args) {
async evaluate<T extends any>(pageFunction: Function | string, ...args: any[]): Promise<T | undefined> {
return await this.executionContext().evaluate(pageFunction, this, ...args);
}
/**
* @param {Function|string} pageFunction
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
*/
async evaluateHandle(pageFunction, ...args) {
async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise<JSHandle> {
return await this.executionContext().evaluateHandle(pageFunction, this, ...args);
}
/**
* @param {string} propertyName
* @return {!Promise<?JSHandle>}
*/
async getProperty(propertyName) {
async getProperty(propertyName: string): Promise<JSHandle | undefined> {
const objectHandle = await this.evaluateHandle((object, propertyName) => {
const result = {__proto__: null};
result[propertyName] = object[propertyName];
@ -82,15 +71,12 @@ class JSHandle {
return result;
}
/**
* @return {!Promise<!Map<string, !JSHandle>>}
*/
async getProperties() {
async getProperties(): Promise<Map<string, JSHandle>> {
const response = await this._client.send('Runtime.getProperties', {
objectId: this._remoteObject.objectId,
ownProperties: true
});
const result = new Map();
const result = new Map<string, JSHandle>();
for (const property of response.result) {
if (!property.enumerable)
continue;
@ -99,10 +85,7 @@ class JSHandle {
return result;
}
/**
* @return {!Promise<?Object>}
*/
async jsonValue() {
async jsonValue(): Promise<{}> {
if (this._remoteObject.objectId) {
const response = await this._client.send('Runtime.callFunctionOn', {
functionDeclaration: 'function() { return this; }',
@ -115,25 +98,19 @@ class JSHandle {
return helper.valueFromRemoteObject(this._remoteObject);
}
/**
* @return {?Puppeteer.ElementHandle}
*/
asElement() {
/* This always returns null but children can define this and return an ElementHandle */
asElement(): ElementHandle | null {
return null;
}
async dispose() {
async dispose(): Promise<void> {
if (this._disposed)
return;
this._disposed = true;
await helper.releaseObject(this._client, this._remoteObject);
}
/**
* @override
* @return {string}
*/
toString() {
toString(): string {
if (this._remoteObject.objectId) {
const type = this._remoteObject.subtype || this._remoteObject.type;
return 'JSHandle@' + type;
@ -142,7 +119,9 @@ class JSHandle {
}
}
class ElementHandle extends JSHandle {
export class ElementHandle extends JSHandle {
_page: Puppeteer.Page;
_frameManager: Puppeteer.FrameManager;
/**
* @param {!Puppeteer.ExecutionContext} context
* @param {!CDPSession} client
@ -150,27 +129,19 @@ class ElementHandle extends JSHandle {
* @param {!Puppeteer.Page} page
* @param {!Puppeteer.FrameManager} frameManager
*/
constructor(context, client, remoteObject, page, frameManager) {
constructor(context: Puppeteer.ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject, page: Puppeteer.Page, frameManager: Puppeteer.FrameManager) {
super(context, client, remoteObject);
this._client = client;
this._remoteObject = remoteObject;
this._page = page;
this._frameManager = frameManager;
this._disposed = false;
}
/**
* @override
* @return {?ElementHandle}
*/
asElement() {
asElement(): ElementHandle | null {
return this;
}
/**
* @return {!Promise<?Puppeteer.Frame>}
*/
async contentFrame() {
async contentFrame(): Promise<Puppeteer.Frame | null> {
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: this._remoteObject.objectId
});
@ -179,8 +150,8 @@ class ElementHandle extends JSHandle {
return this._frameManager.frame(nodeInfo.node.frameId);
}
async _scrollIntoViewIfNeeded() {
const error = await this.evaluate(async(element, pageJavascriptEnabled) => {
async _scrollIntoViewIfNeeded(): Promise<void> {
const error = await this.evaluate<string | false>(async(element, pageJavascriptEnabled) => {
if (!element.isConnected)
return 'Node is detached from document';
if (element.nodeType !== Node.ELEMENT_NODE)
@ -201,14 +172,12 @@ class ElementHandle extends JSHandle {
element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
return false;
}, this._page._javascriptEnabled);
if (error)
throw new Error(error);
}
/**
* @return {!Promise<!{x: number, y: number}>}
*/
async _clickablePoint() {
async _clickablePoint(): Promise<{x: number; y: number}> {
const [result, layoutMetrics] = await Promise.all([
this._client.send('DOM.getContentQuads', {
objectId: this._remoteObject.objectId
@ -236,20 +205,13 @@ class ElementHandle extends JSHandle {
};
}
/**
* @return {!Promise<void|Protocol.DOM.getBoxModelReturnValue>}
*/
_getBoxModel() {
_getBoxModel(): Promise<void | Protocol.DOM.getBoxModelReturnValue> {
return this._client.send('DOM.getBoxModel', {
objectId: this._remoteObject.objectId
}).catch(error => debugError(error));
}
/**
* @param {!Array<number>} quad
* @return {!Array<{x: number, y: number}>}
*/
_fromProtocolQuad(quad) {
_fromProtocolQuad(quad: number[]): Array<{x: number; y: number}> {
return [
{x: quad[0], y: quad[1]},
{x: quad[2], y: quad[3]},
@ -264,23 +226,20 @@ class ElementHandle extends JSHandle {
* @param {number} height
* @return {!Array<{x: number, y: number}>}
*/
_intersectQuadWithViewport(quad, width, height) {
_intersectQuadWithViewport(quad: Array<{x: number; y: number}>, width: number, height: number): Array<{x: number; y: number}> {
return quad.map(point => ({
x: Math.min(Math.max(point.x, 0), width),
y: Math.min(Math.max(point.y, 0), height),
}));
}
async hover() {
async hover(): Promise<void> {
await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint();
await this._page.mouse.move(x, y);
}
/**
* @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
*/
async click(options) {
async click(options: {delay?: number; button?: 'left'|'right'|'middle'; clickCount?: number}): Promise<void> {
await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint();
await this._page.mouse.click(x, y, options);
@ -290,10 +249,15 @@ class ElementHandle extends JSHandle {
* @param {!Array<string>} values
* @return {!Promise<!Array<string>>}
*/
async select(...values) {
async select(...values: string[]): Promise<string[]> {
for (const value of values)
assert(helper.isString(value), 'Values must be strings. Found value "' + value + '" of type "' + (typeof value) + '"');
return this.evaluate((element, values) => {
/* TODO(jacktfranklin@): once ExecutionContext is TypeScript, and
* its evaluate function is properly typed with generics we can
* return here and remove the typecasting
*/
return this.evaluate((element: HTMLSelectElement, values: string[]) => {
if (element.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');
@ -310,15 +274,13 @@ class ElementHandle extends JSHandle {
}, values);
}
/**
* @param {!Array<string>} filePaths
*/
async uploadFile(...filePaths) {
async uploadFile(...filePaths: string[]): Promise<void> {
const isMultiple = await this.evaluate(element => element.multiple);
assert(filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with <input type=file multiple>');
// This import is only needed for `uploadFile`, so keep it scoped here to avoid paying
// the cost unnecessarily.
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
const files = filePaths.map(filePath => path.resolve(filePath));
const {objectId} = this._remoteObject;
@ -341,38 +303,27 @@ class ElementHandle extends JSHandle {
}
}
async tap() {
async tap(): Promise<void> {
await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint();
await this._page.touchscreen.tap(x, y);
}
async focus() {
async focus(): Promise<void> {
await this.evaluate(element => element.focus());
}
/**
* @param {string} text
* @param {{delay: (number|undefined)}=} options
*/
async type(text, options) {
async type(text: string, options?: {delay: number}): Promise<void> {
await this.focus();
await this._page.keyboard.type(text, options);
}
/**
* @param {string} key
* @param {!{delay?: number, text?: string}=} options
*/
async press(key, options) {
async press(key: string, options?: {delay?: number; text?: string}): Promise<void> {
await this.focus();
await this._page.keyboard.press(key, options);
}
/**
* @return {!Promise<?{x: number, y: number, width: number, height: number}>}
*/
async boundingBox() {
async boundingBox(): Promise<{x: number; y: number; width: number; height: number}> {
const result = await this._getBoxModel();
if (!result)
@ -390,7 +341,7 @@ class ElementHandle extends JSHandle {
/**
* @return {!Promise<?BoxModel>}
*/
async boxModel() {
async boxModel(): Promise<BoxModel | null> {
const result = await this._getBoxModel();
if (!result)
@ -407,12 +358,7 @@ class ElementHandle extends JSHandle {
};
}
/**
*
* @param {!Object=} options
* @returns {!Promise<string|!Buffer>}
*/
async screenshot(options = {}) {
async screenshot(options = {}): Promise<string|Buffer> {
let needsViewportReset = false;
let boundingBox = await this.boundingBox();
@ -453,11 +399,7 @@ class ElementHandle extends JSHandle {
return imageData;
}
/**
* @param {string} selector
* @return {!Promise<?ElementHandle>}
*/
async $(selector) {
async $(selector: string): Promise<ElementHandle | null> {
const handle = await this.evaluateHandle(
(element, selector) => element.querySelector(selector),
selector
@ -473,7 +415,7 @@ class ElementHandle extends JSHandle {
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
async $$(selector: string): Promise<ElementHandle[]> {
const arrayHandle = await this.evaluateHandle(
(element, selector) => element.querySelectorAll(selector),
selector
@ -489,13 +431,7 @@ class ElementHandle extends JSHandle {
return result;
}
/**
* @param {string} selector
* @param {Function|String} pageFunction
* @param {!Array<*>} args
* @return {!Promise<(!Object|undefined)>}
*/
async $eval(selector, pageFunction, ...args) {
async $eval<T extends any>(selector: string, pageFunction: Function|string, ...args: any[]): Promise<T | undefined> {
const elementHandle = await this.$(selector);
if (!elementHandle)
throw new Error(`Error: failed to find element matching selector "${selector}"`);
@ -504,13 +440,10 @@ class ElementHandle extends JSHandle {
return result;
}
/**
* @param {string} selector
* @param {Function|String} pageFunction
* @param {!Array<*>} args
* @return {!Promise<(!Object|undefined)>}
*/
async $$eval(selector, pageFunction, ...args) {
// TODO(jacktfranklin@): consider the types here
// we might want $$eval<SelectorType> which returns Promise<SelectorType[]>?
// Once ExecutionContext.evaluate is properly typed we can improve this a bunch
async $$eval<T>(selector: string, pageFunction: Function | string, ...args: any[]): Promise<T | undefined> {
const arrayHandle = await this.evaluateHandle(
(element, selector) => Array.from(element.querySelectorAll(selector)),
selector
@ -521,11 +454,7 @@ class ElementHandle extends JSHandle {
return result;
}
/**
* @param {string} expression
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $x(expression) {
async $x(expression: string): Promise<ElementHandle[]> {
const arrayHandle = await this.evaluateHandle(
(element, expression) => {
const document = element.ownerDocument || element;
@ -549,10 +478,7 @@ class ElementHandle extends JSHandle {
return result;
}
/**
* @returns {!Promise<boolean>}
*/
isIntersectingViewport() {
isIntersectingViewport(): Promise<boolean> {
return this.evaluate(async element => {
const visibleRatio = await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
@ -566,7 +492,7 @@ class ElementHandle extends JSHandle {
}
}
function computeQuadArea(quad) {
function computeQuadArea(quad: Array<{x: number; y: number}>): number {
// Compute sum of all directed areas of adjacent triangles
// https://en.wikipedia.org/wiki/Polygon#Simple_polygons
let area = 0;
@ -577,15 +503,3 @@ function computeQuadArea(quad) {
}
return Math.abs(area);
}
/**
* @typedef {Object} BoxModel
* @property {!Array<!{x: number, y: number}>} content
* @property {!Array<!{x: number, y: number}>} padding
* @property {!Array<!{x: number, y: number}>} border
* @property {!Array<!{x: number, y: number}>} margin
* @property {number} width
* @property {number} height
*/
module.exports = {createJSHandle, JSHandle, ElementHandle};

View File

@ -29,7 +29,9 @@ const Tracing = require('./Tracing');
const {helper, debugError, assert} = require('./helper');
const {Coverage} = require('./Coverage');
const {Worker: PuppeteerWorker} = require('./Worker');
const {createJSHandle} = require('./JSHandle');
// Import used as typedef
// eslint-disable-next-line no-unused-vars
const {createJSHandle, JSHandle, ElementHandle} = require('./JSHandle');
const {Accessibility} = require('./Accessibility');
const {TimeoutSettings} = require('./TimeoutSettings');
@ -316,7 +318,7 @@ class Page extends EventEmitter {
/**
* @param {string} selector
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
async $(selector) {
return this.mainFrame().$(selector);
@ -325,7 +327,7 @@ class Page extends EventEmitter {
/**
* @param {Function|string} pageFunction
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
async evaluateHandle(pageFunction, ...args) {
const context = await this.mainFrame().executionContext();
@ -333,8 +335,8 @@ class Page extends EventEmitter {
}
/**
* @param {!Puppeteer.JSHandle} prototypeHandle
* @return {!Promise<!Puppeteer.JSHandle>}
* @param {!JSHandle} prototypeHandle
* @return {!Promise<!JSHandle>}
*/
async queryObjects(prototypeHandle) {
const context = await this.mainFrame().executionContext();
@ -363,7 +365,7 @@ class Page extends EventEmitter {
/**
* @param {string} selector
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
return this.mainFrame().$$(selector);
@ -371,7 +373,7 @@ class Page extends EventEmitter {
/**
* @param {string} expression
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $x(expression) {
return this.mainFrame().$x(expression);
@ -429,7 +431,7 @@ class Page extends EventEmitter {
/**
* @param {!{url?: string, path?: string, content?: string, type?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addScriptTag(options) {
return this.mainFrame().addScriptTag(options);
@ -437,7 +439,7 @@ class Page extends EventEmitter {
/**
* @param {!{url?: string, path?: string, content?: string}} options
* @return {!Promise<!Puppeteer.ElementHandle>}
* @return {!Promise<!ElementHandle>}
*/
async addStyleTag(options) {
return this.mainFrame().addStyleTag(options);
@ -617,7 +619,7 @@ class Page extends EventEmitter {
/**
* @param {string} type
* @param {!Array<!Puppeteer.JSHandle>} args
* @param {!Array<!JSHandle>} args
* @param {Protocol.Runtime.StackTrace=} stackTrace
*/
_addConsoleMessage(type, args, stackTrace) {
@ -1126,7 +1128,7 @@ class Page extends EventEmitter {
* @param {(string|number|Function)} selectorOrFunctionOrTimeout
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number, polling?: string|number}=} options
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
@ -1135,7 +1137,7 @@ class Page extends EventEmitter {
/**
* @param {string} selector
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
waitForSelector(selector, options = {}) {
return this.mainFrame().waitForSelector(selector, options);
@ -1144,7 +1146,7 @@ class Page extends EventEmitter {
/**
* @param {string} xpath
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
* @return {!Promise<?ElementHandle>}
*/
waitForXPath(xpath, options = {}) {
return this.mainFrame().waitForXPath(xpath, options);
@ -1154,7 +1156,7 @@ class Page extends EventEmitter {
* @param {Function} pageFunction
* @param {!{polling?: string|number, timeout?: number}=} options
* @param {!Array<*>} args
* @return {!Promise<!Puppeteer.JSHandle>}
* @return {!Promise<!JSHandle>}
*/
waitForFunction(pageFunction, options = {}, ...args) {
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
@ -1322,7 +1324,7 @@ class ConsoleMessage {
/**
* @param {string} type
* @param {string} text
* @param {!Array<!Puppeteer.JSHandle>} args
* @param {!Array<!JSHandle>} args
* @param {ConsoleMessage.Location} location
*/
constructor(type, text, args, location = {}) {
@ -1347,7 +1349,7 @@ class ConsoleMessage {
}
/**
* @return {!Array<!Puppeteer.JSHandle>}
* @return {!Array<!JSHandle>}
*/
args() {
return this._args;
@ -1364,7 +1366,7 @@ class ConsoleMessage {
class FileChooser {
/**
* @param {CDPSession} client
* @param {Puppeteer.ElementHandle} element
* @param {ElementHandle} element
* @param {!Protocol.Page.fileChooserOpenedPayload} event
*/
constructor(client, element, event) {

3
src/externs.d.ts vendored
View File

@ -3,7 +3,6 @@ import {Target as RealTarget} from './Target.js';
import {Page as RealPage} from './Page.js';
import {Mouse as RealMouse, Keyboard as RealKeyboard, Touchscreen as RealTouchscreen} from './Input.js';
import {Frame as RealFrame, FrameManager as RealFrameManager} from './FrameManager.js';
import {JSHandle as RealJSHandle, ElementHandle as RealElementHandle} from './JSHandle.js';
import {DOMWorld as RealDOMWorld} from './DOMWorld.js';
import {ExecutionContext as RealExecutionContext} from './ExecutionContext.js';
import { NetworkManager as RealNetworkManager, Request as RealRequest, Response as RealResponse } from './NetworkManager.js';
@ -19,8 +18,6 @@ declare global {
export class Frame extends RealFrame {}
export class FrameManager extends RealFrameManager {}
export class NetworkManager extends RealNetworkManager {}
export class ElementHandle extends RealElementHandle {}
export class JSHandle extends RealJSHandle {}
export class DOMWorld extends RealDOMWorld {}
export class ExecutionContext extends RealExecutionContext {}
export class Page extends RealPage { }

View File

@ -152,7 +152,7 @@ function checkSources(sources) {
*/
function serializeType(type, circular = []) {
let typeName = checker.typeToString(type);
if (typeName === 'any' || typeName === '{ [x: string]: string; }')
if (typeName === 'any' || typeName === '{ [x: string]: string; }' || typeName === '{}')
typeName = 'Object';
const nextCircular = [typeName].concat(circular);