From 204c7ec8c45087adec5e970392efbcfe0c545c94 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Thu, 9 Aug 2018 16:51:12 -0700 Subject: [PATCH] feat: introduce puppeteer/Errors (#3056) This patch adds a new require, `puppeteer/Errors`, that holds all the Puppeteer-specific error classes. Currently, the only custom error class we use is `TimeoutError`. We'll expand in future with `CrashError` and some others. Fixes #1694. --- Errors.js | 28 ++++++++++++++++++ docs/api.md | 38 +++++++++++++++++++++++++ lib/BrowserFetcher.js | 2 +- lib/Errors.js | 29 +++++++++++++++++++ lib/FrameManager.js | 3 +- lib/Launcher.js | 3 +- lib/NavigatorWatcher.js | 3 +- lib/helper.js | 3 +- test/browser.spec.js | 4 ++- test/browsercontext.spec.js | 3 +- test/frame.spec.js | 4 +++ test/headful.spec.js | 7 +++-- test/ignorehttpserrors.spec.js | 6 ++-- test/input.spec.js | 5 ++-- test/page.spec.js | 13 ++++++--- test/puppeteer.spec.js | 8 +++--- test/target.spec.js | 5 ++-- test/test.js | 21 +++++++------- test/utils.js | 18 ++++++++++++ utils/doclint/check_public_api/index.js | 6 ++-- utils/testrunner/Matchers.js | 7 ++++- 21 files changed, 178 insertions(+), 38 deletions(-) create mode 100644 Errors.js create mode 100644 lib/Errors.js diff --git a/Errors.js b/Errors.js new file mode 100644 index 00000000000..dc41298ac12 --- /dev/null +++ b/Errors.js @@ -0,0 +1,28 @@ +/** + * Copyright 2018 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. + */ + +let asyncawait = true; +try { + new Function('async function test(){await 1}'); +} catch (error) { + asyncawait = false; +} + +// If node does not support async await, use the compiled version. +if (asyncawait) + module.exports = require('./lib/Errors'); +else + module.exports = require('./node6/lib/Errors'); diff --git a/docs/api.md b/docs/api.md index a391df725e4..5af186950b8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -12,6 +12,7 @@ Next Release: **Aug 9, 2018** - [Overview](#overview) - [Environment Variables](#environment-variables) +- [Error handling](#error-handling) - [Working with Chrome Extensions](#working-with-chrome-extensions) - [class: Puppeteer](#class-puppeteer) * [puppeteer.connect(options)](#puppeteerconnectoptions) @@ -285,6 +286,7 @@ Next Release: **Aug 9, 2018** * [coverage.startJSCoverage(options)](#coveragestartjscoverageoptions) * [coverage.stopCSSCoverage()](#coveragestopcsscoverage) * [coverage.stopJSCoverage()](#coveragestopjscoverage) +- [class: TimeoutError](#class-timeouterror) ### Overview @@ -316,6 +318,34 @@ If puppeteer doesn't find them in environment, lowercased variant of these varia - `PUPPETEER_DOWNLOAD_HOST` - overwrite host part of URL that is used to download Chromium - `PUPPETEER_CHROMIUM_REVISION` - specify a certain version of chrome you'd like puppeteer to use during the installation step. +### Error handling + +Often times, async methods might throw an error, signaling about their inability +to fulfill request. For example, [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options) +might fail if selector doesn't appear during the given timeframe. + +For certain types of errors Puppeteer uses specific error classes. +These classes are available through the `require('puppeteer/Errors')`. + +List of supported classes: +- [`TimeoutError`](#class-timeouterror) + +An example of handling timeout error: +```js +const {TimeoutError} = require('puppeteer/Errors'); + +// ... + +try { + await page.waitForSelector('.foo'); +} catch (e) { + if (e instanceof TimeoutError) { + // Do something if this is a timeout. + } +} +``` + + ### Working with Chrome Extensions Puppeteer can be used for testing Chrome Extensions. @@ -3157,6 +3187,14 @@ _To output coverage in a form consumable by [Istanbul](https://github.com/istanb > **NOTE** JavaScript Coverage doesn't include anonymous scripts by default. However, scripts with sourceURLs are reported. +### class: TimeoutError + +* extends: [Error] + +TimeoutError is emitted whenever certain operations are terminated due to timeout, e.g. [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options) or [puppeteer.launch([options])](#puppeteerlaunchoptions). + + + [Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array" [boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean" [Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer" diff --git a/lib/BrowserFetcher.js b/lib/BrowserFetcher.js index a550cfc5263..0c8f761cd3c 100644 --- a/lib/BrowserFetcher.js +++ b/lib/BrowserFetcher.js @@ -156,7 +156,7 @@ class BrowserFetcher { else if (this._platform === 'win32' || this._platform === 'win64') executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe'); else - throw 'Unsupported platform: ' + this._platform; + throw new Error('Unsupported platform: ' + this._platform); let url = downloadURLs[this._platform]; url = util.format(url, this._downloadHost, revision); const local = fs.existsSync(folderPath); diff --git a/lib/Errors.js b/lib/Errors.js new file mode 100644 index 00000000000..1bcfc2d0760 --- /dev/null +++ b/lib/Errors.js @@ -0,0 +1,29 @@ +/** + * Copyright 2018 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. + */ + +class CustomError extends Error { + constructor(message) { + super(message); + this.name = this.constructor.name; + Error.captureStackTrace(this, this.constructor); + } +} + +class TimeoutError extends CustomError {} + +module.exports = { + TimeoutError, +}; diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 8e1f238143c..963a1e3b766 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -19,6 +19,7 @@ const EventEmitter = require('events'); const {helper, assert} = require('./helper'); const {ExecutionContext, JSHandle} = require('./ExecutionContext'); const {ElementHandle} = require('./ElementHandle'); +const {TimeoutError} = require('./Errors'); const readFileAsync = helper.promisify(fs.readFile); @@ -876,7 +877,7 @@ class WaitTask { // Since page navigation requires us to re-install the pageScript, we should track // timeout on our end. if (timeout) { - const timeoutError = new Error(`waiting for ${title} failed: timeout ${timeout}ms exceeded`); + const timeoutError = new TimeoutError(`waiting for ${title} failed: timeout ${timeout}ms exceeded`); this._timeoutTimer = setTimeout(() => this.terminate(timeoutError), timeout); } this.rerun(); diff --git a/lib/Launcher.js b/lib/Launcher.js index 4c58f1e9068..b735c017391 100644 --- a/lib/Launcher.js +++ b/lib/Launcher.js @@ -24,6 +24,7 @@ const readline = require('readline'); const fs = require('fs'); const {helper, assert, debugError} = require('./helper'); const ChromiumRevision = require(path.join(helper.projectRoot(), 'package.json')).puppeteer.chromium_revision; +const {TimeoutError} = require('./Errors'); const mkdtempAsync = helper.promisify(fs.mkdtemp); const removeFolderAsync = helper.promisify(removeFolder); @@ -306,7 +307,7 @@ function waitForWSEndpoint(chromeProcess, timeout) { function onTimeout() { cleanup(); - reject(new Error(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${ChromiumRevision}`)); + reject(new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${ChromiumRevision}`)); } /** diff --git a/lib/NavigatorWatcher.js b/lib/NavigatorWatcher.js index abdbd99d3bb..e909b5bee0a 100644 --- a/lib/NavigatorWatcher.js +++ b/lib/NavigatorWatcher.js @@ -16,6 +16,7 @@ const {helper, assert} = require('./helper'); const {FrameManager} = require('./FrameManager'); +const {TimeoutError} = require('./Errors'); class NavigatorWatcher { /** @@ -70,7 +71,7 @@ class NavigatorWatcher { return new Promise(() => {}); const errorMessage = 'Navigation Timeout Exceeded: ' + this._timeout + 'ms exceeded'; return new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout)) - .then(() => new Error(errorMessage)); + .then(() => new TimeoutError(errorMessage)); } /** diff --git a/lib/helper.js b/lib/helper.js index 5770f056ead..f5dbf0e337a 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -15,6 +15,7 @@ */ const fs = require('fs'); const path = require('path'); +const {TimeoutError} = require('./Errors'); const debugError = require('debug')(`puppeteer:error`); /** @type {?Map} */ @@ -262,7 +263,7 @@ class Helper { if (timeout) { eventTimeout = setTimeout(() => { cleanup(); - rejectCallback(new Error('Timeout exceeded while waiting for event')); + rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); }, timeout); } function cleanup() { diff --git a/test/browser.spec.js b/test/browser.spec.js index 384eb1739bf..bceabc3216d 100644 --- a/test/browser.spec.js +++ b/test/browser.spec.js @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const utils = require('./utils'); +const puppeteer = utils.requireRoot('index'); -module.exports.addTests = function({testRunner, expect, puppeteer, headless}) { +module.exports.addTests = function({testRunner, expect, headless}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; diff --git a/test/browsercontext.spec.js b/test/browsercontext.spec.js index a2a317b5f63..e207e931255 100644 --- a/test/browsercontext.spec.js +++ b/test/browsercontext.spec.js @@ -15,8 +15,9 @@ */ const utils = require('./utils'); +const puppeteer = utils.requireRoot('index'); -module.exports.addTests = function({testRunner, expect, puppeteer}) { +module.exports.addTests = function({testRunner, expect}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; diff --git a/test/frame.spec.js b/test/frame.spec.js index 46d925d5283..db4621bac77 100644 --- a/test/frame.spec.js +++ b/test/frame.spec.js @@ -15,6 +15,7 @@ */ const utils = require('./utils'); +const {TimeoutError} = utils.requireRoot('Errors'); module.exports.addTests = function({testRunner, expect}) { const {describe, xdescribe, fdescribe} = testRunner; @@ -165,6 +166,7 @@ module.exports.addTests = function({testRunner, expect}) { await page.waitForFunction('false', {timeout: 10}).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('waiting for function failed: timeout'); + expect(error).toBeInstanceOf(TimeoutError); }); it('should disable timeout when its set to 0', async({page}) => { const watchdog = page.waitForFunction(() => { @@ -317,6 +319,7 @@ module.exports.addTests = function({testRunner, expect}) { await page.waitForSelector('div', {timeout: 10}).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('waiting for selector "div" failed: timeout'); + expect(error).toBeInstanceOf(TimeoutError); }); it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => { await page.setContent(`
`); @@ -359,6 +362,7 @@ module.exports.addTests = function({testRunner, expect}) { await page.waitForXPath('//div', {timeout: 10}).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('waiting for XPath "//div" failed: timeout'); + expect(error).toBeInstanceOf(TimeoutError); }); it('should run in specified frame', async({page, server}) => { await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); diff --git a/test/headful.spec.js b/test/headful.spec.js index bd3ad8f8773..972f38f401a 100644 --- a/test/headful.spec.js +++ b/test/headful.spec.js @@ -16,7 +16,9 @@ const path = require('path'); const os = require('os'); -const {waitEvent} = require('./utils.js'); +const utils = require('./utils'); +const {waitEvent} = utils; +const puppeteer = utils.requireRoot('index.js'); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); @@ -34,11 +36,10 @@ function waitForBackgroundPageTarget(browser) { }); } -module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}) { +module.exports.addTests = function({testRunner, expect, defaultBrowserOptions}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const puppeteer = require(PROJECT_ROOT); const headfulOptions = Object.assign({}, defaultBrowserOptions, { headless: false diff --git a/test/ignorehttpserrors.spec.js b/test/ignorehttpserrors.spec.js index ec5b0c67289..17d21d37d7e 100644 --- a/test/ignorehttpserrors.spec.js +++ b/test/ignorehttpserrors.spec.js @@ -14,11 +14,13 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}) { +const utils = require('./utils'); +const puppeteer = utils.requireRoot('index.js'); + +module.exports.addTests = function({testRunner, expect, defaultBrowserOptions}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const puppeteer = require(PROJECT_ROOT); describe('ignoreHTTPSErrors', function() { beforeAll(async state => { const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions); diff --git a/test/input.spec.js b/test/input.spec.js index 9505b06cc4e..5a583e00c2e 100644 --- a/test/input.spec.js +++ b/test/input.spec.js @@ -16,12 +16,13 @@ const path = require('path'); const utils = require('./utils'); +const DeviceDescriptors = utils.requireRoot('DeviceDescriptors'); +const iPhone = DeviceDescriptors['iPhone 6']; -module.exports.addTests = function({testRunner, expect, DeviceDescriptors}) { +module.exports.addTests = function({testRunner, expect}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const iPhone = DeviceDescriptors['iPhone 6']; describe('input', function() { it('should click the button', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); diff --git a/test/page.spec.js b/test/page.spec.js index ee80272a207..a966fc99867 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -16,14 +16,17 @@ const fs = require('fs'); const path = require('path'); const utils = require('./utils'); -const {waitEvent} = require('./utils'); +const {waitEvent} = utils; +const {TimeoutError} = utils.requireRoot('Errors'); -module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescriptors, headless}) { +const DeviceDescriptors = utils.requireRoot('DeviceDescriptors'); +const iPhone = DeviceDescriptors['iPhone 6']; +const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; + +module.exports.addTests = function({testRunner, expect, headless}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const iPhone = DeviceDescriptors['iPhone 6']; - const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; describe('Page.close', function() { it('should reject all promises when page is closed', async({context}) => { @@ -531,6 +534,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescrip let error = null; await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); + expect(error).toBeInstanceOf(TimeoutError); }); it('should fail when exceeding default maximum navigation timeout', async({page, server}) => { // Hang for request to the empty.html @@ -539,6 +543,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescrip page.setDefaultNavigationTimeout(1); await page.goto(server.PREFIX + '/empty.html').catch(e => error = e); expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); + expect(error).toBeInstanceOf(TimeoutError); }); it('should disable timeout when its set to 0', async({page, server}) => { let error = null; diff --git a/test/puppeteer.spec.js b/test/puppeteer.spec.js index d45fab9d665..032e91978ca 100644 --- a/test/puppeteer.spec.js +++ b/test/puppeteer.spec.js @@ -23,12 +23,12 @@ const readFileAsync = helper.promisify(fs.readFile); const statAsync = helper.promisify(fs.stat); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const utils = require('./utils'); +const puppeteer = utils.requireRoot('index'); -module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}) { +module.exports.addTests = function({testRunner, expect, defaultBrowserOptions}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const puppeteer = require(PROJECT_ROOT); describe('Puppeteer', function() { describe('BrowserFetcher', function() { @@ -145,7 +145,7 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro const {spawn} = require('child_process'); const options = Object.assign({}, defaultBrowserOptions, {dumpio: true}); const res = spawn('node', - [path.join(__dirname, 'fixtures', 'dumpio.js'), PROJECT_ROOT, JSON.stringify(options), server.EMPTY_PAGE, dumpioTextToLog]); + [path.join(__dirname, 'fixtures', 'dumpio.js'), utils.projectRoot(), JSON.stringify(options), server.EMPTY_PAGE, dumpioTextToLog]); res.stderr.on('data', data => dumpioData += data.toString('utf8')); await new Promise(resolve => res.on('close', resolve)); @@ -153,7 +153,7 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro }); it('should close the browser when the node process closes', async({ server }) => { const {spawn, execSync} = require('child_process'); - const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), PROJECT_ROOT, JSON.stringify(defaultBrowserOptions)]); + const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), utils.projectRoot(), JSON.stringify(defaultBrowserOptions)]); let wsEndPointCallback; const wsEndPointPromise = new Promise(x => wsEndPointCallback = x); let output = ''; diff --git a/test/target.spec.js b/test/target.spec.js index ca1bbb66453..1fcb0721beb 100644 --- a/test/target.spec.js +++ b/test/target.spec.js @@ -14,9 +14,10 @@ * limitations under the License. */ -const {waitEvent} = require('./utils'); +const utils = require('./utils'); +const {waitEvent} = utils; -module.exports.addTests = function({testRunner, expect, puppeteer}) { +module.exports.addTests = function({testRunner, expect}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; diff --git a/test/test.js b/test/test.js index fd95f6eb5e8..79afd73e973 100644 --- a/test/test.js +++ b/test/test.js @@ -21,14 +21,13 @@ const GoldenUtils = require('./golden-utils'); const GOLDEN_DIR = path.join(__dirname, 'golden'); const OUTPUT_DIR = path.join(__dirname, 'output'); const {TestRunner, Reporter, Matchers} = require('../utils/testrunner/'); +const utils = require('./utils'); const {helper, assert} = require('../lib/helper'); if (process.env.COVERAGE) helper.recordPublicAPICoverage(); -const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..'); -const puppeteer = require(PROJECT_ROOT); -const DeviceDescriptors = require(path.join(PROJECT_ROOT, 'DeviceDescriptors')); +const puppeteer = utils.requireRoot('index'); const YELLOW_COLOR = '\x1b[33m'; const RESET_COLOR = '\x1b[0m'; @@ -145,28 +144,28 @@ describe('Browser', function() { // Page-level tests that are given a browser, a context and a page. // Each test is launched in a new browser context. require('./CDPSession.spec.js').addTests({testRunner, expect}); - require('./browser.spec.js').addTests({testRunner, expect, puppeteer, headless}); + require('./browser.spec.js').addTests({testRunner, expect, headless}); require('./cookies.spec.js').addTests({testRunner, expect}); require('./coverage.spec.js').addTests({testRunner, expect}); require('./elementhandle.spec.js').addTests({testRunner, expect}); require('./frame.spec.js').addTests({testRunner, expect}); - require('./input.spec.js').addTests({testRunner, expect, DeviceDescriptors}); + require('./input.spec.js').addTests({testRunner, expect}); require('./jshandle.spec.js').addTests({testRunner, expect}); require('./network.spec.js').addTests({testRunner, expect}); - require('./page.spec.js').addTests({testRunner, expect, puppeteer, DeviceDescriptors, headless}); - require('./target.spec.js').addTests({testRunner, expect, puppeteer}); + require('./page.spec.js').addTests({testRunner, expect, headless}); + require('./target.spec.js').addTests({testRunner, expect}); require('./tracing.spec.js').addTests({testRunner, expect}); require('./worker.spec.js').addTests({testRunner, expect}); }); // Browser-level tests that are given a browser. - require('./browsercontext.spec.js').addTests({testRunner, expect, puppeteer}); + require('./browsercontext.spec.js').addTests({testRunner, expect}); }); // Top-level tests that launch Browser themselves. -require('./ignorehttpserrors.spec.js').addTests({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}); -require('./puppeteer.spec.js').addTests({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}); -require('./headful.spec.js').addTests({testRunner, expect, PROJECT_ROOT, defaultBrowserOptions}); +require('./ignorehttpserrors.spec.js').addTests({testRunner, expect, defaultBrowserOptions}); +require('./puppeteer.spec.js').addTests({testRunner, expect, defaultBrowserOptions}); +require('./headful.spec.js').addTests({testRunner, expect, defaultBrowserOptions}); if (process.env.COVERAGE) { describe('COVERAGE', function() { diff --git a/test/utils.js b/test/utils.js index 3aae5f8d98a..a8e7e805931 100644 --- a/test/utils.js +++ b/test/utils.js @@ -14,7 +14,25 @@ * limitations under the License. */ +const fs = require('fs'); +const path = require('path'); +const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..'); + const utils = module.exports = { + /** + * @return {string} + */ + projectRoot: function() { + return PROJECT_ROOT; + }, + + /** + * @return {*} + */ + requireRoot: function(name) { + return require(path.join(PROJECT_ROOT, name)); + }, + /** * @param {!Page} page * @param {string} frameId diff --git a/utils/doclint/check_public_api/index.js b/utils/doclint/check_public_api/index.js index b3a97b0a676..d6b255dc0f3 100644 --- a/utils/doclint/check_public_api/index.js +++ b/utils/doclint/check_public_api/index.js @@ -22,6 +22,7 @@ const Message = require('../Message'); const EXCLUDE_CLASSES = new Set([ 'CSSCoverage', 'Connection', + 'CustomError', 'EmulationManager', 'FrameManager', 'JSCoverage', @@ -35,11 +36,12 @@ const EXCLUDE_CLASSES = new Set([ 'WaitTask', ]); -const EXCLUDE_METHODS = new Set([ +const EXCLUDE_PROPERTIES = new Set([ 'Browser.create', 'Headers.fromPayload', 'Page.create', 'JSHandle.toString', + 'TimeoutError.name', ]); /** @@ -137,7 +139,7 @@ function filterJSDocumentation(jsDocumentation) { // Exclude all constructors by default. if (member.name === 'constructor' && member.type === 'method') return false; - return !EXCLUDE_METHODS.has(`${cls.name}.${member.name}`); + return !EXCLUDE_PROPERTIES.has(`${cls.name}.${member.name}`); }); classes.push(new Documentation.Class(cls.name, members)); } diff --git a/utils/testrunner/Matchers.js b/utils/testrunner/Matchers.js index e6d5b434544..a7d208c2994 100644 --- a/utils/testrunner/Matchers.js +++ b/utils/testrunner/Matchers.js @@ -108,7 +108,12 @@ const DefaultMatchers = { pass: Math.abs(value - other) < Math.pow(10, -precision), message }; - } + }, + + toBeInstanceOf: function(value, other, message) { + message = message || `${value.constructor.name} instanceof ${other.name}`; + return { pass: value instanceof other, message }; + }, }; function stringify(value) {