Skip to content

Commit

Permalink
chore: introduce //lib/api.js (#3835)
Browse files Browse the repository at this point in the history
Introduce `//lib/api.js` that declares a list of publicly exposed
classes.

The `//lib/api.js` list superceedes dynamic `helper.tracePublicAPI()` calls
and is used in the following places:
- [ASYNC STACKS]: generate "async stacks" for publicy exposed API in `//index.js`
- [COVERAGE]: move coverage support from `//lib/helper` to `//test/utils`
- [DOCLINT]: get rid of 'exluded classes' hardcoded list

This will help us to re-use our coverage and doclint infrastructure
for Puppeteer-Firefox.

Drive-By: it turns out we didn't run coverage for `SecurityDetails`
class, so we lack coverage for a few methods there. These are excluded
for now, sanity tests will be added in a follow-up.
  • Loading branch information
aslushnikov committed Jan 26, 2019
1 parent cd678fb commit 62da236
Show file tree
Hide file tree
Showing 21 changed files with 139 additions and 128 deletions.
10 changes: 10 additions & 0 deletions index.js
Expand Up @@ -21,6 +21,16 @@ try {
asyncawait = false;
}

if (asyncawait) {
const {helper} = require('./lib/helper');
const api = require('./lib/api');
for (const className in api) {
// Puppeteer-web excludes certain classes from bundle, e.g. BrowserFetcher.
if (typeof api[className] === 'function')
helper.installAsyncStackHooks(api[className]);
}
}

// If node does not support async await, use the compiled version.
const Puppeteer = asyncawait ? require('./lib/Puppeteer') : require('./node6/lib/Puppeteer');
const packageJson = require('./package.json');
Expand Down
2 changes: 0 additions & 2 deletions lib/Accessibility.js
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {helper} = require('./helper');

/**
* @typedef {Object} SerializedAXNode
Expand Down Expand Up @@ -390,4 +389,3 @@ class AXNode {
}

module.exports = {Accessibility};
helper.tracePublicAPI(Accessibility);
3 changes: 0 additions & 3 deletions lib/Browser.js
Expand Up @@ -373,7 +373,4 @@ class BrowserContext extends EventEmitter {
}
}

helper.tracePublicAPI(BrowserContext);
helper.tracePublicAPI(Browser);

module.exports = {Browser, BrowserContext};
4 changes: 1 addition & 3 deletions lib/Connection.js
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {helper, assert} = require('./helper');
const {assert} = require('./helper');
const {Events} = require('./Events');
const debugProtocol = require('debug')('puppeteer:protocol');
const EventEmitter = require('events');
Expand Down Expand Up @@ -216,8 +216,6 @@ class CDPSession extends EventEmitter {
}
}

helper.tracePublicAPI(CDPSession);

/**
* @param {!Error} error
* @param {string} method
Expand Down
1 change: 0 additions & 1 deletion lib/Coverage.js
Expand Up @@ -64,7 +64,6 @@ class Coverage {
}

module.exports = {Coverage};
helper.tracePublicAPI(Coverage);

class JSCoverage {
/**
Expand Down
3 changes: 1 addition & 2 deletions lib/Dialog.js
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

const {helper, assert} = require('./helper');
const {assert} = require('./helper');

class Dialog {
/**
Expand Down Expand Up @@ -81,4 +81,3 @@ Dialog.Type = {
};

module.exports = {Dialog};
helper.tracePublicAPI(Dialog);
2 changes: 0 additions & 2 deletions lib/ExecutionContext.js
Expand Up @@ -175,6 +175,4 @@ class ExecutionContext {
}
}

helper.tracePublicAPI(ExecutionContext);

module.exports = {ExecutionContext, EVALUATION_SCRIPT_URL};
1 change: 0 additions & 1 deletion lib/FrameManager.js
Expand Up @@ -680,7 +680,6 @@ class Frame {
this._parentFrame = null;
}
}
helper.tracePublicAPI(Frame);

function assertNoLegacyNavigationOptions(options) {
assert(options['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
Expand Down
5 changes: 1 addition & 4 deletions lib/Input.js
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

const {helper, assert} = require('./helper');
const {assert} = require('./helper');
const keyDefinitions = require('./USKeyboardLayout');

/**
Expand Down Expand Up @@ -302,6 +302,3 @@ class Touchscreen {
}

module.exports = { Keyboard, Mouse, Touchscreen};
helper.tracePublicAPI(Keyboard);
helper.tracePublicAPI(Mouse);
helper.tracePublicAPI(Touchscreen);
3 changes: 0 additions & 3 deletions lib/JSHandle.js
Expand Up @@ -122,8 +122,6 @@ class JSHandle {
}
}

helper.tracePublicAPI(JSHandle);

class ElementHandle extends JSHandle {
/**
* @param {!Puppeteer.ExecutionContext} context
Expand Down Expand Up @@ -507,5 +505,4 @@ function computeQuadArea(quad) {
* @property {number} height
*/

