Skip to content

Commit

Permalink
chore(types): type check the rest of the protocol (#2328)
Browse files Browse the repository at this point in the history
This adds events and commands into generated `protocol.d.ts`.
  • Loading branch information
JoelEinbinder authored and aslushnikov committed Apr 8, 2018
1 parent 45d97e5 commit 294f33b
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 37 deletions.
4 changes: 2 additions & 2 deletions lib/Coverage.js
Expand Up @@ -106,7 +106,7 @@ class JSCoverage {
}

/**
* @param {!Object} event
* @param {!Protocol.Debugger.scriptParsedPayload} event
*/
async _onScriptParsed(event) {
// Ignore anonymous scripts
Expand Down Expand Up @@ -193,7 +193,7 @@ class CSSCoverage {
}

/**
* @param {!Object} event
* @param {!Protocol.CSS.styleSheetAddedPayload} event
*/
async _onStyleSheet(event) {
const header = event.header;
Expand Down
2 changes: 1 addition & 1 deletion lib/ElementHandle.js
Expand Up @@ -80,7 +80,7 @@ class ElementHandle extends JSHandle {
}

/**
* @return {!Promise<?{model: object}>}
* @return {!Promise<void|Protocol.DOM.getBoxModelReturnValue>}
*/
_getBoxModel() {
return this._client.send('DOM.getBoxModel', {
Expand Down
4 changes: 2 additions & 2 deletions lib/EmulationManager.js
Expand Up @@ -33,13 +33,13 @@ class EmulationManager {
const width = viewport.width;
const height = viewport.height;
const deviceScaleFactor = viewport.deviceScaleFactor || 1;
/** @type {Protocol.Emulation.ScreenOrientation} */
const screenOrientation = viewport.isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' };

await Promise.all([
this._client.send('Emulation.setDeviceMetricsOverride', { mobile, width, height, deviceScaleFactor, screenOrientation }),
this._client.send('Emulation.setTouchEmulationEnabled', {
enabled: viewport.hasTouch || false,
configuration: viewport.isMobile ? 'mobile' : 'desktop'
enabled: viewport.hasTouch || false
})
]);

Expand Down
2 changes: 1 addition & 1 deletion lib/ExecutionContext.js
Expand Up @@ -61,7 +61,7 @@ class ExecutionContext {
async evaluateHandle(pageFunction, ...args) {
if (helper.isString(pageFunction)) {
const contextId = this._contextId;
const expression = pageFunction;
const expression = /** @type {string} */ (pageFunction);
const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise: true});
if (exceptionDetails)
throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
Expand Down
12 changes: 6 additions & 6 deletions lib/FrameManager.js
Expand Up @@ -25,7 +25,7 @@ const readFileAsync = helper.promisify(fs.readFile);
class FrameManager extends EventEmitter {
/**
* @param {!Puppeteer.CDPSession} client
* @param {{frame: Object, childFrames: ?Array}} frameTree
* @param {!Protocol.Page.FrameTree} frameTree
* @param {!Puppeteer.Page} page
*/
constructor(client, frameTree, page) {
Expand All @@ -34,7 +34,7 @@ class FrameManager extends EventEmitter {
this._page = page;
/** @type {!Map<string, !Frame>} */
this._frames = new Map();
/** @type {!Map<string, !ExecutionContext>} */
/** @type {!Map<number, !ExecutionContext>} */
this._contextIdToContext = new Map();

this._client.on('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId));
Expand All @@ -49,7 +49,7 @@ class FrameManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Page.lifecycleEventPayload} event
*/
_onLifecycleEvent(event) {
const frame = this._frames.get(event.frameId);
Expand All @@ -60,7 +60,7 @@ class FrameManager extends EventEmitter {
}

/**
* @param {{frame: Object, childFrames: ?Array}} frameTree
* @param {!Protocol.Page.FrameTree} frameTree
*/
_handleFrameTree(frameTree) {
if (frameTree.frame.parentId)
Expand Down Expand Up @@ -171,7 +171,7 @@ class FrameManager extends EventEmitter {
}

/**
* @param {string} executionContextId
* @param {number} executionContextId
*/
_onExecutionContextDestroyed(executionContextId) {
const context = this._contextIdToContext.get(executionContextId);
Expand All @@ -188,7 +188,7 @@ class FrameManager extends EventEmitter {
}

/**
* @param {string} contextId
* @param {number} contextId
* @param {*} remoteObject
* @return {!JSHandle}
*/
Expand Down
13 changes: 7 additions & 6 deletions lib/NetworkManager.js
Expand Up @@ -126,10 +126,11 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.requestInterceptedPayload} event
*/
_onRequestIntercepted(event) {
if (event.authChallenge) {
/** @type {"Default"|"CancelAuth"|"ProvideCredentials"} */
let response = 'Default';
if (this._attemptedAuthentications.has(event.interceptionId)) {
response = 'CancelAuth';
Expand Down Expand Up @@ -170,7 +171,7 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.requestServedFromCachePayload} event
*/
_onRequestServedFromCache(event) {
const request = this._requestIdToRequest.get(event.requestId);
Expand Down Expand Up @@ -219,7 +220,7 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.requestWillBeSentPayload} event
*/
_onRequestWillBeSent(event) {
if (this._protocolRequestInterceptionEnabled) {
Expand Down Expand Up @@ -251,7 +252,7 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.responseReceivedPayload} event
*/
_onResponseReceived(event) {
const request = this._requestIdToRequest.get(event.requestId);
Expand All @@ -265,7 +266,7 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.loadingFinishedPayload} event
*/
_onLoadingFinished(event) {
const request = this._requestIdToRequest.get(event.requestId);
Expand All @@ -281,7 +282,7 @@ class NetworkManager extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Network.loadingFailedPayload} event
*/
_onLoadingFailed(event) {
const request = this._requestIdToRequest.get(event.requestId);
Expand Down
11 changes: 6 additions & 5 deletions lib/Page.js
Expand Up @@ -63,7 +63,7 @@ class Page extends EventEmitter {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!Puppeteer.Target} target
* @param {{frame: Object, childFrames: ?Array}} frameTree
* @param {!Protocol.Page.FrameTree} frameTree
* @param {boolean} ignoreHTTPSErrors
* @param {!Puppeteer.TaskQueue} screenshotTaskQueue
*/
Expand Down Expand Up @@ -182,7 +182,7 @@ class Page extends EventEmitter {
}

/**
* @param {!Object} event
* @param {!Protocol.Security.certificateErrorPayload} event
*/
_onCertificateError(event) {
if (!this._ignoreHTTPSErrors)
Expand Down Expand Up @@ -404,7 +404,7 @@ class Page extends EventEmitter {
}

/**
* @param {!Object} exceptionDetails
* @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails
*/
_handleException(exceptionDetails) {
const message = helper.getExceptionMessage(exceptionDetails);
Expand Down Expand Up @@ -708,7 +708,7 @@ class Page extends EventEmitter {
}

/**
* @param {string} format
* @param {"png"|"jpeg"} format
* @param {!Object=} options
* @return {!Promise<!Buffer>}
*/
Expand All @@ -728,6 +728,7 @@ class Page extends EventEmitter {
const mobile = this._viewport.isMobile || false;
const deviceScaleFactor = this._viewport.deviceScaleFactor || 1;
const landscape = this._viewport.isLandscape || false;
/** @type {!Protocol.Emulation.ScreenOrientation} */
const screenOrientation = landscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' };
await this._client.send('Emulation.setDeviceMetricsOverride', { mobile, width, height, deviceScaleFactor, screenOrientation });
}
Expand Down Expand Up @@ -1024,7 +1025,7 @@ Page.Events = {
/**
* @typedef {Object} Network.CookieParam
* @property {string} name
* @property {string=} value
* @property {string} value
* @property {string=} url
* @property {string=} domain
* @property {string=} path
Expand Down
5 changes: 4 additions & 1 deletion lib/externs.d.ts
Expand Up @@ -12,7 +12,10 @@ import * as child_process from 'child_process';
declare global {
module Puppeteer {
export class Connection extends RealConnection {}
export class CDPSession extends RealCDPSession {}
export class CDPSession extends RealCDPSession {
on<T extends keyof Protocol.Events>(event: T, listener: (arg: Protocol.Events[T]) => void): this;
send<T extends keyof Protocol.CommandParameters>(message: T, parameters?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T]>;
}
export class Mouse extends RealMouse {}
export class Keyboard extends RealKeyboard {}
export class Touchscreen extends RealTouchscreen {}
Expand Down
72 changes: 59 additions & 13 deletions utils/protocol-types-generator/index.js
@@ -1,3 +1,4 @@
// @ts-check
const puppeteer = require('../..');
puppeteer.launch({
pipe: false,
Expand All @@ -11,28 +12,59 @@ puppeteer.launch({
await browser.close();
const output = `// This is generated from /utils/protocol-types-generator/index.js
declare global {
module Protocol {
${json.domains.map(domain => `${domain.description ? `
module Protocol {${json.domains.map(domain => `${domain.description ? `
/**
* ${domain.description}
*/` : ''}
export module ${domain.domain} {
${(domain.types || []).map(type => `${type.description ? `
export module ${domain.domain} {${(domain.types || []).map(type => `${type.description ? `
/**
* ${type.description}
*/` : ''}${type.properties ? `
export interface ${type.id} {
${(type.properties || []).map(property => `${property.description ? `
export interface ${type.id} {${(type.properties || []).map(property => `${property.description ? `
/**
* ${property.description}
*/` : ''}
${property.name}${property.optional ? '?' : ''}: ${typeOfProperty(property)};
`).join(``)}
${property.name}${property.optional ? '?' : ''}: ${typeOfProperty(property)};`).join(``)}
}` : `
export type ${type.id} = ${typeOfProperty(type)};`}
`).join('')}
export type ${type.id} = ${typeOfProperty(type)};`}`).join('')}
${(domain.events || []).map(event => `${event.description ? `
/**
* ${event.description}
*/` : ''}${event.parameters ? `
export type ${event.name}Payload = {${event.parameters.map(parameter => `${parameter.description ? `
/**
* ${parameter.description}
*/` : ''}
${parameter.name}${parameter.optional ? '?' : ''}: ${typeOfProperty(parameter)};`).join(``)}
}` : `
export type ${event.name}Payload = void;`}`).join('')}
${(domain.commands || []).map(command => `${command.description ? `
/**
* ${command.description}
*/` : ''}
export type ${command.name}Parameters = {${(command.parameters || []).map(parameter => `${parameter.description ? `
/**
* ${parameter.description}
*/` : ''}
${parameter.name}${parameter.optional ? '?' : ''}: ${typeOfProperty(parameter)};`).join(``)}
}
export type ${command.name}ReturnValue = {${(command.returns || []).map(retVal => `${retVal.description ? `
/**
* ${retVal.description}
*/` : ''}
${retVal.name}${retVal.optional ? '?' : ''}: ${typeOfProperty(retVal)};`).join(``)}
}`).join('')}
}
`).join('')}
export interface Events {${json.domains.map(domain => (domain.events || []).map(event => `
"${domain.domain}.${event.name}": ${domain.domain}.${event.name}Payload;`).join('')).join('')}
}
export interface CommandParameters {${json.domains.map(domain => (domain.commands || []).map(command => `
"${domain.domain}.${command.name}": ${domain.domain}.${command.name}Parameters;`).join('')).join('')}
}
export interface CommandReturnValues {${json.domains.map(domain => (domain.commands || []).map(command => `
"${domain.domain}.${command.name}": ${domain.domain}.${command.name}ReturnValue;`).join('')).join('')}
}
}
}
// empty export to keep file a module
Expand All @@ -41,12 +73,26 @@ export {}
require('fs').writeFileSync(require('path').join(__dirname, '..', '..', 'lib', 'protocol.d.ts'), output);
});

function typeOfProperty(property) {
if (property.$ref) return property.$ref;

/**
* @typedef {Object} Property
* @property {string=} $ref
* @property {!Array=} enum
* @property {string=} type
* @property {!Property=} items
* @property {string=} description
*/

/**
* @param {!Property} property
* @param {string=} domain
*/
function typeOfProperty(property, domain) {
if (property.$ref) return property.$ref.includes('.') || !domain ? property.$ref : domain + '.' + property.$ref;
if (property.enum) return property.enum.map(value => JSON.stringify(value)).join('|');
switch (property.type) {
case 'array':
return typeOfProperty(property.items) + '[]';
return typeOfProperty(property.items, domain) + '[]';
case 'integer':
return 'number';
}
Expand Down

0 comments on commit 294f33b

Please sign in to comment.