helper.tracePublicAPI(ElementHandle);
module.exports = {createJSHandle, JSHandle, ElementHandle};
5 changes: 1 addition & 4 deletions lib/NetworkManager.js
Expand Up @@ -507,8 +507,6 @@ const errorReasons = {
'failed': 'Failed',
};

helper.tracePublicAPI(Request);

class Response {
/**
* @param {!Puppeteer.CDPSession} client
Expand Down Expand Up @@ -649,7 +647,6 @@ class Response {
return this._request.frame();
}
}
helper.tracePublicAPI(Response);

const IGNORED_HEADERS = new Set(['accept', 'referer', 'x-devtools-emulate-network-conditions-client-id', 'cookie', 'origin', 'content-type', 'intervention']);

Expand Down Expand Up @@ -798,4 +795,4 @@ const statusTexts = {
'511': 'Network Authentication Required',
};

module.exports = {Request, Response, NetworkManager};
module.exports = {Request, Response, NetworkManager, SecurityDetails};
3 changes: 1 addition & 2 deletions lib/Page.js
Expand Up @@ -1270,5 +1270,4 @@ class ConsoleMessage {
}


module.exports = {Page};
helper.tracePublicAPI(Page);
module.exports = {Page, ConsoleMessage};
2 changes: 0 additions & 2 deletions lib/Puppeteer.js
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {helper} = require('./helper');
const Launcher = require('./Launcher');
const BrowserFetcher = require('./BrowserFetcher');

Expand Down Expand Up @@ -68,4 +67,3 @@ module.exports = class {
}
};

helper.tracePublicAPI(module.exports, 'Puppeteer');
19 changes: 16 additions & 3 deletions lib/Target.js
@@ -1,6 +1,21 @@
/**
* Copyright 2019 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.
*/

const {Events} = require('./Events');
const {Page} = require('./Page');
const {helper} = require('./helper');

class Target {
/**
Expand Down Expand Up @@ -113,6 +128,4 @@ class Target {
}
}

helper.tracePublicAPI(Target);

module.exports = {Target};
1 change: 0 additions & 1 deletion lib/Tracing.js
Expand Up @@ -101,6 +101,5 @@ class Tracing {
}
}
}
helper.tracePublicAPI(Tracing);

module.exports = Tracing;
3 changes: 1 addition & 2 deletions lib/Worker.js
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/
const EventEmitter = require('events');
const {helper, debugError} = require('./helper');
const {debugError} = require('./helper');
const {ExecutionContext} = require('./ExecutionContext');
const {JSHandle} = require('./JSHandle');

Expand Down Expand Up @@ -78,4 +78,3 @@ class Worker extends EventEmitter {
}

module.exports = {Worker};
helper.tracePublicAPI(Worker);
42 changes: 42 additions & 0 deletions lib/api.js
@@ -0,0 +1,42 @@
/**
* Copyright 2019 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.
*/

module.exports = {
Accessibility: require('./Accessibility').Accessibility,
Browser: require('./Browser').Browser,
BrowserContext: require('./Browser').BrowserContext,
BrowserFetcher: require('./BrowserFetcher'),
CDPSession: require('./Connection').CDPSession,
ConsoleMessage: require('./Page').ConsoleMessage,
Coverage: require('./Coverage').Coverage,
Dialog: require('./Dialog').Dialog,
ElementHandle: require('./JSHandle').ElementHandle,
ExecutionContext: require('./ExecutionContext').ExecutionContext,
Frame: require('./FrameManager').Frame,
JSHandle: require('./JSHandle').JSHandle,
Keyboard: require('./Input').Keyboard,
Mouse: require('./Input').Mouse,
Page: require('./Page').Page,
Puppeteer: require('./Puppeteer'),
Request: require('./NetworkManager').Request,
Response: require('./NetworkManager').Response,
SecurityDetails: require('./NetworkManager').SecurityDetails,
Target: require('./Target').Target,
TimeoutError: require('./Errors').TimeoutError,
Touchscreen: require('./Input').Touchscreen,
Tracing: require('./Tracing'),
Worker: require('./Worker').Worker,
};
51 changes: 1 addition & 50 deletions lib/helper.js
Expand Up @@ -16,41 +16,6 @@
const {TimeoutError} = require('./Errors');

const debugError = require('debug')(`puppeteer:error`);
/** @type {?Map<string, boolean>} */
let apiCoverage = null;

/**
* @param {!Object} classType
* @param {string=} publicName
*/
function traceAPICoverage(classType, publicName) {
if (!apiCoverage)
return;

let className = publicName || classType.prototype.constructor.name;
className = className.substring(0, 1).toLowerCase() + className.substring(1);
for (const methodName of Reflect.ownKeys(classType.prototype)) {
const method = Reflect.get(classType.prototype, methodName);
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function')
continue;
apiCoverage.set(`${className}.${methodName}`, false);
Reflect.set(classType.prototype, methodName, function(...args) {
apiCoverage.set(`${className}.${methodName}`, true);
return method.call(this, ...args);
});
}

if (classType.Events) {
for (const event of Object.values(classType.Events))
apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false);
const method = Reflect.get(classType.prototype, 'emit');
Reflect.set(classType.prototype, 'emit', function(event, ...args) {
if (this.listenerCount(event))
apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, true);
return method.call(this, event, ...args);
});
}
}

class Helper {
/**
Expand Down Expand Up @@ -133,9 +98,8 @@ class Helper {

/**
* @param {!Object} classType
* @param {string=} publicName
*/
static tracePublicAPI(classType, publicName) {
static installAsyncStackHooks(classType) {
for (const methodName of Reflect.ownKeys(classType.prototype)) {
const method = Reflect.get(classType.prototype, methodName);
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction')
Expand All @@ -151,8 +115,6 @@ class Helper {
});
});
}

traceAPICoverage(classType, publicName);
}

/**
Expand All @@ -175,17 +137,6 @@ class Helper {
listeners.splice(0, listeners.length);
}

/**
* @return {?Map<string, boolean>}
*/
static publicAPICoverage() {
return apiCoverage;
}

static recordPublicAPICoverage() {
apiCoverage = new Map();
}

/**
* @param {!Object} obj
* @return {boolean}
Expand Down
30 changes: 11 additions & 19 deletions test/test.js
Expand Up @@ -75,32 +75,24 @@ beforeEach(async({server, httpsServer}) => {
httpsServer.reset();
});

describe('Chromium', () => {
const {helper} = require('../lib/helper');
if (process.env.COVERAGE)
helper.recordPublicAPICoverage();
const CHROMIUM_NO_COVERAGE = new Set([
'page.bringToFront',
'securityDetails.subjectName',
'securityDetails.issuer',
'securityDetails.validFrom',
'securityDetails.validTo',
...(headless ? [] : ['page.pdf']),
]);

describe('Chromium', () => {
require('./puppeteer.spec.js').addTests({
product: 'Chromium',
puppeteer: utils.requireRoot('index'),
defaultBrowserOptions,
testRunner,
});
if (process.env.COVERAGE) {
describe('COVERAGE', function() {
const coverage = helper.publicAPICoverage();
const disabled = new Set(['page.bringToFront']);
if (!headless)
disabled.add('page.pdf');

for (const method of coverage.keys()) {
(disabled.has(method) ? xit : it)(`public api '${method}' should be called`, async({page, server}) => {
if (!coverage.get(method))
throw new Error('NOT CALLED!');
});
}
});
}
if (process.env.COVERAGE)
utils.recordAPICoverage(testRunner, require('../lib/api'), CHROMIUM_NO_COVERAGE);
});

if (process.env.CI && testRunner.hasFocusedTestsOrSuites()) {
Expand Down

0 comments on commit 62da236

Please sign in to comment.