From 448ac4ce647b845c4ed4bb203c6599e9c0f8ba09 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Wed, 21 Jun 2017 07:45:13 -0700 Subject: [PATCH] Reformat codebase into 2-spaces This patch: - reformats codebase to use 2-spaces instead of 4. This will align the project with other codebases (e.g. DevTools and Lighthouse) - enables eslint indentation checking References #19 --- .eslintrc.js | 3 +- index.js | 2 +- install.js | 28 +- lib/Browser.js | 210 +++---- lib/Connection.js | 220 +++---- lib/Dialog.js | 86 +-- lib/FrameManager.js | 390 ++++++------- lib/Page.js | 1080 +++++++++++++++++------------------ lib/Request.js | 186 +++--- lib/helpers.js | 33 +- package-lock.json | 914 +++++++++++++++++++++++++++++ phantom_shim/FileSystem.js | 600 +++++++++---------- phantom_shim/Phantom.js | 200 +++---- phantom_shim/System.js | 158 ++--- phantom_shim/WebPage.js | 612 ++++++++++---------- phantom_shim/WebServer.js | 114 ++-- phantom_shim/runner.js | 92 +-- phantom_shim/utilities.js | 24 +- test/StaticServer.js | 54 +- test/frame-utils.js | 108 ++-- test/golden-utils.js | 188 +++--- test/test.js | 568 +++++++++--------- utils/ChromiumDownloader.js | 238 ++++---- utils/check_availability.js | 188 +++--- yarn.lock | 4 + 25 files changed, 3607 insertions(+), 2693 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7014f72191a..4acbeeb2719 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -100,12 +100,11 @@ module.exports = { "no-trailing-spaces": 2, "linebreak-style": [ 2, "unix" ], + "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], /** * Disabled, aspirational rules */ - "indent": [0, 4, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], - // brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions "brace-style": [0, "allman", { "allowSingleLine": true }], diff --git a/index.js b/index.js index 51451f9ae28..f9e23c37c19 100644 --- a/index.js +++ b/index.js @@ -15,5 +15,5 @@ */ module.exports = { - Browser: require('./lib/Browser') + Browser: require('./lib/Browser') }; diff --git a/install.js b/install.js index 588a393faca..0d6b7f0a5e2 100644 --- a/install.js +++ b/install.js @@ -20,28 +20,26 @@ var ProgressBar = require('progress'); // Do nothing if the revision is already downloaded. if (Downloader.revisionInfo(Downloader.currentPlatform(), revision)) - return; + return; Downloader.downloadRevision(Downloader.currentPlatform(), revision, onProgress) - .catch(error => { - console.error('Download failed: ' + error.message); - }); + .catch(error => console.error('Download failed: ' + error.message)); var progressBar = null; function onProgress(bytesTotal, delta) { - if (!progressBar) { - progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, { - complete: '=', - incomplete: ' ', - width: 20, - total: bytesTotal, - }); - } - progressBar.tick(delta); + if (!progressBar) { + progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, { + complete: '=', + incomplete: ' ', + width: 20, + total: bytesTotal, + }); + } + progressBar.tick(delta); } function toMegabytes(bytes) { - var mb = bytes / 1024 / 1024; - return (Math.round(mb * 10) / 10) + ' Mb'; + var mb = bytes / 1024 / 1024; + return (Math.round(mb * 10) / 10) + ' Mb'; } diff --git a/lib/Browser.js b/lib/Browser.js index e6452462706..16b665d6538 100644 --- a/lib/Browser.js +++ b/lib/Browser.js @@ -26,125 +26,125 @@ var CHROME_PROFILE_PATH = path.resolve(__dirname, '..', '.dev_profile'); var browserId = 0; var DEFAULT_ARGS = [ - '--disable-background-timer-throttling', - '--no-first-run', + '--disable-background-timer-throttling', + '--no-first-run', ]; class Browser { - /** - * @param {(!Object|undefined)} options - */ - constructor(options) { - options = options || {}; - ++browserId; - this._userDataDir = CHROME_PROFILE_PATH + browserId; - this._remoteDebuggingPort = 9227; - if (typeof options.remoteDebuggingPort === 'number') - this._remoteDebuggingPort = options.remoteDebuggingPort; - this._chromeArguments = DEFAULT_ARGS.concat([ - `--user-data-dir=${this._userDataDir}`, - `--remote-debugging-port=${this._remoteDebuggingPort}`, - ]); - if (typeof options.headless !== 'boolean' || options.headless) { - this._chromeArguments.push(...[ - `--headless`, - `--disable-gpu`, - ]); - } - if (typeof options.executablePath === 'string') { - this._chromeExecutable = options.executablePath; - } else { - var chromiumRevision = require('../package.json').puppeteer.chromium_revision; - var revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), chromiumRevision); - console.assert(revisionInfo, 'Chromium revision is not downloaded. Run npm install'); - this._chromeExecutable = revisionInfo.executablePath; - } - if (Array.isArray(options.args)) - this._chromeArguments.push(...options.args); - this._terminated = false; - this._chromeProcess = null; + /** + * @param {(!Object|undefined)} options + */ + constructor(options) { + options = options || {}; + ++browserId; + this._userDataDir = CHROME_PROFILE_PATH + browserId; + this._remoteDebuggingPort = 9227; + if (typeof options.remoteDebuggingPort === 'number') + this._remoteDebuggingPort = options.remoteDebuggingPort; + this._chromeArguments = DEFAULT_ARGS.concat([ + `--user-data-dir=${this._userDataDir}`, + `--remote-debugging-port=${this._remoteDebuggingPort}`, + ]); + if (typeof options.headless !== 'boolean' || options.headless) { + this._chromeArguments.push(...[ + `--headless`, + `--disable-gpu`, + ]); } - - /** - * @return {!Promise} - */ - async newPage() { - await this._ensureChromeIsRunning(); - if (!this._chromeProcess || this._terminated) - throw new Error('ERROR: this chrome instance is not alive any more!'); - var client = await Connection.create(this._remoteDebuggingPort); - var page = await Page.create(client); - return page; + if (typeof options.executablePath === 'string') { + this._chromeExecutable = options.executablePath; + } else { + var chromiumRevision = require('../package.json').puppeteer.chromium_revision; + var revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), chromiumRevision); + console.assert(revisionInfo, 'Chromium revision is not downloaded. Run npm install'); + this._chromeExecutable = revisionInfo.executablePath; } + if (Array.isArray(options.args)) + this._chromeArguments.push(...options.args); + this._terminated = false; + this._chromeProcess = null; + } - /** - * @param {!Page} page - */ - async closePage(page) { - if (!this._chromeProcess || this._terminated) - throw new Error('ERROR: this chrome instance is not running'); - await page.close(); - } + /** + * @return {!Promise} + */ + async newPage() { + await this._ensureChromeIsRunning(); + if (!this._chromeProcess || this._terminated) + throw new Error('ERROR: this chrome instance is not alive any more!'); + var client = await Connection.create(this._remoteDebuggingPort); + var page = await Page.create(client); + return page; + } - /** - * @return {string} - */ - async version() { - await this._ensureChromeIsRunning(); - var version = await Connection.version(this._remoteDebuggingPort); - return version.Browser; - } + /** + * @param {!Page} page + */ + async closePage(page) { + if (!this._chromeProcess || this._terminated) + throw new Error('ERROR: this chrome instance is not running'); + await page.close(); + } - async _ensureChromeIsRunning() { - if (this._chromeProcess) - return; - this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {}); - var stderr = ''; - this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8')); - // Cleanup as processes exit. - process.on('exit', () => this._chromeProcess.kill()); - this._chromeProcess.on('exit', () => { - this._terminated = true; - removeRecursive(this._userDataDir); - }); + /** + * @return {string} + */ + async version() { + await this._ensureChromeIsRunning(); + var version = await Connection.version(this._remoteDebuggingPort); + return version.Browser; + } - await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated); - if (this._terminated) - throw new Error('Failed to launch chrome! ' + stderr); - } + async _ensureChromeIsRunning() { + if (this._chromeProcess) + return; + this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {}); + var stderr = ''; + this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8')); + // Cleanup as processes exit. + process.on('exit', () => this._chromeProcess.kill()); + this._chromeProcess.on('exit', () => { + this._terminated = true; + removeRecursive(this._userDataDir); + }); - close() { - if (!this._chromeProcess) - return; - this._chromeProcess.kill(); - } + await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated); + if (this._terminated) + throw new Error('Failed to launch chrome! ' + stderr); + } + + close() { + if (!this._chromeProcess) + return; + this._chromeProcess.kill(); + } } module.exports = Browser; function waitForChromeResponsive(remoteDebuggingPort, shouldWaitCallback) { - var fulfill; - var promise = new Promise(x => fulfill = x); - var options = { - method: 'GET', - host: 'localhost', - port: remoteDebuggingPort, - path: '/json/list' - }; - var probeTimeout = 100; - sendRequest(); - return promise; + var fulfill; + var promise = new Promise(x => fulfill = x); + var options = { + method: 'GET', + host: 'localhost', + port: remoteDebuggingPort, + path: '/json/list' + }; + var probeTimeout = 100; + sendRequest(); + return promise; - function sendRequest() { - var req = http.request(options, res => { - fulfill(); - }); - req.on('error', e => { - if (shouldWaitCallback()) - setTimeout(sendRequest, probeTimeout); - else - fulfill(); - }); - req.end(); - } + function sendRequest() { + var req = http.request(options, res => { + fulfill(); + }); + req.on('error', e => { + if (shouldWaitCallback()) + setTimeout(sendRequest, probeTimeout); + else + fulfill(); + }); + req.end(); + } } diff --git a/lib/Connection.js b/lib/Connection.js index cc1735a131e..25f924598da 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -20,90 +20,90 @@ var http = require('http'); const COMMAND_TIMEOUT = 10000; class Connection extends EventEmitter { - /** - * @param {number} port - * @param {string} pageId - * @param {!WebSocket} ws - */ - constructor(port, pageId, ws) { - super(); - this._port = port; - this._pageId = pageId; - this._lastId = 0; - /** @type {!Map}*/ - this._callbacks = new Map(); + /** + * @param {number} port + * @param {string} pageId + * @param {!WebSocket} ws + */ + constructor(port, pageId, ws) { + super(); + this._port = port; + this._pageId = pageId; + this._lastId = 0; + /** @type {!Map}*/ + this._callbacks = new Map(); - this._ws = ws; - this._ws.on('message', this._onMessage.bind(this)); - this._ws.on('close', this._onClose.bind(this)); + this._ws = ws; + this._ws.on('message', this._onMessage.bind(this)); + this._ws.on('close', this._onClose.bind(this)); + } + + /** + * @param {string} method + * @param {(!Object|undefined)} params + * @return {!Promise} + */ + send(method, params = {}) { + var id = ++this._lastId; + var message = JSON.stringify({id, method, params}); + this._ws.send(message); + return new Promise((resolve, reject) => { + this._callbacks.set(id, {resolve, reject, method}); + }); + } + + /** + * @param {string} message + */ + _onMessage(message) { + var object = JSON.parse(message); + if (object.id && this._callbacks.has(object.id)) { + var callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) + callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`)); + else + callback.resolve(object.result); + } else { + console.assert(!object.id); + this.emit(object.method, object.params); } + } - /** - * @param {string} method - * @param {(!Object|undefined)} params - * @return {!Promise} - */ - send(method, params = {}) { - var id = ++this._lastId; - var message = JSON.stringify({id, method, params}); - this._ws.send(message); - return new Promise((resolve, reject) => { - this._callbacks.set(id, {resolve, reject, method}); - }); - } + _onClose() { + this._ws.removeAllListeners(); + this._ws.close(); + } - /** - * @param {string} message - */ - _onMessage(message) { - var object = JSON.parse(message); - if (object.id && this._callbacks.has(object.id)) { - var callback = this._callbacks.get(object.id); - this._callbacks.delete(object.id); - if (object.error) - callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`)); - else - callback.resolve(object.result); - } else { - console.assert(!object.id); - this.emit(object.method, object.params); - } - } + /** + * @return {!Promise} + */ + async dispose() { + await runJsonCommand(this._port, `close/${this._pageId}`); + } - _onClose() { - this._ws.removeAllListeners(); - this._ws.close(); - } + /** + * @param {number} port + * @return {!Promise} + */ + static async create(port) { + var newTab = await runJsonCommand(port, 'new'); + var url = newTab.webSocketDebuggerUrl; - /** - * @return {!Promise} - */ - async dispose() { - await runJsonCommand(this._port, `close/${this._pageId}`); - } + return new Promise((resolve, reject) => { + var ws = new WebSocket(url, { perMessageDeflate: false }); + ws.on('open', () => resolve(new Connection(port, newTab.id, ws))); + ws.on('error', reject); + }); + } - /** - * @param {number} port - * @return {!Promise} - */ - static async create(port) { - var newTab = await runJsonCommand(port, 'new'); - var url = newTab.webSocketDebuggerUrl; - - return new Promise((resolve, reject) => { - var ws = new WebSocket(url, { perMessageDeflate: false }); - ws.on('open', () => resolve(new Connection(port, newTab.id, ws))); - ws.on('error', reject); - }); - } - - /** - * @param {number} port - * @return {!Promise} - */ - static version(port) { - return runJsonCommand(port, 'version'); - } + /** + * @param {number} port + * @return {!Promise} + */ + static version(port) { + return runJsonCommand(port, 'version'); + } } /** @@ -112,41 +112,41 @@ class Connection extends EventEmitter { * @return {!Promise} */ function runJsonCommand(port, command) { - var request = http.get({ - hostname: 'localhost', - port: port, - path: '/json/' + command - }, onResponse); - request.setTimeout(COMMAND_TIMEOUT, onTimeout); - var resolve, reject; - return new Promise((res, rej) => { resolve = res; reject = rej; }); + var request = http.get({ + hostname: 'localhost', + port: port, + path: '/json/' + command + }, onResponse); + request.setTimeout(COMMAND_TIMEOUT, onTimeout); + var resolve, reject; + return new Promise((res, rej) => { resolve = res; reject = rej; }); - function onResponse(response) { - var data = ''; - response.on('data', chunk => data += chunk); - response.on('end', () => { - if (response.statusCode !== 200) { - reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`)); - return; - } - // In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD - if (data === 'Target is closing' || data === 'Target activated') { - resolve({message: data}); - return; - } - try { - resolve(JSON.parse(data)); - } catch (e) { - reject(e); - } - }); - } + function onResponse(response) { + var data = ''; + response.on('data', chunk => data += chunk); + response.on('end', () => { + if (response.statusCode !== 200) { + reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`)); + return; + } + // In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD + if (data === 'Target is closing' || data === 'Target activated') { + resolve({message: data}); + return; + } + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(e); + } + }); + } - function onTimeout() { - request.abort(); - // Reject on error with code specifically indicating timeout in connection setup. - reject(new Error('Timeout waiting for initial Debugger Protocol connection.')); - } + function onTimeout() { + request.abort(); + // Reject on error with code specifically indicating timeout in connection setup. + reject(new Error('Timeout waiting for initial Debugger Protocol connection.')); + } } module.exports = Connection; diff --git a/lib/Dialog.js b/lib/Dialog.js index db859a370df..af2cbf34d15 100644 --- a/lib/Dialog.js +++ b/lib/Dialog.js @@ -15,55 +15,55 @@ */ class Dialog { - /** - * @param {!Connection} client - * @param {!Dialog.Type} type - * @param {string} message - */ - constructor(client, type, message) { - this._client = client; - this.type = type; - this._message = message; - this._handled = false; - } + /** + * @param {!Connection} client + * @param {!Dialog.Type} type + * @param {string} message + */ + constructor(client, type, message) { + this._client = client; + this.type = type; + this._message = message; + this._handled = false; + } - /** - * @return {string} - */ - message() { - return this._message; - } + /** + * @return {string} + */ + message() { + return this._message; + } - /** - * @param {string=} promptText - * @return {!Promise} - */ - async accept(promptText) { - console.assert(!this._handled, 'Cannot accept dialog which is already handled!'); - this._handled = true; - await this._client.send('Page.handleJavaScriptDialog', { - accept: true, - promptText: promptText - }); - } + /** + * @param {string=} promptText + * @return {!Promise} + */ + async accept(promptText) { + console.assert(!this._handled, 'Cannot accept dialog which is already handled!'); + this._handled = true; + await this._client.send('Page.handleJavaScriptDialog', { + accept: true, + promptText: promptText + }); + } - /** - * @return {!Promise} - */ - async dismiss() { - console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); - this._handled = true; - await this._client.send('Page.handleJavaScriptDialog', { - accept: false - }); - } + /** + * @return {!Promise} + */ + async dismiss() { + console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); + this._handled = true; + await this._client.send('Page.handleJavaScriptDialog', { + accept: false + }); + } } Dialog.Type = { - Alert: 'alert', - BeforeUnload: 'beforeunload', - Confirm: 'confirm', - Prompt: 'prompt' + Alert: 'alert', + BeforeUnload: 'beforeunload', + Confirm: 'confirm', + Prompt: 'prompt' }; module.exports = Dialog; diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 6e0e82755c0..8763229aa4a 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -17,229 +17,229 @@ var EventEmitter = require('events'); class FrameManager extends EventEmitter { - /** - * @param {!Connection} client - * @return {!FrameManager} - */ - static async create(client) { - var mainFramePayload = await client.send('Page.getResourceTree'); - return new FrameManager(client, mainFramePayload.frameTree); + /** + * @param {!Connection} client + * @return {!FrameManager} + */ + static async create(client) { + var mainFramePayload = await client.send('Page.getResourceTree'); + return new FrameManager(client, mainFramePayload.frameTree); + } + + /** + * @param {!Connection} client + * @param {!Object} frameTree + */ + constructor(client, frameTree) { + super(); + this._client = client; + /** @type {!Map} */ + this._frames = new Map(); + this._mainFrame = this._addFramesRecursively(null, frameTree); + + this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId)); + this._client.on('Page.frameNavigated', event => this._frameNavigated(event.frame)); + this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId)); + } + + /** + * @return {!Frame} + */ + mainFrame() { + return this._mainFrame; + } + + /** + * @return {!Array} + */ + frames() { + return Array.from(this._frames.values()); + } + + /** + * @param {string} frameId + * @param {?string} parentFrameId + * @return {?Frame} + */ + _frameAttached(frameId, parentFrameId) { + if (this._frames.has(frameId)) + return; + + if (!parentFrameId) { + // Navigation to the new backend process. + this._navigateFrame(this._mainFrame, frameId, null); + return; } + var parentFrame = this._frames.get(parentFrameId); + var frame = new Frame(parentFrame, frameId, null); + this._frames.set(frame._id, frame); + this.emit(FrameManager.Events.FrameAttached, frame); + } - /** - * @param {!Connection} client - * @param {!Object} frameTree - */ - constructor(client, frameTree) { - super(); - this._client = client; - /** @type {!Map} */ - this._frames = new Map(); - this._mainFrame = this._addFramesRecursively(null, frameTree); - - this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId)); - this._client.on('Page.frameNavigated', event => this._frameNavigated(event.frame)); - this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId)); + /** + * @param {!Object} framePayload + */ + _frameNavigated(framePayload) { + var frame = this._frames.get(framePayload.id); + if (!frame) { + // Simulate missed "frameAttached" for a main frame navigation to the new backend process. + console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.'); + frame = this._mainFrame; } + this._navigateFrame(frame, framePayload.id, framePayload); + } - /** - * @return {!Frame} - */ - mainFrame() { - return this._mainFrame; - } + /** + * @param {string} frameId + */ + _frameDetached(frameId) { + var frame = this._frames.get(frameId); + if (frame) + this._removeFramesRecursively(frame); + } - /** - * @return {!Array} - */ - frames() { - return Array.from(this._frames.values()); - } + /** + * @param {!Frame} frame + * @param {string} newFrameId + * @param {?Object} newFramePayload + */ + _navigateFrame(frame, newFrameId, newFramePayload) { + // Detach all child frames first. + for (var child of frame.childFrames()) + this._removeFramesRecursively(child); + this._frames.delete(frame._id, frame); + frame._id = newFrameId; + frame._adoptPayload(newFramePayload); + this._frames.set(newFrameId, frame); + this.emit(FrameManager.Events.FrameNavigated, frame); + } - /** - * @param {string} frameId - * @param {?string} parentFrameId - * @return {?Frame} - */ - _frameAttached(frameId, parentFrameId) { - if (this._frames.has(frameId)) - return; + /** + * @param {?Frame} parentFrame + * @param {!Object} frameTreePayload + * @return {!Frame} + */ + _addFramesRecursively(parentFrame, frameTreePayload) { + var framePayload = frameTreePayload.frame; + var frame = new Frame(parentFrame, framePayload.id, framePayload); + this._frames.set(frame._id, frame); - if (!parentFrameId) { - // Navigation to the new backend process. - this._navigateFrame(this._mainFrame, frameId, null); - return; - } - var parentFrame = this._frames.get(parentFrameId); - var frame = new Frame(parentFrame, frameId, null); - this._frames.set(frame._id, frame); - this.emit(FrameManager.Events.FrameAttached, frame); - } + for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i) + this._addFramesRecursively(frame, frameTreePayload.childFrames[i]); + return frame; + } - /** - * @param {!Object} framePayload - */ - _frameNavigated(framePayload) { - var frame = this._frames.get(framePayload.id); - if (!frame) { - // Simulate missed "frameAttached" for a main frame navigation to the new backend process. - console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.'); - frame = this._mainFrame; - } - this._navigateFrame(frame, framePayload.id, framePayload); - } - - /** - * @param {string} frameId - */ - _frameDetached(frameId) { - var frame = this._frames.get(frameId); - if (frame) - this._removeFramesRecursively(frame); - } - - /** - * @param {!Frame} frame - * @param {string} newFrameId - * @param {?Object} newFramePayload - */ - _navigateFrame(frame, newFrameId, newFramePayload) { - // Detach all child frames first. - for (var child of frame.childFrames()) - this._removeFramesRecursively(child); - this._frames.delete(frame._id, frame); - frame._id = newFrameId; - frame._adoptPayload(newFramePayload); - this._frames.set(newFrameId, frame); - this.emit(FrameManager.Events.FrameNavigated, frame); - } - - /** - * @param {?Frame} parentFrame - * @param {!Object} frameTreePayload - * @return {!Frame} - */ - _addFramesRecursively(parentFrame, frameTreePayload) { - var framePayload = frameTreePayload.frame; - var frame = new Frame(parentFrame, framePayload.id, framePayload); - this._frames.set(frame._id, frame); - - for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i) - this._addFramesRecursively(frame, frameTreePayload.childFrames[i]); - return frame; - } - - /** - * @param {!Frame} frame - */ - _removeFramesRecursively(frame) { - for (var child of frame.childFrames()) - this._removeFramesRecursively(child); - frame._detach(); - this._frames.delete(frame._id); - this.emit(FrameManager.Events.FrameDetached, frame); - } + /** + * @param {!Frame} frame + */ + _removeFramesRecursively(frame) { + for (var child of frame.childFrames()) + this._removeFramesRecursively(child); + frame._detach(); + this._frames.delete(frame._id); + this.emit(FrameManager.Events.FrameDetached, frame); + } } /** @enum {string} */ FrameManager.Events = { - FrameAttached: 'frameattached', - FrameNavigated: 'framenavigated', - FrameDetached: 'framedetached' + FrameAttached: 'frameattached', + FrameNavigated: 'framenavigated', + FrameDetached: 'framedetached' }; /** * @unrestricted */ class Frame { - /** - * @param {?Frame} parentFrame - * @param {string} frameId - * @param {?Object} payload - */ - constructor(parentFrame, frameId, payload) { - this._parentFrame = parentFrame; - this._url = ''; - this._id = frameId; - this._adoptPayload(payload); + /** + * @param {?Frame} parentFrame + * @param {string} frameId + * @param {?Object} payload + */ + constructor(parentFrame, frameId, payload) { + this._parentFrame = parentFrame; + this._url = ''; + this._id = frameId; + this._adoptPayload(payload); - /** @type {!Set} */ - this._childFrames = new Set(); - if (this._parentFrame) - this._parentFrame._childFrames.add(this); - } + /** @type {!Set} */ + this._childFrames = new Set(); + if (this._parentFrame) + this._parentFrame._childFrames.add(this); + } - /** - * @return {string} - */ - name() { - return this._name || ''; - } + /** + * @return {string} + */ + name() { + return this._name || ''; + } - /** - * @return {string} - */ - url() { - return this._url; - } + /** + * @return {string} + */ + url() { + return this._url; + } - /** - * @return {string} - */ - securityOrigin() { - return this._securityOrigin; - } + /** + * @return {string} + */ + securityOrigin() { + return this._securityOrigin; + } - /** - * @return {?Frame} - */ - parentFrame() { - return this._parentFrame; - } + /** + * @return {?Frame} + */ + parentFrame() { + return this._parentFrame; + } - /** - * @return {!Array.} - */ - childFrames() { - return Array.from(this._childFrames); - } + /** + * @return {!Array.} + */ + childFrames() { + return Array.from(this._childFrames); + } - /** - * @return {boolean} - */ - isMainFrame() { - return !this._detached && !this._parentFrame; - } + /** + * @return {boolean} + */ + isMainFrame() { + return !this._detached && !this._parentFrame; + } - /** - * @return {boolean} - */ - isDetached() { - return this._detached; - } + /** + * @return {boolean} + */ + isDetached() { + return this._detached; + } - /** - * @param {?Object} framePayload - */ - _adoptPayload(framePayload) { - framePayload = framePayload || { - name: '', - url: '', - securityOrigin: '', - mimeType: '' - }; - this._name = framePayload.name; - this._url = framePayload.url; - this._securityOrigin = framePayload.securityOrigin; - this._mimeType = framePayload.mimeType; - } + /** + * @param {?Object} framePayload + */ + _adoptPayload(framePayload) { + framePayload = framePayload || { + name: '', + url: '', + securityOrigin: '', + mimeType: '' + }; + this._name = framePayload.name; + this._url = framePayload.url; + this._securityOrigin = framePayload.securityOrigin; + this._mimeType = framePayload.mimeType; + } - _detach() { - this._detached = true; - if (this._parentFrame) - this._parentFrame._childFrames.delete(this); - this._parentFrame = null; - } + _detach() { + this._detached = true; + if (this._parentFrame) + this._parentFrame._childFrames.delete(this); + this._parentFrame = null; + } } module.exports = FrameManager; diff --git a/lib/Page.js b/lib/Page.js index c3763aee907..b2d04be835d 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -23,548 +23,548 @@ var Dialog = require('./Dialog'); var FrameManager = require('./FrameManager'); class Page extends EventEmitter { - /** - * @param {!Connection} client - * @return {!Promise} - */ - static async create(client) { - await Promise.all([ - client.send('Network.enable', {}), - client.send('Page.enable', {}), - client.send('Runtime.enable', {}), - client.send('Security.enable', {}), - ]); - var expression = helpers.evaluationString(() => window.devicePixelRatio, []); - var {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true }); - var frameManager = await FrameManager.create(client); - var page = new Page(client, frameManager, screenDPI); - // Initialize default page size. - await page.setViewportSize({width: 400, height: 300}); - return page; - } + /** + * @param {!Connection} client + * @return {!Promise} + */ + static async create(client) { + await Promise.all([ + client.send('Network.enable', {}), + client.send('Page.enable', {}), + client.send('Runtime.enable', {}), + client.send('Security.enable', {}), + ]); + var expression = helpers.evaluationString(() => window.devicePixelRatio, []); + var {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true }); + var frameManager = await FrameManager.create(client); + var page = new Page(client, frameManager, screenDPI); + // Initialize default page size. + await page.setViewportSize({width: 400, height: 300}); + return page; + } - /** - * @param {!Connection} client - * @param {!FrameManager} frameManager - * @param {number} screenDPI - */ - constructor(client, frameManager, screenDPI) { - super(); - this._client = client; - this._frameManager = frameManager; - this._screenDPI = screenDPI; - this._extraHeaders = {}; - /** @type {!Map} */ - this._inPageCallbacks = new Map(); - /** @type {?function(!Request)} */ - this._requestInterceptor = null; + /** + * @param {!Connection} client + * @param {!FrameManager} frameManager + * @param {number} screenDPI + */ + constructor(client, frameManager, screenDPI) { + super(); + this._client = client; + this._frameManager = frameManager; + this._screenDPI = screenDPI; + this._extraHeaders = {}; + /** @type {!Map} */ + this._inPageCallbacks = new Map(); + /** @type {?function(!Request)} */ + this._requestInterceptor = null; - this._screenshotTaskChain = Promise.resolve(); + this._screenshotTaskChain = Promise.resolve(); - this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event)); - this._frameManager.on(FrameManager.Events.FrameDetached, event => this.emit(Page.Events.FrameDetached, event)); - this._frameManager.on(FrameManager.Events.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event)); + this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event)); + this._frameManager.on(FrameManager.Events.FrameDetached, event => this.emit(Page.Events.FrameDetached, event)); + this._frameManager.on(FrameManager.Events.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event)); - client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response)); - client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event)); - client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event)); - client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); - client.on('Page.javascriptDialogOpening', event => this._onDialog(event)); - client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)); - } + client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response)); + client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event)); + client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event)); + client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); + client.on('Page.javascriptDialogOpening', event => this._onDialog(event)); + client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)); + } - /** - * @return {!Frame} - */ - mainFrame() { - return this._frameManager.mainFrame(); - } + /** + * @return {!Frame} + */ + mainFrame() { + return this._frameManager.mainFrame(); + } - /** - * @return {!Array} - */ - frames() { - return this._frameManager.frames(); - } + /** + * @return {!Array} + */ + frames() { + return this._frameManager.frames(); + } - /** - * @param {?function(!Request)} interceptor - */ - async setRequestInterceptor(interceptor) { - this._requestInterceptor = interceptor; - await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor}); - } + /** + * @param {?function(!Request)} interceptor + */ + async setRequestInterceptor(interceptor) { + this._requestInterceptor = interceptor; + await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor}); + } - /** - * @param {!Object} event - */ - _onRequestIntercepted(event) { - var request = new Request(this._client, event.InterceptionId, event.request); - this._requestInterceptor(request); - } + /** + * @param {!Object} event + */ + _onRequestIntercepted(event) { + var request = new Request(this._client, event.InterceptionId, event.request); + this._requestInterceptor(request); + } + + /** + * @param {string} url + * @return {!Promise} + */ + async addScriptTag(url) { + return this.evaluate(addScriptTag, url); /** * @param {string} url - * @return {!Promise} */ - async addScriptTag(url) { - return this.evaluate(addScriptTag, url); + function addScriptTag(url) { + var script = document.createElement('script'); + script.src = url; + var promise = new Promise(x => script.onload = x); + document.head.appendChild(script); + return promise; + } + } - /** - * @param {string} url - */ - function addScriptTag(url) { - var script = document.createElement('script'); - script.src = url; - var promise = new Promise(x => script.onload = x); - document.head.appendChild(script); - return promise; + /** + * @param {string} filePath + * @return {!Promise} + */ + async injectFile(filePath) { + var expression = fs.readFileSync(filePath, 'utf8'); + await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + } + + /** + * @param {string} name + * @param {function(?)} callback + */ + async setInPageCallback(name, callback) { + if (this._inPageCallbacks[name]) + throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`); + this._inPageCallbacks[name] = callback; + + var expression = helpers.evaluationString(inPageCallback, [name]); + await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression }); + await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + + function inPageCallback(callbackName) { + window[callbackName] = async(...args) => { + const me = window[callbackName]; + let callbacks = me['callbacks']; + if (!callbacks) { + callbacks = new Map(); + me['callbacks'] = callbacks; } + const seq = (me['lastSeq'] || 0) + 1; + me['lastSeq'] = seq; + const promise = new Promise(fulfill => callbacks.set(seq, fulfill)); + // eslint-disable-next-line no-console + console.debug('driver:InPageCallback', JSON.stringify({name: callbackName, seq, args})); + return promise; + }; + } + } + + /** + * @param {!Object} headers + * @return {!Promise} + */ + async setExtraHTTPHeaders(headers) { + this._extraHeaders = {}; + // Note: header names are case-insensitive. + for (var key of Object.keys(headers)) + this._extraHeaders[key.toLowerCase()] = headers[key]; + return this._client.send('Network.setExtraHTTPHeaders', { headers }); + } + + /** + * @return {!Object} + */ + extraHTTPHeaders() { + return Object.assign({}, this._extraHeaders); + } + + /** + * @param {string} userAgent + * @return {!Promise} + */ + async setUserAgentOverride(userAgent) { + this._userAgent = userAgent; + return this._client.send('Network.setUserAgentOverride', { userAgent }); + } + + /** + * @return {string} + */ + userAgentOverride() { + return this._userAgent; + } + + /** + * @param {!Object} exceptionDetails + */ + async _handleException(exceptionDetails) { + var message = await this._getExceptionMessage(exceptionDetails); + this.emit(Page.Events.Error, new Error(message)); + } + + async _onConsoleAPI(event) { + if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:InPageCallback') { + var {name, seq, args} = JSON.parse(event.args[1].value); + var result = await this._inPageCallbacks[name](...args); + var expression = helpers.evaluationString(deliverResult, [name, seq, result]); + this._client.send('Runtime.evaluate', { expression }); + + function deliverResult(name, seq, result) { + window[name]['callbacks'].get(seq)(result); + window[name]['callbacks'].delete(seq); + } + return; + } + var values = event.args.map(arg => arg.value || arg.description || ''); + this.emit(Page.Events.ConsoleMessage, values.join(' ')); + } + + _onDialog(event) { + var dialogType = null; + if (event.type === 'alert') + dialogType = Dialog.Type.Alert; + else if (event.type === 'confirm') + dialogType = Dialog.Type.Confirm; + else if (event.type === 'prompt') + dialogType = Dialog.Type.Prompt; + else if (event.type === 'beforeunload') + dialogType = Dialog.Type.BeforeUnload; + console.assert(dialogType, 'Unknown javascript dialog type: ' + event.type); + var dialog = new Dialog(this._client, dialogType, event.message); + this.emit(Page.Events.Dialog, dialog); + } + + /** + * @return {!Promise} + */ + async url() { + return this.evaluate(function() { + return window.location.href; + }); + } + + /** + * @param {string} html + * @return {!Promise} + */ + async setContent(html) { + var resourceTree = await this._client.send('Page.getResourceTree', {}); + await this._client.send('Page.setDocumentContent', { + frameId: resourceTree.frameTree.frame.id, + html: html + }); + } + + /** + * @param {string} html + * @return {!Promise} + */ + async navigate(url) { + var loadPromise = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)).then(() => true); + var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false); + var referrer = this._extraHeaders.referer; + // Await for the command to throw exception in case of illegal arguments. + try { + await this._client.send('Page.navigate', {url, referrer}); + } catch (e) { + return false; + } + return await Promise.race([loadPromise, interstitialPromise]); + } + + /** + * @param {!{width: number, height: number}} size + * @return {!Promise} + */ + async setViewportSize(size) { + this._size = size; + var width = size.width; + var height = size.height; + var zoom = this._screenDPI; + return Promise.all([ + this._client.send('Emulation.setDeviceMetricsOverride', { + width, + height, + deviceScaleFactor: 1, + scale: 1 / zoom, + mobile: false, + fitWindow: false + }), + this._client.send('Emulation.setVisibleSize', { + width: width / zoom, + height: height / zoom, + }) + ]); + } + + /** + * @return {!{width: number, height: number}} + */ + viewportSize() { + return this._size; + } + + /** + * @param {function()} fun + * @param {!Array<*>} args + * @return {!Promise<(!Object|undefined)>} + */ + async evaluate(fun, ...args) { + var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); + var response = await this._client.send('Runtime.evaluate', { + expression: code + }); + if (response.exceptionDetails) { + var message = await this._getExceptionMessage(response.exceptionDetails); + throw new Error('Evaluation failed: ' + message); } - /** - * @param {string} filePath - * @return {!Promise} - */ - async injectFile(filePath) { - var expression = fs.readFileSync(filePath, 'utf8'); - await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + var remoteObject = response.result; + if (remoteObject.type !== 'object') + return remoteObject.value; + var isPromise = remoteObject.type === 'object' && remoteObject.subtype === 'promise'; + var response = await this._client.send('Runtime.callFunctionOn', { + objectId: remoteObject.objectId, + functionDeclaration: 'function() { return this; }', + returnByValue: true, + awaitPromise: isPromise + }); + await this._client.send('Runtime.releaseObject', { + objectId: remoteObject.objectId + }); + if (response.exceptionDetails) { + var message = await this._getExceptionMessage(response.exceptionDetails); + throw new Error('Evaluation failed with ' + message); } - /** - * @param {string} name - * @param {function(?)} callback - */ - async setInPageCallback(name, callback) { - if (this._inPageCallbacks[name]) - throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`); - this._inPageCallbacks[name] = callback; + return response.result.value; + } - var expression = helpers.evaluationString(inPageCallback, [name]); - await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression }); - await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + /** + * @param {!Object} exceptionDetails + * @return {string} + */ + async _getExceptionMessage(exceptionDetails) { + var message = ''; + var exception = exceptionDetails.exception; + if (exception) { + var response = await this._client.send('Runtime.callFunctionOn', { + objectId: exception.objectId, + functionDeclaration: 'function() { return this.message; }', + returnByValue: true, + }); + message = response.result.value; + } else { + message = exceptionDetails.text; + } + if (exceptionDetails.stackTrace) { + for (var callframe of exceptionDetails.stackTrace.callFrames) { + var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; + var functionName = callframe.functionName || ''; + message += `\n at ${functionName} (${location})`; + } + } + return message; + } - function inPageCallback(callbackName) { - window[callbackName] = async(...args) => { - const me = window[callbackName]; - let callbacks = me['callbacks']; - if (!callbacks) { - callbacks = new Map(); - me['callbacks'] = callbacks; - } - const seq = (me['lastSeq'] || 0) + 1; - me['lastSeq'] = seq; - const promise = new Promise(fulfill => callbacks.set(seq, fulfill)); - // eslint-disable-next-line no-console - console.debug('driver:InPageCallback', JSON.stringify({name: callbackName, seq, args})); - return promise; - }; - } + /** + * @param {function()} fun + * @param {!Array<*>} args + * @return {!Promise} + */ + async evaluateOnInitialized(fun, ...args) { + var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); + await this._client.send('Page.addScriptToEvaluateOnLoad', { + scriptSource: code + }); + } + + /** + * @param {!Object=} options + * @return {!Promise} + */ + async screenshot(options) { + options = options || {}; + var screenshotType = null; + if (options.path) { + var mimeType = mime.lookup(options.path); + if (mimeType === 'image/png') + screenshotType = 'png'; + else if (mimeType === 'image/jpeg') + screenshotType = 'jpeg'; + console.assert(screenshotType, 'Unsupported screenshot mime type: ' + mimeType); + } + if (options.type) { + console.assert(!screenshotType || options.type === screenshotType, `Passed screenshot type '${options.type}' does not match the type inferred from the file path: '${screenshotType}'`); + console.assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type); + screenshotType = options.type; + } + if (!screenshotType) + screenshotType = 'png'; + + if (options.quality) { + console.assert(screenshotType === 'jpeg', 'options.quality is unsupported for the ' + screenshotType + ' screenshots'); + console.assert(typeof options.quality === 'number', 'Expected options.quality to be a number but found ' + (typeof options.quality)); + console.assert(Number.isInteger(options.quality), 'Expected options.quality to be an integer'); + console.assert(options.quality >= 0 && options.quality <= 100, 'Expected options.quality to be between 0 and 100 (inclusive), got ' + options.quality); + } + console.assert(!options.clip || !options.fullPage, 'options.clip and options.fullPage are exclusive'); + if (options.clip) { + console.assert(typeof options.clip.x === 'number', 'Expected options.clip.x to be a number but found ' + (typeof options.clip.x)); + console.assert(typeof options.clip.y === 'number', 'Expected options.clip.y to be a number but found ' + (typeof options.clip.y)); + console.assert(typeof options.clip.width === 'number', 'Expected options.clip.width to be a number but found ' + (typeof options.clip.width)); + console.assert(typeof options.clip.height === 'number', 'Expected options.clip.height to be a number but found ' + (typeof options.clip.height)); + } + this._screenshotTaskChain = this._screenshotTaskChain.then(this._screenshotTask.bind(this, screenshotType, options)); + return this._screenshotTaskChain; + } + + /** + * @param {string} screenshotType + * @param {!Object=} options + * @return {!Promise} + */ + async _screenshotTask(screenshotType, options) { + if (options.clip) { + await Promise.all([ + this._client.send('Emulation.setVisibleSize', { + width: Math.ceil(options.clip.width / this._screenDPI), + height: Math.ceil(options.clip.height / this._screenDPI), + }), + this._client.send('Emulation.forceViewport', { + x: options.clip.x / this._screenDPI, + y: options.clip.y / this._screenDPI, + scale: 1, + }) + ]); + } else if (options.fullPage) { + var response = await this._client.send('Page.getLayoutMetrics'); + await Promise.all([ + this._client.send('Emulation.setVisibleSize', { + width: Math.ceil(response.contentSize.width / this._screenDPI), + height: Math.ceil(response.contentSize.height / this._screenDPI), + }), + this._client.send('Emulation.forceViewport', { + x: 0, + y: 0, + scale: 1, + }) + ]); + } + var result = await this._client.send('Page.captureScreenshot', { + fromSurface: true, + format: screenshotType, + quality: options.quality + }); + if (options.clip || options.fullPage) { + await Promise.all([ + this.setViewportSize(this.viewportSize()), + this._client.send('Emulation.resetViewport') + ]); + } + var buffer = new Buffer(result.data, 'base64'); + if (options.path) + fs.writeFileSync(options.path, buffer); + return buffer; + } + + /** + * @param {string} filePath + * @param {!Object=} options + * @return {!Promise} + */ + async printToPDF(filePath, options) { + options = options || {}; + + var scale = options.scale || 1; + var displayHeaderFooter = options.displayHeaderFooter || false; + var printBackground = options.printBackground || true; + var landscape = options.landscape || false; + var pageRanges = options.pageRanges || ''; + + var paperWidth = 8.5; + var paperHeight = 11; + if (options.format) { + var format = Page.PaperFormats[options.format]; + console.assert(format, 'Unknown paper format: ' + options.format); + paperWidth = format.width; + paperHeight = format.height; + } else { + paperWidth = convertPrintParameterToInches(options.width) || paperWidth; + paperHeight = convertPrintParameterToInches(options.height) || paperHeight; } - /** - * @param {!Object} headers - * @return {!Promise} - */ - async setExtraHTTPHeaders(headers) { - this._extraHeaders = {}; - // Note: header names are case-insensitive. - for (var key of Object.keys(headers)) - this._extraHeaders[key.toLowerCase()] = headers[key]; - return this._client.send('Network.setExtraHTTPHeaders', { headers }); - } + var marginOptions = options.margin || {}; + var marginTop = convertPrintParameterToInches(marginOptions.top) || 0; + var marginLeft = convertPrintParameterToInches(marginOptions.left) || 0; + var marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0; + var marginRight = convertPrintParameterToInches(marginOptions.right) || 0; - /** - * @return {!Object} - */ - extraHTTPHeaders() { - return Object.assign({}, this._extraHeaders); - } + var result = await this._client.send('Page.printToPDF', { + landscape: landscape, + displayHeaderFooter: displayHeaderFooter, + printBackground: printBackground, + scale: scale, + paperWidth: paperWidth, + paperHeight: paperHeight, + marginTop: marginTop, + marginBottom: marginBottom, + marginLeft: marginLeft, + marginRight: marginRight, + pageRanges: pageRanges + }); + var buffer = new Buffer(result.data, 'base64'); + fs.writeFileSync(filePath, buffer); + } - /** - * @param {string} userAgent - * @return {!Promise} - */ - async setUserAgentOverride(userAgent) { - this._userAgent = userAgent; - return this._client.send('Network.setUserAgentOverride', { userAgent }); - } + /** + * @return {!Promise} + */ + async plainText() { + return this.evaluate(function() { + return document.body.innerText; + }); + } - /** - * @return {string} - */ - userAgentOverride() { - return this._userAgent; - } + /** + * @return {!Promise} + */ + async title() { + return this.evaluate(function() { + return document.title; + }); + } - /** - * @param {!Object} exceptionDetails - */ - async _handleException(exceptionDetails) { - var message = await this._getExceptionMessage(exceptionDetails); - this.emit(Page.Events.Error, new Error(message)); - } - - async _onConsoleAPI(event) { - if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:InPageCallback') { - var {name, seq, args} = JSON.parse(event.args[1].value); - var result = await this._inPageCallbacks[name](...args); - var expression = helpers.evaluationString(deliverResult, [name, seq, result]); - this._client.send('Runtime.evaluate', { expression }); - - function deliverResult(name, seq, result) { - window[name]['callbacks'].get(seq)(result); - window[name]['callbacks'].delete(seq); - } - return; - } - var values = event.args.map(arg => arg.value || arg.description || ''); - this.emit(Page.Events.ConsoleMessage, values.join(' ')); - } - - _onDialog(event) { - var dialogType = null; - if (event.type === 'alert') - dialogType = Dialog.Type.Alert; - else if (event.type === 'confirm') - dialogType = Dialog.Type.Confirm; - else if (event.type === 'prompt') - dialogType = Dialog.Type.Prompt; - else if (event.type === 'beforeunload') - dialogType = Dialog.Type.BeforeUnload; - console.assert(dialogType, 'Unknown javascript dialog type: ' + event.type); - var dialog = new Dialog(this._client, dialogType, event.message); - this.emit(Page.Events.Dialog, dialog); - } - - /** - * @return {!Promise} - */ - async url() { - return this.evaluate(function() { - return window.location.href; - }); - } - - /** - * @param {string} html - * @return {!Promise} - */ - async setContent(html) { - var resourceTree = await this._client.send('Page.getResourceTree', {}); - await this._client.send('Page.setDocumentContent', { - frameId: resourceTree.frameTree.frame.id, - html: html - }); - } - - /** - * @param {string} html - * @return {!Promise} - */ - async navigate(url) { - var loadPromise = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)).then(() => true); - var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false); - var referrer = this._extraHeaders.referer; - // Await for the command to throw exception in case of illegal arguments. - try { - await this._client.send('Page.navigate', {url, referrer}); - } catch (e) { - return false; - } - return await Promise.race([loadPromise, interstitialPromise]); - } - - /** - * @param {!{width: number, height: number}} size - * @return {!Promise} - */ - async setViewportSize(size) { - this._size = size; - var width = size.width; - var height = size.height; - var zoom = this._screenDPI; - return Promise.all([ - this._client.send('Emulation.setDeviceMetricsOverride', { - width, - height, - deviceScaleFactor: 1, - scale: 1 / zoom, - mobile: false, - fitWindow: false - }), - this._client.send('Emulation.setVisibleSize', { - width: width / zoom, - height: height / zoom, - }) - ]); - } - - /** - * @return {!{width: number, height: number}} - */ - viewportSize() { - return this._size; - } - - /** - * @param {function()} fun - * @param {!Array<*>} args - * @return {!Promise<(!Object|undefined)>} - */ - async evaluate(fun, ...args) { - var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); - var response = await this._client.send('Runtime.evaluate', { - expression: code - }); - if (response.exceptionDetails) { - var message = await this._getExceptionMessage(response.exceptionDetails); - throw new Error('Evaluation failed: ' + message); - } - - var remoteObject = response.result; - if (remoteObject.type !== 'object') - return remoteObject.value; - var isPromise = remoteObject.type === 'object' && remoteObject.subtype === 'promise'; - var response = await this._client.send('Runtime.callFunctionOn', { - objectId: remoteObject.objectId, - functionDeclaration: 'function() { return this; }', - returnByValue: true, - awaitPromise: isPromise - }); - await this._client.send('Runtime.releaseObject', { - objectId: remoteObject.objectId - }); - if (response.exceptionDetails) { - var message = await this._getExceptionMessage(response.exceptionDetails); - throw new Error('Evaluation failed with ' + message); - } - - return response.result.value; - } - - /** - * @param {!Object} exceptionDetails - * @return {string} - */ - async _getExceptionMessage(exceptionDetails) { - var message = ''; - var exception = exceptionDetails.exception; - if (exception) { - var response = await this._client.send('Runtime.callFunctionOn', { - objectId: exception.objectId, - functionDeclaration: 'function() { return this.message; }', - returnByValue: true, - }); - message = response.result.value; - } else { - message = exceptionDetails.text; - } - if (exceptionDetails.stackTrace) { - for (var callframe of exceptionDetails.stackTrace.callFrames) { - var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; - var functionName = callframe.functionName || ''; - message += `\n at ${functionName} (${location})`; - } - } - return message; - } - - /** - * @param {function()} fun - * @param {!Array<*>} args - * @return {!Promise} - */ - async evaluateOnInitialized(fun, ...args) { - var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); - await this._client.send('Page.addScriptToEvaluateOnLoad', { - scriptSource: code - }); - } - - /** - * @param {!Object=} options - * @return {!Promise} - */ - async screenshot(options) { - options = options || {}; - var screenshotType = null; - if (options.path) { - var mimeType = mime.lookup(options.path); - if (mimeType === 'image/png') - screenshotType = 'png'; - else if (mimeType === 'image/jpeg') - screenshotType = 'jpeg'; - console.assert(screenshotType, 'Unsupported screenshot mime type: ' + mimeType); - } - if (options.type) { - console.assert(!screenshotType || options.type === screenshotType, `Passed screenshot type '${options.type}' does not match the type inferred from the file path: '${screenshotType}'`); - console.assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type); - screenshotType = options.type; - } - if (!screenshotType) - screenshotType = 'png'; - - if (options.quality) { - console.assert(screenshotType === 'jpeg', 'options.quality is unsupported for the ' + screenshotType + ' screenshots'); - console.assert(typeof options.quality === 'number', 'Expected options.quality to be a number but found ' + (typeof options.quality)); - console.assert(Number.isInteger(options.quality), 'Expected options.quality to be an integer'); - console.assert(options.quality >= 0 && options.quality <= 100, 'Expected options.quality to be between 0 and 100 (inclusive), got ' + options.quality); - } - console.assert(!options.clip || !options.fullPage, 'options.clip and options.fullPage are exclusive'); - if (options.clip) { - console.assert(typeof options.clip.x === 'number', 'Expected options.clip.x to be a number but found ' + (typeof options.clip.x)); - console.assert(typeof options.clip.y === 'number', 'Expected options.clip.y to be a number but found ' + (typeof options.clip.y)); - console.assert(typeof options.clip.width === 'number', 'Expected options.clip.width to be a number but found ' + (typeof options.clip.width)); - console.assert(typeof options.clip.height === 'number', 'Expected options.clip.height to be a number but found ' + (typeof options.clip.height)); - } - this._screenshotTaskChain = this._screenshotTaskChain.then(this._screenshotTask.bind(this, screenshotType, options)); - return this._screenshotTaskChain; - } - - /** - * @param {string} screenshotType - * @param {!Object=} options - * @return {!Promise} - */ - async _screenshotTask(screenshotType, options) { - if (options.clip) { - await Promise.all([ - this._client.send('Emulation.setVisibleSize', { - width: Math.ceil(options.clip.width / this._screenDPI), - height: Math.ceil(options.clip.height / this._screenDPI), - }), - this._client.send('Emulation.forceViewport', { - x: options.clip.x / this._screenDPI, - y: options.clip.y / this._screenDPI, - scale: 1, - }) - ]); - } else if (options.fullPage) { - var response = await this._client.send('Page.getLayoutMetrics'); - await Promise.all([ - this._client.send('Emulation.setVisibleSize', { - width: Math.ceil(response.contentSize.width / this._screenDPI), - height: Math.ceil(response.contentSize.height / this._screenDPI), - }), - this._client.send('Emulation.forceViewport', { - x: 0, - y: 0, - scale: 1, - }) - ]); - } - var result = await this._client.send('Page.captureScreenshot', { - fromSurface: true, - format: screenshotType, - quality: options.quality - }); - if (options.clip || options.fullPage) { - await Promise.all([ - this.setViewportSize(this.viewportSize()), - this._client.send('Emulation.resetViewport') - ]); - } - var buffer = new Buffer(result.data, 'base64'); - if (options.path) - fs.writeFileSync(options.path, buffer); - return buffer; - } - - /** - * @param {string} filePath - * @param {!Object=} options - * @return {!Promise} - */ - async printToPDF(filePath, options) { - options = options || {}; - - var scale = options.scale || 1; - var displayHeaderFooter = options.displayHeaderFooter || false; - var printBackground = options.printBackground || true; - var landscape = options.landscape || false; - var pageRanges = options.pageRanges || ''; - - var paperWidth = 8.5; - var paperHeight = 11; - if (options.format) { - var format = Page.PaperFormats[options.format]; - console.assert(format, 'Unknown paper format: ' + options.format); - paperWidth = format.width; - paperHeight = format.height; - } else { - paperWidth = convertPrintParameterToInches(options.width) || paperWidth; - paperHeight = convertPrintParameterToInches(options.height) || paperHeight; - } - - var marginOptions = options.margin || {}; - var marginTop = convertPrintParameterToInches(marginOptions.top) || 0; - var marginLeft = convertPrintParameterToInches(marginOptions.left) || 0; - var marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0; - var marginRight = convertPrintParameterToInches(marginOptions.right) || 0; - - var result = await this._client.send('Page.printToPDF', { - landscape: landscape, - displayHeaderFooter: displayHeaderFooter, - printBackground: printBackground, - scale: scale, - paperWidth: paperWidth, - paperHeight: paperHeight, - marginTop: marginTop, - marginBottom: marginBottom, - marginLeft: marginLeft, - marginRight: marginRight, - pageRanges: pageRanges - }); - var buffer = new Buffer(result.data, 'base64'); - fs.writeFileSync(filePath, buffer); - } - - /** - * @return {!Promise} - */ - async plainText() { - return this.evaluate(function() { - return document.body.innerText; - }); - } - - /** - * @return {!Promise} - */ - async title() { - return this.evaluate(function() { - return document.title; - }); - } - - /** - * @return {!Promise} - */ - async close() { - await this._client.dispose(); - } + /** + * @return {!Promise} + */ + async close() { + await this._client.dispose(); + } } /** @enum {string} */ Page.PaperFormats = { - Letter: {width: 8.5, height: 11}, - Legal: {width: 8.5, height: 14}, - Tabloid: {width: 11, height: 17}, - Ledger: {width: 17, height: 11}, - A0: {width: 33.1, height: 46.8 }, - A1: {width: 23.4, height: 33.1 }, - A2: {width: 16.5, height: 23.4 }, - A3: {width: 11.7, height: 16.5 }, - A4: {width: 8.27, height: 11.7 }, - A5: {width: 5.83, height: 8.27 }, + Letter: {width: 8.5, height: 11}, + Legal: {width: 8.5, height: 14}, + Tabloid: {width: 11, height: 17}, + Ledger: {width: 17, height: 11}, + A0: {width: 33.1, height: 46.8 }, + A1: {width: 23.4, height: 33.1 }, + A2: {width: 16.5, height: 23.4 }, + A3: {width: 11.7, height: 16.5 }, + A4: {width: 8.27, height: 11.7 }, + A5: {width: 5.83, height: 8.27 }, }; var unitToPixels = { - 'px': 1, - 'in': 96, - 'cm': 37.8, - 'mm': 3.78 + 'px': 1, + 'in': 96, + 'cm': 37.8, + 'mm': 3.78 }; /** @@ -572,42 +572,42 @@ var unitToPixels = { * @return {(number|undefined)} */ function convertPrintParameterToInches(parameter) { - if (typeof parameter === 'undefined') - return undefined; - var pixels; - if (typeof parameter === 'number') { - // Treat numbers as pixel values to be aligned with phantom's paperSize. - pixels = /** @type {number} */ (parameter); - } else if (typeof parameter === 'string') { - var text = parameter; - var unit = text.substring(text.length - 2).toLowerCase(); - var valueText = ''; - if (unitToPixels.hasOwnProperty(unit)) { - valueText = text.substring(0, text.length - 2); - } else { - // In case of unknown unit try to parse the whole parameter as number of pixels. - // This is consistent with phantom's paperSize behavior. - unit = 'px'; - valueText = text; - } - var value = Number(valueText); - console.assert(!isNaN(value), 'Failed to parse parameter value: ' + text); - pixels = value * unitToPixels[unit]; + if (typeof parameter === 'undefined') + return undefined; + var pixels; + if (typeof parameter === 'number') { + // Treat numbers as pixel values to be aligned with phantom's paperSize. + pixels = /** @type {number} */ (parameter); + } else if (typeof parameter === 'string') { + var text = parameter; + var unit = text.substring(text.length - 2).toLowerCase(); + var valueText = ''; + if (unitToPixels.hasOwnProperty(unit)) { + valueText = text.substring(0, text.length - 2); } else { - throw new Error('printToPDF Cannot handle parameter type: ' + (typeof parameter)); + // In case of unknown unit try to parse the whole parameter as number of pixels. + // This is consistent with phantom's paperSize behavior. + unit = 'px'; + valueText = text; } - return pixels / 96; + var value = Number(valueText); + console.assert(!isNaN(value), 'Failed to parse parameter value: ' + text); + pixels = value * unitToPixels[unit]; + } else { + throw new Error('printToPDF Cannot handle parameter type: ' + (typeof parameter)); + } + return pixels / 96; } Page.Events = { - ConsoleMessage: 'consolemessage', - Dialog: 'dialog', - Error: 'error', - ResourceLoadingFailed: 'resourceloadingfailed', - ResponseReceived: 'responsereceived', - FrameAttached: 'frameattached', - FrameDetached: 'framedetached', - FrameNavigated: 'framenavigated', + ConsoleMessage: 'consolemessage', + Dialog: 'dialog', + Error: 'error', + ResourceLoadingFailed: 'resourceloadingfailed', + ResponseReceived: 'responsereceived', + FrameAttached: 'frameattached', + FrameDetached: 'framedetached', + FrameNavigated: 'framenavigated', }; module.exports = Page; diff --git a/lib/Request.js b/lib/Request.js index f8797a9dcba..e74fc828f77 100644 --- a/lib/Request.js +++ b/lib/Request.js @@ -15,112 +15,112 @@ */ class Request { - /** - * @param {!Connection} client - * @param {string} interceptionId - * @param {!Object} payload - */ - constructor(client, interceptionId, payload) { - this._client = client; - this._interceptionId = interceptionId; - this._url = payload.url; - this._method = payload.method; - this._headers = payload.headers; - this._postData = payload.postData; + /** + * @param {!Connection} client + * @param {string} interceptionId + * @param {!Object} payload + */ + constructor(client, interceptionId, payload) { + this._client = client; + this._interceptionId = interceptionId; + this._url = payload.url; + this._method = payload.method; + this._headers = payload.headers; + this._postData = payload.postData; - this._urlOverride = undefined; - this._methodOverride = undefined; - this._postDataOverride = undefined; + this._urlOverride = undefined; + this._methodOverride = undefined; + this._postDataOverride = undefined; - this._handled = false; - } + this._handled = false; + } - /** - * @return {string} - */ - url() { - return this._urlOverride || this._url; - } + /** + * @return {string} + */ + url() { + return this._urlOverride || this._url; + } - /** - * @param {string} url - */ - setUrl(url) { - this._urlOverride = url; - } + /** + * @param {string} url + */ + setUrl(url) { + this._urlOverride = url; + } - /** - * @return {string} - */ - method() { - return this._methodOverride || this._method; - } + /** + * @return {string} + */ + method() { + return this._methodOverride || this._method; + } - /** - * @param {string} method - */ - setMethod(method) { - this._methodOverride = method; - } + /** + * @param {string} method + */ + setMethod(method) { + this._methodOverride = method; + } - /** - * @return {!Object} - */ - headers() { - return Object.assign({}, this._headersOverride || this._headers); - } + /** + * @return {!Object} + */ + headers() { + return Object.assign({}, this._headersOverride || this._headers); + } - /** - * @param {string} key - * @param {string} value - */ - setHeader(key, value) { - if (!this._headersOverride) - this._headersOverride = Object.assign({}, this._headers); - this._headersOverride[key] = value; - } + /** + * @param {string} key + * @param {string} value + */ + setHeader(key, value) { + if (!this._headersOverride) + this._headersOverride = Object.assign({}, this._headers); + this._headersOverride[key] = value; + } - /** - * @return {(string|undefined)} - */ - postData() { - return this._postDataOverride || this._postData; - } + /** + * @return {(string|undefined)} + */ + postData() { + return this._postDataOverride || this._postData; + } - /** - * @return {(string|undefined)} - */ - setPostData(data) { - this._postDataOverride = data; - } + /** + * @return {(string|undefined)} + */ + setPostData(data) { + this._postDataOverride = data; + } - abort() { - console.assert(!this._handled, 'This request is already handled!'); - this._handled = true; - this._client.send('Network.continueInterceptedRequest', { - interceptionId: this._interceptionId, - errorReason: 'Aborted' - }); - } + abort() { + console.assert(!this._handled, 'This request is already handled!'); + this._handled = true; + this._client.send('Network.continueInterceptedRequest', { + interceptionId: this._interceptionId, + errorReason: 'Aborted' + }); + } - continue() { - console.assert(!this._handled, 'This request is already handled!'); - this._handled = true; - this._client.send('Network.continueInterceptedRequest', { - interceptionId: this._interceptionId, - url: this._urlOverride, - method: this._methodOverride, - postData: this._postDataOverride, - headers: this._headersOverride - }); - } + continue() { + console.assert(!this._handled, 'This request is already handled!'); + this._handled = true; + this._client.send('Network.continueInterceptedRequest', { + interceptionId: this._interceptionId, + url: this._urlOverride, + method: this._methodOverride, + postData: this._postDataOverride, + headers: this._headersOverride + }); + } - /** - * @return {boolean} - */ - handled() { - return this._handled; - } + /** + * @return {boolean} + */ + handled() { + return this._handled; + } } module.exports = Request; diff --git a/lib/helpers.js b/lib/helpers.js index af0ab1c78b2..d6af1ad0f67 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -15,21 +15,20 @@ */ module.exports = { - - /** - * @param {function()} fun - * @param {!Array<*>} args - * @param {boolean=} wrapInPromise - * @param {string=} sourceURL - * @return {string} - */ - evaluationString: function(fun, args, wrapInPromise, sourceURL) { - var argsString = args.map(x => JSON.stringify(x)).join(','); - var code = `(${fun.toString()})(${argsString})`; - if (wrapInPromise) - code = `Promise.resolve(${code})`; - if (sourceURL) - code += `\n//# sourceURL=${sourceURL}`; - return code; - } + /** + * @param {function()} fun + * @param {!Array<*>} args + * @param {boolean=} wrapInPromise + * @param {string=} sourceURL + * @return {string} + */ + evaluationString: function(fun, args, wrapInPromise, sourceURL) { + var argsString = args.map(x => JSON.stringify(x)).join(','); + var code = `(${fun.toString()})(${argsString})`; + if (wrapInPromise) + code = `Promise.resolve(${code})`; + if (sourceURL) + code += `\n//# sourceURL=${sourceURL}`; + return code; + } }; diff --git a/package-lock.json b/package-lock.json index ebdaf885b93..4306d1f20be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,6 +3,86 @@ "version": "0.0.1", "lockfileVersion": 1, "dependencies": { + "acorn": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", + "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", + "integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "babel-code-frame": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "dev": true + }, "balanced-match": { "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" @@ -16,6 +96,48 @@ "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=" }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true + }, + "circular-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", + "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true + }, + "cli-width": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "concat-map": { "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" @@ -37,27 +159,493 @@ "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "dev": true, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.0.0.tgz", + "integrity": "sha1-cnfAFDf99B3M0WjVqg5Jt1yh8mA=", + "dev": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "readable-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.1.tgz", + "integrity": "sha512-u6cxIvtbZcjq2HH71Zc/SRBUl7vbv62szIqmqqGpK3HY5J1c0kR/LUzKUpeoFgMzapvVAlBD+QY56ilWmHi4Nw==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", + "dev": true + }, + "string_decoder": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", + "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", + "dev": true, + "dependencies": { + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "dev": true + } + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true + }, + "espree": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz", + "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=", + "dev": true + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "exit": { "version": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "external-editor": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", + "integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=", + "dev": true + }, "extract-zip": { "version": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fd-slicer": { "version": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=" }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true + }, + "flat-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "dev": true + }, "fs.realpath": { "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true + }, "glob": { "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=" }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==", + "dev": true + }, + "ignore": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", + "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" @@ -66,6 +654,60 @@ "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "inquirer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz", + "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-my-json-valid": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true + }, + "is-path-inside": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true + }, "isarray": { "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" @@ -80,10 +722,64 @@ "integrity": "sha1-RQcpUOSkKx4yL+VcABEApGXXeBU=", "dev": true }, + "js-tokens": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", + "dev": true + }, + "js-yaml": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", + "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=", + "dev": true + }, + "jschardet": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.4.2.tgz", + "integrity": "sha1-KqEH8UKvQSHRRWWdRPUIMJYeaZo=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, "mime": { "version": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, "minimatch": { "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" @@ -107,40 +803,112 @@ "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "ncp": { "version": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "once": { "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "path-is-absolute": { "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, "pend": { "version": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true + }, "pixelmatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", "dev": true }, + "pluralize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-4.0.0.tgz", + "integrity": "sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=", + "dev": true + }, "pngjs": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.2.0.tgz", "integrity": "sha1-/J/OoaijddpUpRFIAZ1avUHbq94=", "dev": true }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "process-nextick-args": { "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" @@ -153,24 +921,138 @@ "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", "integrity": "sha1-B5azH412iAB/8Lk6gIjTSqF8D3I=" }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true + }, "rimraf": { "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=" }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true + }, "safe-buffer": { "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "string_decoder": { "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=" }, + "string-width": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", + "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz", + "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=", + "dev": true + }, "text-diff": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/text-diff/-/text-diff-1.0.1.tgz", "integrity": "sha1-bBBZBUNeM3hXN1ydL2ymPkU/9WU=", "dev": true }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true + }, "typedarray": { "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" @@ -184,15 +1066,47 @@ "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "wrappy": { "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true + } + } + }, "ws": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ws/-/ws-3.0.0.tgz", "integrity": "sha1-mN2wAFbIOQy3Ued4h4hJf5kQO2w=" }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, "yauzl": { "version": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=" diff --git a/phantom_shim/FileSystem.js b/phantom_shim/FileSystem.js index f25cf7f72a7..f96ddfdbc3d 100644 --- a/phantom_shim/FileSystem.js +++ b/phantom_shim/FileSystem.js @@ -21,355 +21,355 @@ var removeRecursive = require('rimraf').sync; var copyRecursive = deasync(require('ncp').ncp); class FileSystem { - constructor() { - this.separator = path.sep; - } + constructor() { + this.separator = path.sep; + } - /** - * @return {string} - */ - get workingDirectory() { - return process.cwd(); - } + /** + * @return {string} + */ + get workingDirectory() { + return process.cwd(); + } - /** - * @param {string} directoryPath - */ - changeWorkingDirectory(directoryPath) { - try { - process.chdir(directoryPath); - return true; - } catch (e){ - return false; - } + /** + * @param {string} directoryPath + */ + changeWorkingDirectory(directoryPath) { + try { + process.chdir(directoryPath); + return true; + } catch (e){ + return false; } + } - /** - * @param {string} relativePath - * @return {string} - */ - absolute(relativePath) { - relativePath = path.normalize(relativePath); - if (path.isAbsolute(relativePath)) - return relativePath; - return path.resolve(path.join(process.cwd(), relativePath)); - } + /** + * @param {string} relativePath + * @return {string} + */ + absolute(relativePath) { + relativePath = path.normalize(relativePath); + if (path.isAbsolute(relativePath)) + return relativePath; + return path.resolve(path.join(process.cwd(), relativePath)); + } - /** - * @param {string} filePath - * @return {boolean} - */ - exists(filePath) { - return fs.existsSync(filePath); - } + /** + * @param {string} filePath + * @return {boolean} + */ + exists(filePath) { + return fs.existsSync(filePath); + } - /** - * @param {string} fromPath - * @param {string} toPath - */ - copy(fromPath, toPath) { - var content = fs.readFileSync(fromPath); - fs.writeFileSync(toPath, content); - } + /** + * @param {string} fromPath + * @param {string} toPath + */ + copy(fromPath, toPath) { + var content = fs.readFileSync(fromPath); + fs.writeFileSync(toPath, content); + } - /** - * @param {string} fromPath - * @param {string} toPath - */ - move(fromPath, toPath) { - var content = fs.readFileSync(fromPath); - fs.writeFileSync(toPath, content); - fs.unlinkSync(fromPath); - } + /** + * @param {string} fromPath + * @param {string} toPath + */ + move(fromPath, toPath) { + var content = fs.readFileSync(fromPath); + fs.writeFileSync(toPath, content); + fs.unlinkSync(fromPath); + } - /** - * @param {string} filePath - * @return {number} - */ - size(filePath) { - return fs.statSync(filePath).size; - } + /** + * @param {string} filePath + * @return {number} + */ + size(filePath) { + return fs.statSync(filePath).size; + } - /** - * @param {string} filePath - */ - touch(filePath) { - fs.closeSync(fs.openSync(filePath, 'a')); - } + /** + * @param {string} filePath + */ + touch(filePath) { + fs.closeSync(fs.openSync(filePath, 'a')); + } - /** - * @param {string} filePath - */ - remove(filePath) { - fs.unlinkSync(filePath); - } + /** + * @param {string} filePath + */ + remove(filePath) { + fs.unlinkSync(filePath); + } - /** - * @param {string} filePath - * @return {boolean} - */ - lastModified(filePath) { - return fs.statSync(filePath).mtime; - } + /** + * @param {string} filePath + * @return {boolean} + */ + lastModified(filePath) { + return fs.statSync(filePath).mtime; + } - /** - * @param {string} dirPath - * @return {boolean} - */ - makeDirectory(dirPath) { - try { - fs.mkdirSync(dirPath); - return true; - } catch (e) { - return false; - } + /** + * @param {string} dirPath + * @return {boolean} + */ + makeDirectory(dirPath) { + try { + fs.mkdirSync(dirPath); + return true; + } catch (e) { + return false; } + } - /** - * @param {string} dirPath - * @return {boolean} - */ - makeTree(dirPath) { - return this.makeDirectory(dirPath); - } + /** + * @param {string} dirPath + * @return {boolean} + */ + makeTree(dirPath) { + return this.makeDirectory(dirPath); + } - /** - * @param {string} dirPath - */ - removeTree(dirPath) { - removeRecursive(dirPath); - } + /** + * @param {string} dirPath + */ + removeTree(dirPath) { + removeRecursive(dirPath); + } - /** - * @param {string} fromPath - * @param {string} toPath - */ - copyTree(fromPath, toPath) { - copyRecursive(fromPath, toPath); - } + /** + * @param {string} fromPath + * @param {string} toPath + */ + copyTree(fromPath, toPath) { + copyRecursive(fromPath, toPath); + } - /** - * @param {string} dirPath - * @return {!Array} - */ - list(dirPath) { - return fs.readdirSync(dirPath); - } + /** + * @param {string} dirPath + * @return {!Array} + */ + list(dirPath) { + return fs.readdirSync(dirPath); + } - /** - * @param {string} linkPath - * @return {string} - */ - readLink(linkPath) { - return fs.readlinkSync(linkPath); - } + /** + * @param {string} linkPath + * @return {string} + */ + readLink(linkPath) { + return fs.readlinkSync(linkPath); + } - /** - * @param {string} filePath - * @param {Object} data - * @param {string} mode - */ - write(filePath, data, mode) { - var fd = new FileDescriptor(filePath, mode, 'utf8'); - fd.write(data); - fd.close(); - } + /** + * @param {string} filePath + * @param {Object} data + * @param {string} mode + */ + write(filePath, data, mode) { + var fd = new FileDescriptor(filePath, mode, 'utf8'); + fd.write(data); + fd.close(); + } - /** - * @param {string} somePath - * @return {boolean} - */ - isAbsolute(somePath) { - return path.isAbsolute(somePath); - } + /** + * @param {string} somePath + * @return {boolean} + */ + isAbsolute(somePath) { + return path.isAbsolute(somePath); + } - /** - * @return {string} - */ - read(filePath) { - return fs.readFileSync(filePath, 'utf8'); - } + /** + * @return {string} + */ + read(filePath) { + return fs.readFileSync(filePath, 'utf8'); + } - /** - * @param {string} filePath - * @return {boolean} - */ - isFile(filePath) { - return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile(); - } + /** + * @param {string} filePath + * @return {boolean} + */ + isFile(filePath) { + return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile(); + } - /** - * @param {string} dirPath - * @return {boolean} - */ - isDirectory(dirPath) { - return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); - } + /** + * @param {string} dirPath + * @return {boolean} + */ + isDirectory(dirPath) { + return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); + } - /** - * @param {string} filePath - * @return {boolean} - */ - isLink(filePath) { - return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink(); - } + /** + * @param {string} filePath + * @return {boolean} + */ + isLink(filePath) { + return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink(); + } - /** - * @param {string} filePath - * @return {boolean} - */ - isReadable(filePath) { - try { - fs.accessSync(filePath, fs.constants.R_OK); - return true; - } catch (e) { - return false; - } + /** + * @param {string} filePath + * @return {boolean} + */ + isReadable(filePath) { + try { + fs.accessSync(filePath, fs.constants.R_OK); + return true; + } catch (e) { + return false; } + } - /** - * @param {string} filePath - * @return {boolean} - */ - isWritable(filePath) { - try { - fs.accessSync(filePath, fs.constants.W_OK); - return true; - } catch (e) { - return false; - } + /** + * @param {string} filePath + * @return {boolean} + */ + isWritable(filePath) { + try { + fs.accessSync(filePath, fs.constants.W_OK); + return true; + } catch (e) { + return false; } + } - /** - * @param {string} filePath - * @return {boolean} - */ - isExecutable(filePath) { - try { - fs.accessSync(filePath, fs.constants.X_OK); - return true; - } catch (e) { - return false; - } + /** + * @param {string} filePath + * @return {boolean} + */ + isExecutable(filePath) { + try { + fs.accessSync(filePath, fs.constants.X_OK); + return true; + } catch (e) { + return false; } + } - /** - * @param {string} somePath - * @return {!Array} - */ - split(somePath) { - somePath = path.normalize(somePath); - if (somePath.endsWith(path.sep)) - somePath = somePath.substring(0, somePath.length - path.sep.length); - return somePath.split(path.sep); - } + /** + * @param {string} somePath + * @return {!Array} + */ + split(somePath) { + somePath = path.normalize(somePath); + if (somePath.endsWith(path.sep)) + somePath = somePath.substring(0, somePath.length - path.sep.length); + return somePath.split(path.sep); + } - /** - * @param {string} path1 - * @param {string} path2 - * @return {string} - */ - join(...args) { - if (args[0] === '' && args.length > 1) - args[0] = path.sep; - args = args.filter(part => typeof part === 'string'); - return path.join.apply(path, args); - } + /** + * @param {string} path1 + * @param {string} path2 + * @return {string} + */ + join(...args) { + if (args[0] === '' && args.length > 1) + args[0] = path.sep; + args = args.filter(part => typeof part === 'string'); + return path.join.apply(path, args); + } - /** - * @param {string} filePath - * @param {(string|!Object)} option - * @return {!FileDescriptor} - */ - open(filePath, option) { - if (typeof option === 'string') - return new FileDescriptor(filePath, option); - return new FileDescriptor(filePath, option.mode); - } + /** + * @param {string} filePath + * @param {(string|!Object)} option + * @return {!FileDescriptor} + */ + open(filePath, option) { + if (typeof option === 'string') + return new FileDescriptor(filePath, option); + return new FileDescriptor(filePath, option.mode); + } } var fdwrite = deasync(fs.write); var fdread = deasync(fs.read); class FileDescriptor { - /** - * @param {string} filePath - * @param {string} mode - */ - constructor(filePath, mode) { - this._position = 0; - this._encoding = 'utf8'; - if (mode === 'rb') { - this._mode = 'r'; - this._encoding = 'latin1'; - } else if (mode === 'wb' || mode === 'b') { - this._mode = 'w'; - this._encoding = 'latin1'; - } else if (mode === 'rw+') { - this._mode = 'a+'; - this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0; - } else { - this._mode = mode; - } - this._fd = fs.openSync(filePath, this._mode); + /** + * @param {string} filePath + * @param {string} mode + */ + constructor(filePath, mode) { + this._position = 0; + this._encoding = 'utf8'; + if (mode === 'rb') { + this._mode = 'r'; + this._encoding = 'latin1'; + } else if (mode === 'wb' || mode === 'b') { + this._mode = 'w'; + this._encoding = 'latin1'; + } else if (mode === 'rw+') { + this._mode = 'a+'; + this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0; + } else { + this._mode = mode; } + this._fd = fs.openSync(filePath, this._mode); + } - /** - * @param {string} data - */ - write(data) { - var buffer = Buffer.from(data, this._encoding); - var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position); - this._position += written; - } + /** + * @param {string} data + */ + write(data) { + var buffer = Buffer.from(data, this._encoding); + var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position); + this._position += written; + } - getEncoding() { - return 'UTF-8'; - } + getEncoding() { + return 'UTF-8'; + } - /** - * @param {string} data - */ - writeLine(data) { - this.write(data + '\n'); - } + /** + * @param {string} data + */ + writeLine(data) { + this.write(data + '\n'); + } - /** - * @param {number=} size - * @return {string} - */ - read(size) { - var position = this._position; - if (!size) { - size = fs.fstatSync(this._fd).size; - position = 0; - } - var buffer = new Buffer(size); - var bytesRead = fdread(this._fd, buffer, 0, size, position); - this._position += bytesRead; - return buffer.toString(this._encoding); + /** + * @param {number=} size + * @return {string} + */ + read(size) { + var position = this._position; + if (!size) { + size = fs.fstatSync(this._fd).size; + position = 0; } + var buffer = new Buffer(size); + var bytesRead = fdread(this._fd, buffer, 0, size, position); + this._position += bytesRead; + return buffer.toString(this._encoding); + } - flush() { - // noop. - } + flush() { + // noop. + } - /** - * @param {number} position - */ - seek(position) { - this._position = position; - } + /** + * @param {number} position + */ + seek(position) { + this._position = position; + } - close() { - fs.closeSync(this._fd); - } + close() { + fs.closeSync(this._fd); + } - /** - * @return {boolean} - */ - atEnd() { - } + /** + * @return {boolean} + */ + atEnd() { + } } module.exports = FileSystem; diff --git a/phantom_shim/Phantom.js b/phantom_shim/Phantom.js index e1fdec631dc..e62c228bebb 100644 --- a/phantom_shim/Phantom.js +++ b/phantom_shim/Phantom.js @@ -24,116 +24,116 @@ var url = require('url'); * @param {string} scriptPath */ module.exports.create = function(context, scriptPath) { - var phantom = { - page: { - onConsoleMessage: null, - }, + var phantom = { + page: { + onConsoleMessage: null, + }, - /** - * @param {string} relative - * @param {string} base - * @return {string} - */ - resolveRelativeUrl: function(relative, base) { - return url.resolve(base, relative); - }, + /** + * @param {string} relative + * @param {string} base + * @return {string} + */ + resolveRelativeUrl: function(relative, base) { + return url.resolve(base, relative); + }, - /** - * @param {string} url - * @return {string} - */ - fullyDecodeUrl: function(url) { - return decodeURI(url); - }, + /** + * @param {string} url + * @return {string} + */ + fullyDecodeUrl: function(url) { + return decodeURI(url); + }, - libraryPath: path.dirname(scriptPath), + libraryPath: path.dirname(scriptPath), - onError: null, + onError: null, - /** - * @return {string} - */ - get outputEncoding() { - return 'UTF-8'; - }, + /** + * @return {string} + */ + get outputEncoding() { + return 'UTF-8'; + }, - /** - * @param {string} value - */ - set outputEncoding(value) { - throw new Error('Phantom.outputEncoding setter is not implemented'); - }, + /** + * @param {string} value + */ + set outputEncoding(value) { + throw new Error('Phantom.outputEncoding setter is not implemented'); + }, - /** - * @return {boolean} - */ - get cookiesEnabled() { - return true; - }, + /** + * @return {boolean} + */ + get cookiesEnabled() { + return true; + }, - /** - * @param {boolean} value - */ - set cookiesEnabled(value) { - throw new Error('Phantom.cookiesEnabled setter is not implemented'); - }, + /** + * @param {boolean} value + */ + set cookiesEnabled(value) { + throw new Error('Phantom.cookiesEnabled setter is not implemented'); + }, - /** - * @return {!{major: number, minor: number, patch: number}} - */ - get version() { - var versionParts = require('../package.json').version.split('.'); - return { - major: parseInt(versionParts[0], 10), - minor: parseInt(versionParts[1], 10), - patch: parseInt(versionParts[2], 10), - }; - }, + /** + * @return {!{major: number, minor: number, patch: number}} + */ + get version() { + var versionParts = require('../package.json').version.split('.'); + return { + major: parseInt(versionParts[0], 10), + minor: parseInt(versionParts[1], 10), + patch: parseInt(versionParts[2], 10), + }; + }, - /** - * @param {number=} code - */ - exit: function(code) { - process.exit(code); - }, + /** + * @param {number=} code + */ + exit: function(code) { + process.exit(code); + }, - /** - * @param {string} filePath - * @return {boolean} - */ - injectJs: function(filePath) { - filePath = path.resolve(phantom.libraryPath, filePath); - if (!fs.existsSync(filePath)) - return false; - var code = fs.readFileSync(filePath, 'utf8'); - if (code.startsWith('#!')) - code = code.substring(code.indexOf('\n')); - vm.runInContext(code, context, { - filename: filePath, - displayErrors: true - }); - return true; - }, + /** + * @param {string} filePath + * @return {boolean} + */ + injectJs: function(filePath) { + filePath = path.resolve(phantom.libraryPath, filePath); + if (!fs.existsSync(filePath)) + return false; + var code = fs.readFileSync(filePath, 'utf8'); + if (code.startsWith('#!')) + code = code.substring(code.indexOf('\n')); + vm.runInContext(code, context, { + filename: filePath, + displayErrors: true + }); + return true; + }, - /** - * @param {string} moduleSource - * @param {string} filename - */ - loadModule: function(moduleSource, filename) { - var code = [ - '(function(require, exports, module) {\n', - moduleSource, - '\n}.call({},', - 'require.cache[\'' + filename + '\']._getRequire(),', - 'require.cache[\'' + filename + '\'].exports,', - 'require.cache[\'' + filename + '\']', - '));' - ].join(''); - vm.runInContext(code, context, { - filename: filename, - displayErrors: true - }); - }, - }; - return phantom; + /** + * @param {string} moduleSource + * @param {string} filename + */ + loadModule: function(moduleSource, filename) { + var code = [ + '(function(require, exports, module) {\n', + moduleSource, + '\n}.call({},', + 'require.cache[\'' + filename + '\']._getRequire(),', + 'require.cache[\'' + filename + '\'].exports,', + 'require.cache[\'' + filename + '\']', + '));' + ].join(''); + vm.runInContext(code, context, { + filename: filename, + displayErrors: true + }); + }, + }; + return phantom; }; diff --git a/phantom_shim/System.js b/phantom_shim/System.js index c6187267991..87e1d07c9a2 100644 --- a/phantom_shim/System.js +++ b/phantom_shim/System.js @@ -19,100 +19,100 @@ var await = require('./utilities').await; var os = require('os'); class System { - /** - * @param {!Array} args - */ - constructor(args) { - this.args = args; - this.env = {}; - Object.assign(this.env, process.env); - this.stdin = new StandardInput(process.stdin); - this.stdout = new StandardOutput(process.stdout); - this.stderr = new StandardOutput(process.stderr); - this.platform = 'phantomjs'; - this.pid = process.pid; - this.isSSLSupported = false; - this.os = { - architecture: os.arch(), - name: os.type(), - version: os.release() - }; - } + /** + * @param {!Array} args + */ + constructor(args) { + this.args = args; + this.env = {}; + Object.assign(this.env, process.env); + this.stdin = new StandardInput(process.stdin); + this.stdout = new StandardOutput(process.stdout); + this.stderr = new StandardOutput(process.stderr); + this.platform = 'phantomjs'; + this.pid = process.pid; + this.isSSLSupported = false; + this.os = { + architecture: os.arch(), + name: os.type(), + version: os.release() + }; + } } class StandardInput { - /** - * @param {!Readable} readableStream - */ - constructor(readableStream) { - this._readline = readline.createInterface({ - input: readableStream - }); - this._lines = []; - this._closed = false; - this._readline.on('line', line => this._lines.push(line)); - this._readline.on('close', () => this._closed = true); - } + /** + * @param {!Readable} readableStream + */ + constructor(readableStream) { + this._readline = readline.createInterface({ + input: readableStream + }); + this._lines = []; + this._closed = false; + this._readline.on('line', line => this._lines.push(line)); + this._readline.on('close', () => this._closed = true); + } - /** - * @return {string} - */ - readLine() { - if (this._closed && !this._lines.length) - return ''; - if (!this._lines.length) { - var linePromise = new Promise(fulfill => this._readline.once('line', fulfill)); - await(linePromise); - } - return this._lines.shift(); + /** + * @return {string} + */ + readLine() { + if (this._closed && !this._lines.length) + return ''; + if (!this._lines.length) { + var linePromise = new Promise(fulfill => this._readline.once('line', fulfill)); + await(linePromise); } + return this._lines.shift(); + } - /** - * @return {string} - */ - read() { - if (!this._closed) { - var closePromise = new Promise(fulfill => this._readline.once('close', fulfill)); - await(closePromise); - } - var text = this._lines.join('\n'); - this._lines = []; - return text; + /** + * @return {string} + */ + read() { + if (!this._closed) { + var closePromise = new Promise(fulfill => this._readline.once('close', fulfill)); + await(closePromise); } + var text = this._lines.join('\n'); + this._lines = []; + return text; + } - close() { - this._readline.close(); - } + close() { + this._readline.close(); + } } class StandardOutput { - /** - * @param {!Writable} writableStream - */ - constructor(writableStream) { - this._stream = writableStream; - } + /** + * @param {!Writable} writableStream + */ + constructor(writableStream) { + this._stream = writableStream; + } - /** - * @param {string} data - */ - write(data) { - this._stream.write(data); - } + /** + * @param {string} data + */ + write(data) { + this._stream.write(data); + } - /** - * @param {string} data - */ - writeLine(data) { - this._stream.write(data + '\n'); - } + /** + * @param {string} data + */ + writeLine(data) { + this._stream.write(data + '\n'); + } - flush() { - } + flush() { + } - close() { - this._stream.end(); - } + close() { + this._stream.end(); + } } module.exports = System; diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 6d07a5a5483..0f83541c000 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -23,319 +23,319 @@ var PageEvents = require('../lib/Page').Events; var noop = function() { }; class WebPage { - /** - * @param {!Browser} browser - * @param {string} scriptPath - * @param {!Object=} options - */ - constructor(browser, scriptPath, options) { - this._page = await(browser.newPage()); - this.settings = new WebPageSettings(this._page); + /** + * @param {!Browser} browser + * @param {string} scriptPath + * @param {!Object=} options + */ + constructor(browser, scriptPath, options) { + this._page = await(browser.newPage()); + this.settings = new WebPageSettings(this._page); - options = options || {}; - options.settings = options.settings || {}; - if (options.settings.userAgent) - this.settings.userAgent = options.settings.userAgent; - if (options.viewportSize) - await(this._page.setViewportSize(options.viewportSize)); + options = options || {}; + options.settings = options.settings || {}; + if (options.settings.userAgent) + this.settings.userAgent = options.settings.userAgent; + if (options.viewportSize) + await(this._page.setViewportSize(options.viewportSize)); - this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0}; - this.onConsoleMessage = null; - this.onLoadFinished = null; - this.onResourceError = null; - this.onResourceReceived = null; - this.onInitialized = null; - this._deferEvaluate = false; + this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0}; + this.onConsoleMessage = null; + this.onLoadFinished = null; + this.onResourceError = null; + this.onResourceReceived = null; + this.onInitialized = null; + this._deferEvaluate = false; - this.libraryPath = path.dirname(scriptPath); + this.libraryPath = path.dirname(scriptPath); - this._onResourceRequestedCallback = undefined; - this._onConfirmCallback = undefined; - this._onAlertCallback = undefined; - this._onError = noop; + this._onResourceRequestedCallback = undefined; + this._onConfirmCallback = undefined; + this._onAlertCallback = undefined; + this._onError = noop; - this._pageEvents = new AsyncEmitter(this._page); - this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response)); - this._pageEvents.on(PageEvents.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event)); - this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg)); - this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message)); - this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message)); - this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog)); - this._pageEvents.on(PageEvents.Error, error => (this._onError || noop).call(null, error.message, error.stack)); - } + this._pageEvents = new AsyncEmitter(this._page); + this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response)); + this._pageEvents.on(PageEvents.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event)); + this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg)); + this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message)); + this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message)); + this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog)); + this._pageEvents.on(PageEvents.Error, error => (this._onError || noop).call(null, error.message, error.stack)); + } + + /** + * @return {?function(!Object, !Request)} + */ + get onResourceRequested() { + return this._onResourceRequestedCallback; + } + + /** + * @return {?function(!Object, !Request)} callback + */ + set onResourceRequested(callback) { + this._onResourceRequestedCallback = callback; + this._page.setRequestInterceptor(callback ? resourceInterceptor : null); /** - * @return {?function(!Object, !Request)} + * @param {!Request} request */ - get onResourceRequested() { - return this._onResourceRequestedCallback; + function resourceInterceptor(request) { + var requestData = { + url: request.url(), + headers: request.headers() + }; + callback(requestData, request); + if (!request.handled()) + request.continue(); } + } - /** - * @return {?function(!Object, !Request)} callback - */ - set onResourceRequested(callback) { - this._onResourceRequestedCallback = callback; - this._page.setRequestInterceptor(callback ? resourceInterceptor : null); - - /** - * @param {!Request} request - */ - function resourceInterceptor(request) { - var requestData = { - url: request.url(), - headers: request.headers() - }; - callback(requestData, request); - if (!request.handled()) - request.continue(); - } + _onResponseReceived(response) { + if (!this.onResourceReceived) + return; + var headers = []; + for (var key in response.headers) { + headers.push({ + name: key, + value: response.headers[key] + }); } + response.headers = headers; + this.onResourceReceived.call(null, response); + } - _onResponseReceived(response) { - if (!this.onResourceReceived) - return; - var headers = []; - for (var key in response.headers) { - headers.push({ - name: key, - value: response.headers[key] - }); - } - response.headers = headers; - this.onResourceReceived.call(null, response); + /** + * @param {string} url + * @param {function()} callback + */ + includeJs(url, callback) { + this._page.addScriptTag(url).then(callback); + } + + /** + * @return {!{width: number, height: number}} + */ + get viewportSize() { + return this._page.viewportSize(); + } + + /** + * @return {!Object} + */ + get customHeaders() { + return this._page.extraHTTPHeaders(); + } + + /** + * @param {!Object} value + */ + set customHeaders(value) { + await(this._page.setExtraHTTPHeaders(value)); + } + + /** + * @param {string} filePath + */ + injectJs(filePath) { + if (!fs.existsSync(filePath)) + filePath = path.resolve(this.libraryPath, filePath); + if (!fs.existsSync(filePath)) + return false; + await(this._page.injectFile(filePath)); + return true; + } + + /** + * @return {string} + */ + get plainText() { + return await(this._page.plainText()); + } + + /** + * @return {string} + */ + get title() { + return await(this._page.title()); + } + + /** + * @return {(function()|undefined)} + */ + get onError() { + return this._onError; + } + + /** + * @param {(function()|undefined)} handler + */ + set onError(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onError = handler; + } + + /** + * @return {(function()|undefined)} + */ + get onConfirm() { + return this._onConfirmCallback; + } + + /** + * @param {function()} handler + */ + set onConfirm(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onConfirmCallback = handler; + } + + /** + * @return {(function()|undefined)} + */ + get onAlert() { + return this._onAlertCallback; + } + + /** + * @param {function()} handler + */ + set onAlert(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onAlertCallback = handler; + } + + /** + * @param {!Dialog} dialog + */ + _onDialog(dialog) { + if (dialog.type === 'alert' && this._onAlertCallback) { + this._onAlertCallback.call(null, dialog.message()); + await(dialog.accept()); + } else if (dialog.type === 'confirm' && this._onConfirmCallback) { + var result = this._onConfirmCallback.call(null, dialog.message()); + await(result ? dialog.accept() : dialog.dismiss()); } + } - /** - * @param {string} url - * @param {function()} callback - */ - includeJs(url, callback) { - this._page.addScriptTag(url).then(callback); - } + /** + * @return {string} + */ + get url() { + return await(this._page.url()); + } - /** - * @return {!{width: number, height: number}} - */ - get viewportSize() { - return this._page.viewportSize(); - } + /** + * @param {string} html + */ + set content(html) { + await(this._page.setContent(html)); + } - /** - * @return {!Object} - */ - get customHeaders() { - return this._page.extraHTTPHeaders(); - } - - /** - * @param {!Object} value - */ - set customHeaders(value) { - await(this._page.setExtraHTTPHeaders(value)); - } - - /** - * @param {string} filePath - */ - injectJs(filePath) { - if (!fs.existsSync(filePath)) - filePath = path.resolve(this.libraryPath, filePath); - if (!fs.existsSync(filePath)) - return false; - await(this._page.injectFile(filePath)); - return true; - } - - /** - * @return {string} - */ - get plainText() { - return await(this._page.plainText()); - } - - /** - * @return {string} - */ - get title() { - return await(this._page.title()); - } - - /** - * @return {(function()|undefined)} - */ - get onError() { - return this._onError; - } - - /** - * @param {(function()|undefined)} handler - */ - set onError(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onError = handler; - } - - /** - * @return {(function()|undefined)} - */ - get onConfirm() { - return this._onConfirmCallback; - } - - /** - * @param {function()} handler - */ - set onConfirm(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onConfirmCallback = handler; - } - - /** - * @return {(function()|undefined)} - */ - get onAlert() { - return this._onAlertCallback; - } - - /** - * @param {function()} handler - */ - set onAlert(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onAlertCallback = handler; - } - - /** - * @param {!Dialog} dialog - */ - _onDialog(dialog) { - if (dialog.type === 'alert' && this._onAlertCallback) { - this._onAlertCallback.call(null, dialog.message()); - await(dialog.accept()); - } else if (dialog.type === 'confirm' && this._onConfirmCallback) { - var result = this._onConfirmCallback.call(null, dialog.message()); - await(result ? dialog.accept() : dialog.dismiss()); - } - } - - /** - * @return {string} - */ - get url() { - return await(this._page.url()); - } - - /** - * @param {string} html - */ - set content(html) { - await(this._page.setContent(html)); - } - - /** - * @param {string} html - * @param {function()=} callback - */ - open(url, callback) { - console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments'); - this._deferEvaluate = true; - if (typeof this.onInitialized === 'function') - this.onInitialized(); - this._deferEvaluate = false; - this._page.navigate(url).then(result => { - var status = result ? 'success' : 'fail'; - if (!result) { - this.onResourceError.call(null, { - url, - errorString: 'SSL handshake failed' - }); - } - if (this.onLoadFinished) - this.onLoadFinished.call(null, status); - if (callback) - callback.call(null, status); + /** + * @param {string} html + * @param {function()=} callback + */ + open(url, callback) { + console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments'); + this._deferEvaluate = true; + if (typeof this.onInitialized === 'function') + this.onInitialized(); + this._deferEvaluate = false; + this._page.navigate(url).then(result => { + var status = result ? 'success' : 'fail'; + if (!result) { + this.onResourceError.call(null, { + url, + errorString: 'SSL handshake failed' }); - } + } + if (this.onLoadFinished) + this.onLoadFinished.call(null, status); + if (callback) + callback.call(null, status); + }); + } - /** - * @param {!{width: number, height: number}} options - */ - set viewportSize(options) { - await(this._page.setViewportSize(options)); - } + /** + * @param {!{width: number, height: number}} options + */ + set viewportSize(options) { + await(this._page.setViewportSize(options)); + } - /** - * @param {function()} fun - * @param {!Array} args - */ - evaluate(fun, ...args) { - if (this._deferEvaluate) - return await(this._page.evaluateOnInitialized(fun, ...args)); - return await(this._page.evaluate(fun, ...args)); - } + /** + * @param {function()} fun + * @param {!Array} args + */ + evaluate(fun, ...args) { + if (this._deferEvaluate) + return await(this._page.evaluateOnInitialized(fun, ...args)); + return await(this._page.evaluate(fun, ...args)); + } - /** - * {string} fileName - */ - render(fileName) { - if (fileName.endsWith('pdf')) { - var options = {}; - var paperSize = this.paperSize || {}; - options.margin = paperSize.margin; - options.format = paperSize.format; - options.landscape = paperSize.orientation === 'landscape'; - options.width = paperSize.width; - options.height = paperSize.height; - await(this._page.printToPDF(fileName, options)); - } else { - var options = {}; - if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) { - options.clip = { - x: this.clipRect.left, - y: this.clipRect.top, - width: this.clipRect.width, - height: this.clipRect.height - }; - } - options.path = fileName; - await(this._page.screenshot(options)); - } + /** + * {string} fileName + */ + render(fileName) { + if (fileName.endsWith('pdf')) { + var options = {}; + var paperSize = this.paperSize || {}; + options.margin = paperSize.margin; + options.format = paperSize.format; + options.landscape = paperSize.orientation === 'landscape'; + options.width = paperSize.width; + options.height = paperSize.height; + await(this._page.printToPDF(fileName, options)); + } else { + var options = {}; + if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) { + options.clip = { + x: this.clipRect.left, + y: this.clipRect.top, + width: this.clipRect.width, + height: this.clipRect.height + }; + } + options.path = fileName; + await(this._page.screenshot(options)); } + } - release() { - this._page.close(); - } + release() { + this._page.close(); + } - close() { - this._page.close(); - } + close() { + this._page.close(); + } } class WebPageSettings { - /** - * @param {!Page} page - */ - constructor(page) { - this._page = page; - } + /** + * @param {!Page} page + */ + constructor(page) { + this._page = page; + } - /** - * @param {string} value - */ - set userAgent(value) { - await(this._page.setUserAgentOverride(value)); - } + /** + * @param {string} value + */ + set userAgent(value) { + await(this._page.setUserAgentOverride(value)); + } - /** - * @return {string} - */ - get userAgent() { - return this._page.userAgentOverride(); - } + /** + * @return {string} + */ + get userAgent() { + return this._page.userAgentOverride(); + } } // To prevent reenterability, eventemitters should emit events @@ -349,29 +349,29 @@ class WebPageSettings { // This class is a wrapper around EventEmitter which re-emits events asynchronously, // helping to overcome the issue. class AsyncEmitter extends EventEmitter { - /** - * @param {!Page} page - */ - constructor(page) { - super(); - this._page = page; - this._symbol = Symbol('AsyncEmitter'); - this.on('newListener', this._onListenerAdded); - this.on('removeListener', this._onListenerRemoved); - } + /** + * @param {!Page} page + */ + constructor(page) { + super(); + this._page = page; + this._symbol = Symbol('AsyncEmitter'); + this.on('newListener', this._onListenerAdded); + this.on('removeListener', this._onListenerRemoved); + } - _onListenerAdded(event, listener) { - // Async listener calls original listener on next tick. - var asyncListener = (...args) => { - process.nextTick(() => listener.apply(null, args)); - }; - listener[this._symbol] = asyncListener; - this._page.on(event, asyncListener); - } + _onListenerAdded(event, listener) { + // Async listener calls original listener on next tick. + var asyncListener = (...args) => { + process.nextTick(() => listener.apply(null, args)); + }; + listener[this._symbol] = asyncListener; + this._page.on(event, asyncListener); + } - _onListenerRemoved(event, listener) { - this._page.removeListener(event, listener[this._symbol]); - } + _onListenerRemoved(event, listener) { + this._page.removeListener(event, listener[this._symbol]); + } } module.exports = WebPage; diff --git a/phantom_shim/WebServer.js b/phantom_shim/WebServer.js index 320243f46d6..b5e151c5645 100644 --- a/phantom_shim/WebServer.js +++ b/phantom_shim/WebServer.js @@ -18,66 +18,66 @@ var http = require('http'); var await = require('./utilities').await; class WebServer { - constructor() { - this._server = http.createServer(); - this.objectName = 'WebServer'; - this.listenOnPort = this.listen; - this.newRequest = function(req, res) { }; - Object.defineProperty(this, 'port', { - get: () => { - if (!this._server.listening) - return ''; - return this._server.address().port + ''; - }, - enumerable: true, - configurable: false - }); - } - - close() { - this._server.close(); - } - - /** - * @param {nubmer} port - * @return {boolean} - */ - listen(port, callback) { - if (this._server.listening) - return false; - this.newRequest = callback; - this._server.listen(port); - var errorPromise = new Promise(x => this._server.once('error', x)); - var successPromise = new Promise(x => this._server.once('listening', x)); - await(Promise.race([errorPromise, successPromise])); + constructor() { + this._server = http.createServer(); + this.objectName = 'WebServer'; + this.listenOnPort = this.listen; + this.newRequest = function(req, res) { }; + Object.defineProperty(this, 'port', { + get: () => { if (!this._server.listening) - return false; + return ''; + return this._server.address().port + ''; + }, + enumerable: true, + configurable: false + }); + } - this._server.on('request', (req, res) => { - res.close = res.end.bind(res); - var headers = res.getHeaders(); - res.headers = []; - for (var key in headers) { - res.headers.push({ - name: key, - value: headers[key] - }); - } - res.header = res.getHeader; - res.setHeaders = headers => { - for (var key in headers) - res.setHeader(key, headers[key]); - }; - Object.defineProperty(res, 'statusCode', { - enumerable: true, - configurable: true, - writable: true, - value: res.statusCode - }); - this.newRequest.call(null, req, res); + close() { + this._server.close(); + } + + /** + * @param {nubmer} port + * @return {boolean} + */ + listen(port, callback) { + if (this._server.listening) + return false; + this.newRequest = callback; + this._server.listen(port); + var errorPromise = new Promise(x => this._server.once('error', x)); + var successPromise = new Promise(x => this._server.once('listening', x)); + await(Promise.race([errorPromise, successPromise])); + if (!this._server.listening) + return false; + + this._server.on('request', (req, res) => { + res.close = res.end.bind(res); + var headers = res.getHeaders(); + res.headers = []; + for (var key in headers) { + res.headers.push({ + name: key, + value: headers[key] }); - return true; - } + } + res.header = res.getHeader; + res.setHeaders = headers => { + for (var key in headers) + res.setHeader(key, headers[key]); + }; + Object.defineProperty(res, 'statusCode', { + enumerable: true, + configurable: true, + writable: true, + value: res.statusCode + }); + this.newRequest.call(null, req, res); + }); + return true; + } } module.exports = WebServer; diff --git a/phantom_shim/runner.js b/phantom_shim/runner.js index 87d25fdd0d7..fc83eacb52b 100755 --- a/phantom_shim/runner.js +++ b/phantom_shim/runner.js @@ -27,39 +27,39 @@ var child_process = require('child_process'); var Browser = require('..').Browser; var version = require('../package.json').version; var argv = require('minimist')(process.argv.slice(2), { - alias: { v: 'version' }, - boolean: ['headless'], - default: {'headless': true }, + alias: { v: 'version' }, + boolean: ['headless'], + default: {'headless': true }, }); if (argv.version) { - console.log('Puppeteer v' + version); - return; + console.log('Puppeteer v' + version); + return; } if (argv['ssl-certificates-path']) { - console.error('Flag --ssl-certificates-path is not supported.'); - process.exit(1); - return; + console.error('Flag --ssl-certificates-path is not supported.'); + process.exit(1); + return; } var scriptArguments = argv._; if (!scriptArguments.length) { - console.log(__filename.split('/').pop() + ' [scriptfile]'); - return; + console.log(__filename.split('/').pop() + ' [scriptfile]'); + return; } var scriptPath = path.resolve(process.cwd(), scriptArguments[0]); if (!fs.existsSync(scriptPath)) { - console.error(`script not found: ${scriptPath}`); - process.exit(1); - return; + console.error(`script not found: ${scriptPath}`); + process.exit(1); + return; } var browser = new Browser({ - remoteDebuggingPort: 9229, - headless: argv.headless, - args: ['--no-sandbox'] + remoteDebuggingPort: 9229, + headless: argv.headless, + args: ['--no-sandbox'] }); var context = createPhantomContext(browser, scriptPath, argv); @@ -73,38 +73,38 @@ vm.runInContext(scriptContent, context); * @return {!Object} */ function createPhantomContext(browser, scriptPath, argv) { - var context = {}; - context.setInterval = setInterval; - context.setTimeout = setTimeout; - context.clearInterval = clearInterval; - context.clearTimeout = clearTimeout; + var context = {}; + context.setInterval = setInterval; + context.setTimeout = setTimeout; + context.clearInterval = clearInterval; + context.clearTimeout = clearTimeout; - context.phantom = Phantom.create(context, scriptPath); - context.console = console; - context.window = context; - context.WebPage = options => new WebPage(browser, scriptPath, options); + context.phantom = Phantom.create(context, scriptPath); + context.console = console; + context.window = context; + context.WebPage = options => new WebPage(browser, scriptPath, options); - vm.createContext(context); + vm.createContext(context); - var nativeExports = { - fs: new FileSystem(), - system: new System(argv._), - webpage: { - create: context.WebPage, - }, - webserver: { - create: () => new WebServer(), - }, - cookiejar: { - create: () => {}, - }, - child_process: child_process - }; - var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js'); - var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8'); - vm.runInContext(bootstrapCode, context, { - filename: 'bootstrap.js' - })(nativeExports); - return context; + var nativeExports = { + fs: new FileSystem(), + system: new System(argv._), + webpage: { + create: context.WebPage, + }, + webserver: { + create: () => new WebServer(), + }, + cookiejar: { + create: () => {}, + }, + child_process: child_process + }; + var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js'); + var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8'); + vm.runInContext(bootstrapCode, context, { + filename: 'bootstrap.js' + })(nativeExports); + return context; } diff --git a/phantom_shim/utilities.js b/phantom_shim/utilities.js index 46ee945ce7f..487ef4be71b 100644 --- a/phantom_shim/utilities.js +++ b/phantom_shim/utilities.js @@ -17,16 +17,16 @@ var loopWhile = require('deasync').loopWhile; module.exports = { - await: function(promise) { - var error; - var result; - var done = false; - promise.then(r => result = r) - .catch(err => error = err) - .then(() => done = true); - loopWhile(() => !done); - if (error) - throw error; - return result; - } + await: function(promise) { + var error; + var result; + var done = false; + promise.then(r => result = r) + .catch(err => error = err) + .then(() => done = true); + loopWhile(() => !done); + if (error) + throw error; + return result; + } }; diff --git a/test/StaticServer.js b/test/StaticServer.js index 6f16616c464..8cdf930c98c 100644 --- a/test/StaticServer.js +++ b/test/StaticServer.js @@ -21,35 +21,35 @@ var path = require('path'); var mime = require('mime'); class StaticServer { - /** - * @param {string} dirPath - * @param {number} port - */ - constructor(dirPath, port) { - this._server = http.createServer(this._onRequest.bind(this)); - this._server.listen(port); - this._dirPath = dirPath; - } + /** + * @param {string} dirPath + * @param {number} port + */ + constructor(dirPath, port) { + this._server = http.createServer(this._onRequest.bind(this)); + this._server.listen(port); + this._dirPath = dirPath; + } - stop() { - this._server.close(); - } + stop() { + this._server.close(); + } - _onRequest(request, response) { - var pathName = url.parse(request.url).path; - if (pathName === '/') - pathName = '/index.html'; - pathName = path.join(this._dirPath, pathName.substring(1)); - fs.readFile(pathName, function(err, data) { - if (err) { - response.statusCode = 404; - response.end(`File not found: ${pathName}`); - return; - } - response.setHeader('Content-Type', mime.lookup(pathName)); - response.end(data); - }); - } + _onRequest(request, response) { + var pathName = url.parse(request.url).path; + if (pathName === '/') + pathName = '/index.html'; + pathName = path.join(this._dirPath, pathName.substring(1)); + fs.readFile(pathName, function(err, data) { + if (err) { + response.statusCode = 404; + response.end(`File not found: ${pathName}`); + return; + } + response.setHeader('Content-Type', mime.lookup(pathName)); + response.end(data); + }); + } } module.exports = StaticServer; diff --git a/test/frame-utils.js b/test/frame-utils.js index 148f7748573..2dfc3105b94 100644 --- a/test/frame-utils.js +++ b/test/frame-utils.js @@ -1,62 +1,62 @@ var utils = module.exports = { - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - * @return {!Promise} - */ - attachFrame: async function(page, frameId, url) { - await page.evaluate(attachFrame, frameId, url); + /** + * @param {!Page} page + * @param {string} frameId + * @param {string} url + * @return {!Promise} + */ + attachFrame: async function(page, frameId, url) { + await page.evaluate(attachFrame, frameId, url); - function attachFrame(frameId, url) { - var frame = document.createElement('iframe'); - frame.src = url; - frame.id = frameId; - document.body.appendChild(frame); - return new Promise(x => frame.onload = x); - } - }, + function attachFrame(frameId, url) { + var frame = document.createElement('iframe'); + frame.src = url; + frame.id = frameId; + document.body.appendChild(frame); + return new Promise(x => frame.onload = x); + } + }, - /** - * @param {!Page} page - * @param {string} frameId - * @return {!Promise} - */ - detachFrame: async function(page, frameId) { - await page.evaluate(detachFrame, frameId); + /** + * @param {!Page} page + * @param {string} frameId + * @return {!Promise} + */ + detachFrame: async function(page, frameId) { + await page.evaluate(detachFrame, frameId); - function detachFrame(frameId) { - var frame = document.getElementById(frameId); - frame.remove(); - } - }, + function detachFrame(frameId) { + var frame = document.getElementById(frameId); + frame.remove(); + } + }, - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - * @return {!Promise} - */ - navigateFrame: async function(page, frameId, url) { - await page.evaluate(navigateFrame, frameId, url); + /** + * @param {!Page} page + * @param {string} frameId + * @param {string} url + * @return {!Promise} + */ + navigateFrame: async function(page, frameId, url) { + await page.evaluate(navigateFrame, frameId, url); - function navigateFrame(frameId, url) { - var frame = document.getElementById(frameId); - frame.src = url; - return new Promise(x => frame.onload = x); - } - }, + function navigateFrame(frameId, url) { + var frame = document.getElementById(frameId); + frame.src = url; + return new Promise(x => frame.onload = x); + } + }, - /** - * @param {!Frame} frame - * @param {string=} indentation - * @return {string} - */ - dumpFrames: function(frame, indentation) { - indentation = indentation || ''; - var result = indentation + frame.url(); - for (var child of frame.childFrames()) - result += '\n' + utils.dumpFrames(child, ' ' + indentation); - return result; - }, + /** + * @param {!Frame} frame + * @param {string=} indentation + * @return {string} + */ + dumpFrames: function(frame, indentation) { + indentation = indentation || ''; + var result = indentation + frame.url(); + for (var child of frame.childFrames()) + result += '\n' + utils.dumpFrames(child, ' ' + indentation); + return result; + }, }; diff --git a/test/golden-utils.js b/test/golden-utils.js index 55379286aa2..715a0c444e6 100644 --- a/test/golden-utils.js +++ b/test/golden-utils.js @@ -25,19 +25,19 @@ var GOLDEN_DIR = path.join(__dirname, 'golden'); var OUTPUT_DIR = path.join(__dirname, 'output'); module.exports = { - addMatchers: function(jasmine) { - jasmine.addMatchers(customMatchers); - }, + addMatchers: function(jasmine) { + jasmine.addMatchers(customMatchers); + }, - removeOutputDir: function() { - if (fs.existsSync(OUTPUT_DIR)) - rm(OUTPUT_DIR); - }, + removeOutputDir: function() { + if (fs.existsSync(OUTPUT_DIR)) + rm(OUTPUT_DIR); + }, }; var GoldenComparators = { - 'image/png': compareImages, - 'text/plain': compareText + 'image/png': compareImages, + 'text/plain': compareText }; /** @@ -46,19 +46,19 @@ var GoldenComparators = { * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} */ function compareImages(actualBuffer, expectedBuffer) { - if (!actualBuffer || !(actualBuffer instanceof Buffer)) - return { errorMessage: 'Actual result should be Buffer.' }; + if (!actualBuffer || !(actualBuffer instanceof Buffer)) + return { errorMessage: 'Actual result should be Buffer.' }; - var actual = PNG.sync.read(actualBuffer); - var expected = PNG.sync.read(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. ` - }; - } - var diff = new PNG({width: expected.width, height: expected.height}); - var count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1}); - return count > 0 ? { diff: PNG.sync.write(diff) } : null; + var actual = PNG.sync.read(actualBuffer); + var expected = PNG.sync.read(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. ` + }; + } + var diff = new PNG({width: expected.width, height: expected.height}); + var count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1}); + return count > 0 ? { diff: PNG.sync.write(diff) } : null; } /** @@ -67,80 +67,80 @@ function compareImages(actualBuffer, expectedBuffer) { * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} */ function compareText(actual, expectedBuffer) { - if (typeof actual !== 'string') - return { errorMessage: 'Actual result should be string' }; - var expected = expectedBuffer.toString('utf-8'); - if (expected === actual) - return null; - var diff = new Diff(); - var result = diff.main(expected, actual); - diff.cleanupSemantic(result); - var html = diff.prettyHtml(result); - var diffStylePath = path.join(__dirname, 'diffstyle.css'); - html = `` + html; - return { - diff: html, - diffExtension: '.html' - }; + if (typeof actual !== 'string') + return { errorMessage: 'Actual result should be string' }; + var expected = expectedBuffer.toString('utf-8'); + if (expected === actual) + return null; + var diff = new Diff(); + var result = diff.main(expected, actual); + diff.cleanupSemantic(result); + var html = diff.prettyHtml(result); + var diffStylePath = path.join(__dirname, 'diffstyle.css'); + html = `` + html; + return { + diff: html, + diffExtension: '.html' + }; } var customMatchers = { - toBeGolden: function(util, customEqualityTesters) { + toBeGolden: function(util, customEqualityTesters) { + return { + /** + * @param {?Object} actual + * @param {string} goldenName + * @return {!{pass: boolean, message: (undefined|string)}} + */ + compare: function(actual, goldenName) { + var expectedPath = path.join(GOLDEN_DIR, goldenName); + var actualPath = path.join(OUTPUT_DIR, goldenName); + + var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory'); + + if (!fs.existsSync(expectedPath)) { + ensureOutputDir(); + fs.writeFileSync(actualPath, actual); + return { + pass: false, + message: goldenName + ' is missing in golden results. ' + messageSuffix + }; + } + var expected = fs.readFileSync(expectedPath); + var comparator = GoldenComparators[mime.lookup(goldenName)]; + if (!comparator) { + return { + pass: false, + message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName + }; + } + var result = comparator(actual, expected); + if (!result) + return { pass: true }; + ensureOutputDir(); + fs.writeFileSync(actualPath, actual); + // Copy expected to the output/ folder for convenience. + fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); + if (result.diff) { + var diffPath = addSuffix(actualPath, '-diff', result.diffExtension); + fs.writeFileSync(diffPath, result.diff); + } + + var message = goldenName + ' mismatch!'; + if (result.errorMessage) + message += ' ' + result.errorMessage; return { - /** - * @param {?Object} actual - * @param {string} goldenName - * @return {!{pass: boolean, message: (undefined|string)}} - */ - compare: function(actual, goldenName) { - var expectedPath = path.join(GOLDEN_DIR, goldenName); - var actualPath = path.join(OUTPUT_DIR, goldenName); - - var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory'); - - if (!fs.existsSync(expectedPath)) { - ensureOutputDir(); - fs.writeFileSync(actualPath, actual); - return { - pass: false, - message: goldenName + ' is missing in golden results. ' + messageSuffix - }; - } - var expected = fs.readFileSync(expectedPath); - var comparator = GoldenComparators[mime.lookup(goldenName)]; - if (!comparator) { - return { - pass: false, - message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName - }; - } - var result = comparator(actual, expected); - if (!result) - return { pass: true }; - ensureOutputDir(); - fs.writeFileSync(actualPath, actual); - // Copy expected to the output/ folder for convenience. - fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); - if (result.diff) { - var diffPath = addSuffix(actualPath, '-diff', result.diffExtension); - fs.writeFileSync(diffPath, result.diff); - } - - var message = goldenName + ' mismatch!'; - if (result.errorMessage) - message += ' ' + result.errorMessage; - return { - pass: false, - message: message + ' ' + messageSuffix - }; - - function ensureOutputDir() { - if (!fs.existsSync(OUTPUT_DIR)) - fs.mkdirSync(OUTPUT_DIR); - } - } + pass: false, + message: message + ' ' + messageSuffix }; - }, + + function ensureOutputDir() { + if (!fs.existsSync(OUTPUT_DIR)) + fs.mkdirSync(OUTPUT_DIR); + } + } + }; + }, }; /** @@ -150,8 +150,8 @@ var customMatchers = { * @return {string} */ function addSuffix(filePath, suffix, customExtension) { - var dirname = path.dirname(filePath); - var ext = path.extname(filePath); - var name = path.basename(filePath, ext); - return path.join(dirname, name + suffix + (customExtension || ext)); + var dirname = path.dirname(filePath); + var ext = path.extname(filePath); + var name = path.basename(filePath, ext); + return path.join(dirname, name + suffix + (customExtension || ext)); } diff --git a/test/test.js b/test/test.js index 5eb330dcccf..c0154b6ae5c 100644 --- a/test/test.js +++ b/test/test.js @@ -26,308 +26,308 @@ var EMPTY_PAGE = STATIC_PREFIX + '/empty.html'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000; describe('Puppeteer', function() { - var browser; - var staticServer; - var page; + var browser; + var staticServer; + var page; - beforeAll(function() { - browser = new Browser({args: ['--no-sandbox']}); - staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT); - GoldenUtils.removeOutputDir(); - }); + beforeAll(function() { + browser = new Browser({args: ['--no-sandbox']}); + staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT); + GoldenUtils.removeOutputDir(); + }); - afterAll(function() { - browser.close(); - staticServer.stop(); - }); + afterAll(function() { + browser.close(); + staticServer.stop(); + }); - beforeEach(SX(async function() { - page = await browser.newPage(); - GoldenUtils.addMatchers(jasmine); + beforeEach(SX(async function() { + page = await browser.newPage(); + GoldenUtils.addMatchers(jasmine); + })); + + afterEach(function() { + page.close(); + }); + + describe('Page.evaluate', function() { + it('should work', SX(async function() { + var result = await page.evaluate(() => 7 * 3); + expect(result).toBe(21); })); - - afterEach(function() { - page.close(); - }); - - describe('Page.evaluate', function() { - it('should work', SX(async function() { - var result = await page.evaluate(() => 7 * 3); - expect(result).toBe(21); - })); - it('should await promise', SX(async function() { - var result = await page.evaluate(() => Promise.resolve(8 * 7)); - expect(result).toBe(56); - })); - it('should work from-inside inPageCallback', SX(async function() { - // Setup inpage callback, which calls Page.evaluate - await page.setInPageCallback('callController', async function(a, b) { - return await page.evaluate((a, b) => a * b, a, b); - }); - var result = await page.evaluate(async function() { - return await callController(9, 3); - }); - expect(result).toBe(27); - })); - it('should reject promise with exception', SX(async function() { - var error = null; - try { - await page.evaluate(() => not.existing.object.property); - } catch (e) { - error = e; - } - expect(error).toBeTruthy(); - expect(error.message).toContain('not is not defined'); - })); - }); - - it('Page Events: ConsoleMessage', SX(async function() { - var msgs = []; - page.on('consolemessage', msg => msgs.push(msg)); - await page.evaluate(() => console.log('Message!')); - expect(msgs).toEqual(['Message!']); + it('should await promise', SX(async function() { + var result = await page.evaluate(() => Promise.resolve(8 * 7)); + expect(result).toBe(56); })); + it('should work from-inside inPageCallback', SX(async function() { + // Setup inpage callback, which calls Page.evaluate + await page.setInPageCallback('callController', async function(a, b) { + return await page.evaluate((a, b) => a * b, a, b); + }); + var result = await page.evaluate(async function() { + return await callController(9, 3); + }); + expect(result).toBe(27); + })); + it('should reject promise with exception', SX(async function() { + var error = null; + try { + await page.evaluate(() => not.existing.object.property); + } catch (e) { + error = e; + } + expect(error).toBeTruthy(); + expect(error.message).toContain('not is not defined'); + })); + }); - describe('Page.navigate', function() { - it('should fail when navigating to bad url', SX(async function() { - var success = await page.navigate('asdfasdf'); - expect(success).toBe(false); - })); - it('should succeed when navigating to good url', SX(async function() { - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); - })); + it('Page Events: ConsoleMessage', SX(async function() { + var msgs = []; + page.on('consolemessage', msg => msgs.push(msg)); + await page.evaluate(() => console.log('Message!')); + expect(msgs).toEqual(['Message!']); + })); + + describe('Page.navigate', function() { + it('should fail when navigating to bad url', SX(async function() { + var success = await page.navigate('asdfasdf'); + expect(success).toBe(false); + })); + it('should succeed when navigating to good url', SX(async function() { + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + }); + + describe('Page.setInPageCallback', function() { + it('should work', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return a * b; + }); + var result = await page.evaluate(async function() { + return await callController(9, 4); + }); + expect(result).toBe(36); + })); + it('should survive navigation', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return a * b; + }); + + await page.navigate(EMPTY_PAGE); + var result = await page.evaluate(async function() { + return await callController(9, 4); + }); + expect(result).toBe(36); + })); + it('should await returned promise', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return Promise.resolve(a * b); + }); + + var result = await page.evaluate(async function() { + return await callController(3, 5); + }); + expect(result).toBe(15); + })); + }); + + describe('Page.setRequestInterceptor', function() { + it('should intercept', SX(async function() { + page.setRequestInterceptor(request => { + expect(request.url()).toContain('empty.html'); + expect(request.headers()['User-Agent']).toBeTruthy(); + expect(request.method()).toBe('GET'); + expect(request.postData()).toBe(undefined); + request.continue(); + }); + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + it('should show extraHTTPHeaders', SX(async function() { + await page.setExtraHTTPHeaders({ + foo: 'bar' + }); + page.setRequestInterceptor(request => { + expect(request.headers()['foo']).toBe('bar'); + request.continue(); + }); + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + it('should be abortable', SX(async function() { + page.setRequestInterceptor(request => { + if (request.url().endsWith('.css')) + request.abort(); + else + request.continue(); + }); + var failedResources = 0; + page.on('resourceloadingfailed', event => ++failedResources); + var success = await page.navigate(STATIC_PREFIX + '/one-style.html'); + expect(success).toBe(true); + expect(failedResources).toBe(1); + })); + }); + + describe('Page.Events.Dialog', function() { + it('should fire', function(done) { + page.on('dialog', dialog => { + expect(dialog.type).toBe('alert'); + expect(dialog.message()).toBe('yo'); + done(); + }); + page.evaluate(() => alert('yo')); }); + // TODO Enable this when crbug.com/718235 is fixed. + xit('should allow accepting prompts', SX(async function(done) { + page.on('dialog', dialog => { + expect(dialog.type).toBe('prompt'); + expect(dialog.message()).toBe('question?'); + dialog.accept('answer!'); + }); + var result = await page.evaluate(() => prompt('question?')); + expect(result).toBe('answer!'); + })); + }); - describe('Page.setInPageCallback', function() { - it('should work', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return a * b; - }); - var result = await page.evaluate(async function() { - return await callController(9, 4); - }); - expect(result).toBe(36); - })); - it('should survive navigation', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return a * b; - }); - - await page.navigate(EMPTY_PAGE); - var result = await page.evaluate(async function() { - return await callController(9, 4); - }); - expect(result).toBe(36); - })); - it('should await returned promise', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return Promise.resolve(a * b); - }); - - var result = await page.evaluate(async function() { - return await callController(3, 5); - }); - expect(result).toBe(15); - })); + describe('Page.Events.Error', function() { + it('should fire', function(done) { + page.on('error', error => { + expect(error.message).toContain('Fancy'); + done(); + }); + page.navigate(STATIC_PREFIX + '/error.html'); }); + }); - describe('Page.setRequestInterceptor', function() { - it('should intercept', SX(async function() { - page.setRequestInterceptor(request => { - expect(request.url()).toContain('empty.html'); - expect(request.headers()['User-Agent']).toBeTruthy(); - expect(request.method()).toBe('GET'); - expect(request.postData()).toBe(undefined); - request.continue(); - }); - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); + describe('Page.screenshot', function() { + it('should work', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot(); + expect(screenshot).toBeGolden('screenshot-sanity.png'); + })); + it('should clip rect', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + clip: { + x: 50, + y: 100, + width: 150, + height: 100 + } + }); + expect(screenshot).toBeGolden('screenshot-clip-rect.png'); + })); + it('should work for offscreen clip', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + clip: { + x: 50, + y: 600, + width: 100, + height: 100 + } + }); + expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); + })); + it('should run in parallel', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var promises = []; + for (var i = 0; i < 3; ++i) { + promises.push(page.screenshot({ + clip: { + x: 50 * i, + y: 0, + width: 50, + height: 50 + } })); - it('should show extraHTTPHeaders', SX(async function() { - await page.setExtraHTTPHeaders({ - foo: 'bar' - }); - page.setRequestInterceptor(request => { - expect(request.headers()['foo']).toBe('bar'); - request.continue(); - }); - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); - })); - it('should be abortable', SX(async function() { - page.setRequestInterceptor(request => { - if (request.url().endsWith('.css')) - request.abort(); - else - request.continue(); - }); - var failedResources = 0; - page.on('resourceloadingfailed', event => ++failedResources); - var success = await page.navigate(STATIC_PREFIX + '/one-style.html'); - expect(success).toBe(true); - expect(failedResources).toBe(1); - })); - }); + } + var screenshot = await promises[1]; + expect(screenshot).toBeGolden('screenshot-parallel-calls.png'); + })); + it('should take fullPage screenshots', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + fullPage: true + }); + expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); + })); + }); - describe('Page.Events.Dialog', function() { - it('should fire', function(done) { - page.on('dialog', dialog => { - expect(dialog.type).toBe('alert'); - expect(dialog.message()).toBe('yo'); - done(); - }); - page.evaluate(() => alert('yo')); - }); - // TODO Enable this when crbug.com/718235 is fixed. - xit('should allow accepting prompts', SX(async function(done) { - page.on('dialog', dialog => { - expect(dialog.type).toBe('prompt'); - expect(dialog.message()).toBe('question?'); - dialog.accept('answer!'); - }); - var result = await page.evaluate(() => prompt('question?')); - expect(result).toBe('answer!'); - })); - }); + describe('Frame Management', function() { + var FrameUtils = require('./frame-utils'); + it('should handle nested frames', SX(async function() { + await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt'); + })); + it('should send events when frames are manipulated dynamically', SX(async function() { + await page.navigate(EMPTY_PAGE); + // validate frameattached events + var attachedFrames = []; + page.on('frameattached', frame => attachedFrames.push(frame)); + await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html'); + expect(attachedFrames.length).toBe(1); + expect(attachedFrames[0].url()).toContain('/assets/frame.html'); - describe('Page.Events.Error', function() { - it('should fire', function(done) { - page.on('error', error => { - expect(error.message).toContain('Fancy'); - done(); - }); - page.navigate(STATIC_PREFIX + '/error.html'); - }); - }); + // validate framenavigated events + var navigatedFrames = []; + page.on('framenavigated', frame => navigatedFrames.push(frame)); + await FrameUtils.navigateFrame(page, 'frame1', './empty.html'); + expect(navigatedFrames.length).toBe(1); + expect(navigatedFrames[0].url()).toContain('/empty.html'); - describe('Page.screenshot', function() { - it('should work', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot(); - expect(screenshot).toBeGolden('screenshot-sanity.png'); - })); - it('should clip rect', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - clip: { - x: 50, - y: 100, - width: 150, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-clip-rect.png'); - })); - it('should work for offscreen clip', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - clip: { - x: 50, - y: 600, - width: 100, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); - })); - it('should run in parallel', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var promises = []; - for (var i = 0; i < 3; ++i) { - promises.push(page.screenshot({ - clip: { - x: 50 * i, - y: 0, - width: 50, - height: 50 - } - })); - } - var screenshot = await promises[1]; - expect(screenshot).toBeGolden('screenshot-parallel-calls.png'); - })); - it('should take fullPage screenshots', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - fullPage: true - }); - expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); - })); - }); + // validate framedetached events + var detachedFrames = []; + page.on('framedetached', frame => detachedFrames.push(frame)); + await FrameUtils.detachFrame(page, 'frame1'); + expect(detachedFrames.length).toBe(1); + expect(detachedFrames[0].isDetached()).toBe(true); + })); + it('should persist mainFrame on cross-process navigation', SX(async function() { + await page.navigate(EMPTY_PAGE); + var mainFrame = page.mainFrame(); + await page.navigate('http://127.0.0.1:' + PORT + '/empty.html'); + expect(page.mainFrame() === mainFrame).toBeTruthy(); + })); + it('should not send attach/detach events for main frame', SX(async function() { + var hasEvents = false; + page.on('frameattached', frame => hasEvents = true); + page.on('framedetached', frame => hasEvents = true); + await page.navigate(EMPTY_PAGE); + expect(hasEvents).toBe(false); + })); + it('should detach child frames on navigation', SX(async function() { + var attachedFrames = []; + var detachedFrames = []; + var navigatedFrames = []; + page.on('frameattached', frame => attachedFrames.push(frame)); + page.on('framedetached', frame => detachedFrames.push(frame)); + page.on('framenavigated', frame => navigatedFrames.push(frame)); + await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + expect(attachedFrames.length).toBe(4); + expect(detachedFrames.length).toBe(0); + expect(navigatedFrames.length).toBe(5); - describe('Frame Management', function() { - var FrameUtils = require('./frame-utils'); - it('should handle nested frames', SX(async function() { - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); - expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt'); - })); - it('should send events when frames are manipulated dynamically', SX(async function() { - await page.navigate(EMPTY_PAGE); - // validate frameattached events - var attachedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html'); - expect(attachedFrames.length).toBe(1); - expect(attachedFrames[0].url()).toContain('/assets/frame.html'); - - // validate framenavigated events - var navigatedFrames = []; - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await FrameUtils.navigateFrame(page, 'frame1', './empty.html'); - expect(navigatedFrames.length).toBe(1); - expect(navigatedFrames[0].url()).toContain('/empty.html'); - - // validate framedetached events - var detachedFrames = []; - page.on('framedetached', frame => detachedFrames.push(frame)); - await FrameUtils.detachFrame(page, 'frame1'); - expect(detachedFrames.length).toBe(1); - expect(detachedFrames[0].isDetached()).toBe(true); - })); - it('should persist mainFrame on cross-process navigation', SX(async function() { - await page.navigate(EMPTY_PAGE); - var mainFrame = page.mainFrame(); - await page.navigate('http://127.0.0.1:' + PORT + '/empty.html'); - expect(page.mainFrame() === mainFrame).toBeTruthy(); - })); - it('should not send attach/detach events for main frame', SX(async function() { - var hasEvents = false; - page.on('frameattached', frame => hasEvents = true); - page.on('framedetached', frame => hasEvents = true); - await page.navigate(EMPTY_PAGE); - expect(hasEvents).toBe(false); - })); - it('should detach child frames on navigation', SX(async function() { - var attachedFrames = []; - var detachedFrames = []; - var navigatedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - page.on('framedetached', frame => detachedFrames.push(frame)); - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); - expect(attachedFrames.length).toBe(4); - expect(detachedFrames.length).toBe(0); - expect(navigatedFrames.length).toBe(5); - - var attachedFrames = []; - var detachedFrames = []; - var navigatedFrames = []; - await page.navigate(EMPTY_PAGE); - expect(attachedFrames.length).toBe(0); - expect(detachedFrames.length).toBe(4); - expect(navigatedFrames.length).toBe(1); - })); - }); + var attachedFrames = []; + var detachedFrames = []; + var navigatedFrames = []; + await page.navigate(EMPTY_PAGE); + expect(attachedFrames.length).toBe(0); + expect(detachedFrames.length).toBe(4); + expect(navigatedFrames.length).toBe(1); + })); + }); }); // Since Jasmine doesn't like async functions, they should be wrapped // in a SX function. function SX(fun) { - return done => Promise.resolve(fun()).then(done).catch(done.fail); + return done => Promise.resolve(fun()).then(done).catch(done.fail); } diff --git a/utils/ChromiumDownloader.js b/utils/ChromiumDownloader.js index 5b5510b220a..5d868ec92a1 100644 --- a/utils/ChromiumDownloader.js +++ b/utils/ChromiumDownloader.js @@ -25,107 +25,107 @@ var URL = require('url'); var CHROMIUM_PATH = path.join(__dirname, '..', '.local-chromium'); var downloadURLs = { - linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip', - mac: 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', - win32: 'https://storage.googleapis.com/chromium-browser-snapshots/Win/%d/chrome-win32.zip', - win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip', + linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip', + mac: 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', + win32: 'https://storage.googleapis.com/chromium-browser-snapshots/Win/%d/chrome-win32.zip', + win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip', }; module.exports = { - /** - * @return {!Array} - */ - supportedPlatforms: function() { - return Object.keys(downloadURLs); - }, + /** + * @return {!Array} + */ + supportedPlatforms: function() { + return Object.keys(downloadURLs); + }, - /** - * @return {string} - */ - currentPlatform: function() { - var platform = os.platform(); - if (platform === 'darwin') - return 'mac'; - if (platform === 'linux') - return 'linux'; - if (platform === 'win32') - return os.arch() === 'x64' ? 'win64' : 'win32'; - return ''; - }, + /** + * @return {string} + */ + currentPlatform: function() { + var platform = os.platform(); + if (platform === 'darwin') + return 'mac'; + if (platform === 'linux') + return 'linux'; + if (platform === 'win32') + return os.arch() === 'x64' ? 'win64' : 'win32'; + return ''; + }, - /** - * @param {string} platform - * @param {string} revision - * @return {!Promise} - */ - canDownloadRevision: function(platform, revision) { - console.assert(downloadURLs[platform], 'Unknown platform: ' + platform); - var url = URL.parse(util.format(downloadURLs[platform], revision)); - var options = { - method: 'HEAD', - host: url.host, - path: url.pathname, - }; - var resolve; - var promise = new Promise(x => resolve = x); - var request = https.request(options, response => { - resolve(response.statusCode === 200); - }); - request.on('error', error => { - console.error(error); - resolve(false); - }); - request.end(); - return promise; - }, + /** + * @param {string} platform + * @param {string} revision + * @return {!Promise} + */ + canDownloadRevision: function(platform, revision) { + console.assert(downloadURLs[platform], 'Unknown platform: ' + platform); + var url = URL.parse(util.format(downloadURLs[platform], revision)); + var options = { + method: 'HEAD', + host: url.host, + path: url.pathname, + }; + var resolve; + var promise = new Promise(x => resolve = x); + var request = https.request(options, response => { + resolve(response.statusCode === 200); + }); + request.on('error', error => { + console.error(error); + resolve(false); + }); + request.end(); + return promise; + }, - /** - * @param {string} platform - * @param {string} revision - * @param {?function(number, number)} progressCallback - * @return {!Promise} - */ - downloadRevision: async function(platform, revision, progressCallback) { - var url = downloadURLs[platform]; - console.assert(url, `Unsupported platform: ${platform}`); - url = util.format(url, revision); - var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`); - var folderPath = getFolderPath(platform, revision); - if (fs.existsSync(folderPath)) - return; - try { - if (!fs.existsSync(CHROMIUM_PATH)) - fs.mkdirSync(CHROMIUM_PATH); - await downloadFile(url, zipPath, progressCallback); - await extractZip(zipPath, folderPath); - } finally { - if (fs.existsSync(zipPath)) - fs.unlinkSync(zipPath); - } - }, + /** + * @param {string} platform + * @param {string} revision + * @param {?function(number, number)} progressCallback + * @return {!Promise} + */ + downloadRevision: async function(platform, revision, progressCallback) { + var url = downloadURLs[platform]; + console.assert(url, `Unsupported platform: ${platform}`); + url = util.format(url, revision); + var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`); + var folderPath = getFolderPath(platform, revision); + if (fs.existsSync(folderPath)) + return; + try { + if (!fs.existsSync(CHROMIUM_PATH)) + fs.mkdirSync(CHROMIUM_PATH); + await downloadFile(url, zipPath, progressCallback); + await extractZip(zipPath, folderPath); + } finally { + if (fs.existsSync(zipPath)) + fs.unlinkSync(zipPath); + } + }, - /** - * @param {string} platform - * @param {string} revision - * @return {?{executablePath: string}} - */ - revisionInfo: function(platform, revision) { - var folderPath = getFolderPath(platform, revision); - if (!fs.existsSync(folderPath)) - return null; - var executablePath = ''; - if (platform === 'mac') - executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'); - else if (platform === 'linux') - executablePath = path.join(folderPath, 'chrome-linux', 'chrome'); - else if (platform === 'win32' || platform === 'win64') - executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe'); - else - throw 'Unsupported platfrom: ' + platfrom; - return { - executablePath: executablePath - }; - }, + /** + * @param {string} platform + * @param {string} revision + * @return {?{executablePath: string}} + */ + revisionInfo: function(platform, revision) { + var folderPath = getFolderPath(platform, revision); + if (!fs.existsSync(folderPath)) + return null; + var executablePath = ''; + if (platform === 'mac') + executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'); + else if (platform === 'linux') + executablePath = path.join(folderPath, 'chrome-linux', 'chrome'); + else if (platform === 'win32' || platform === 'win64') + executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe'); + else + throw 'Unsupported platfrom: ' + platfrom; + return { + executablePath: executablePath + }; + }, }; /** @@ -134,7 +134,7 @@ module.exports = { * @return {string} */ function getFolderPath(platform, revision) { - return path.join(CHROMIUM_PATH, platform + '-' + revision); + return path.join(CHROMIUM_PATH, platform + '-' + revision); } /** @@ -144,30 +144,30 @@ function getFolderPath(platform, revision) { * @return {!Promise} */ function downloadFile(url, destinationPath, progressCallback) { - var fulfill, reject; - var promise = new Promise((x, y) => { fulfill = x; reject = y; }); - var request = https.get(url, response => { - if (response.statusCode !== 200) { - var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`); - // consume response data to free up memory - response.resume(); - reject(error); - return; - } - var file = fs.createWriteStream(destinationPath); - file.on('finish', () => fulfill()); - file.on('error', error => reject(error)); - response.pipe(file); - var totalBytes = parseInt(response.headers['content-length'], 10); - if (progressCallback) - response.on('data', onData.bind(null, totalBytes)); - }); - request.on('error', error => reject(error)); - return promise; - - function onData(totalBytes, chunk) { - progressCallback(totalBytes, chunk.length); + var fulfill, reject; + var promise = new Promise((x, y) => { fulfill = x; reject = y; }); + var request = https.get(url, response => { + if (response.statusCode !== 200) { + var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`); + // consume response data to free up memory + response.resume(); + reject(error); + return; } + var file = fs.createWriteStream(destinationPath); + file.on('finish', () => fulfill()); + file.on('error', error => reject(error)); + response.pipe(file); + var totalBytes = parseInt(response.headers['content-length'], 10); + if (progressCallback) + response.on('data', onData.bind(null, totalBytes)); + }); + request.on('error', error => reject(error)); + return promise; + + function onData(totalBytes, chunk) { + progressCallback(totalBytes, chunk.length); + } } /** @@ -176,5 +176,5 @@ function downloadFile(url, destinationPath, progressCallback) { * @return {!Promise} */ function extractZip(zipPath, folderPath) { - return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill)); + return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill)); } diff --git a/utils/check_availability.js b/utils/check_availability.js index 8a3501beec3..7b083820fac 100755 --- a/utils/check_availability.js +++ b/utils/check_availability.js @@ -20,43 +20,43 @@ var https = require('https'); var OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json'; var colors = { - reset: '\x1b[0m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m' + reset: '\x1b[0m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m' }; class Table { - /** - * @param {!Array} columnWidths - */ - constructor(columnWidths) { - this.widths = columnWidths; - } + /** + * @param {!Array} columnWidths + */ + constructor(columnWidths) { + this.widths = columnWidths; + } - /** - * @param {!Array} values - */ - drawRow(values) { - console.assert(values.length === this.widths.length); - var row = ''; - for (var i = 0; i < values.length; ++i) - row += padCenter(values[i], this.widths[i]); - console.log(row); - } + /** + * @param {!Array} values + */ + drawRow(values) { + console.assert(values.length === this.widths.length); + var row = ''; + for (var i = 0; i < values.length; ++i) + row += padCenter(values[i], this.widths[i]); + console.log(row); + } } if (process.argv.length === 2) { - checkOmahaProxyAvailability(); - return; + checkOmahaProxyAvailability(); + return; } if (process.argv.length !== 4) { - console.log(` + console.log(` Usage: node check_revisions.js [fromRevision] [toRevision] This script checks availability of different prebuild chromium revisions. Running command without arguments will check against omahaproxy revisions.`); - return; + return; } var fromRevision = parseInt(process.argv[2], 10); @@ -67,27 +67,27 @@ checkRangeAvailability(fromRevision, toRevision); * @return {!Promise} */ async function checkOmahaProxyAvailability() { - console.log('Fetching revisions from ' + OMAHA_PROXY); - var platforms = await loadJSON(OMAHA_PROXY); - if (!platforms) { - console.error('ERROR: failed to fetch chromium revisions from omahaproxy.'); - return; - } - var table = new Table([27, 7, 7, 7, 7]); - table.drawRow([''].concat(Downloader.supportedPlatforms())); - for (var platform of platforms) { - // Trust only to the main platforms. - if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux') - continue; - var osName = platform.os === 'win' ? 'win32' : platform.os; - for (var version of platform.versions) { - if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable') - continue; - var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15); - var revision = parseInt(version.branch_base_position, 10); - await checkAndDrawRevisionAvailability(table, revisionName, revision); - } + console.log('Fetching revisions from ' + OMAHA_PROXY); + var platforms = await loadJSON(OMAHA_PROXY); + if (!platforms) { + console.error('ERROR: failed to fetch chromium revisions from omahaproxy.'); + return; + } + var table = new Table([27, 7, 7, 7, 7]); + table.drawRow([''].concat(Downloader.supportedPlatforms())); + for (var platform of platforms) { + // Trust only to the main platforms. + if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux') + continue; + var osName = platform.os === 'win' ? 'win32' : platform.os; + for (var version of platform.versions) { + if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable') + continue; + var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15); + var revision = parseInt(version.branch_base_position, 10); + await checkAndDrawRevisionAvailability(table, revisionName, revision); } + } } /** @@ -96,11 +96,11 @@ async function checkOmahaProxyAvailability() { * @return {!Promise} */ async function checkRangeAvailability(fromRevision, toRevision) { - var table = new Table([10, 7, 7, 7, 7]); - table.drawRow([''].concat(Downloader.supportedPlatforms())); - var inc = fromRevision < toRevision ? 1 : -1; - for (var revision = fromRevision; revision !== toRevision; revision += inc) - await checkAndDrawRevisionAvailability(table, '', revision); + var table = new Table([10, 7, 7, 7, 7]); + table.drawRow([''].concat(Downloader.supportedPlatforms())); + var inc = fromRevision < toRevision ? 1 : -1; + for (var revision = fromRevision; revision !== toRevision; revision += inc) + await checkAndDrawRevisionAvailability(table, '', revision); } /** @@ -110,18 +110,18 @@ async function checkRangeAvailability(fromRevision, toRevision) { * @return {!Promise} */ async function checkAndDrawRevisionAvailability(table, name, revision) { - var promises = []; - for (var platform of Downloader.supportedPlatforms()) - promises.push(Downloader.canDownloadRevision(platform, revision)); - var availability = await Promise.all(promises); - var allAvailable = availability.every(e => !!e); - var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)]; - for (var i = 0; i < availability.length; ++i) { - var decoration = availability[i] ? '+' : '-'; - var color = availability[i] ? colors.green : colors.red; - values.push(color + decoration + colors.reset); - } - table.drawRow(values); + var promises = []; + for (var platform of Downloader.supportedPlatforms()) + promises.push(Downloader.canDownloadRevision(platform, revision)); + var availability = await Promise.all(promises); + var allAvailable = availability.every(e => !!e); + var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)]; + for (var i = 0; i < availability.length; ++i) { + var decoration = availability[i] ? '+' : '-'; + var color = availability[i] ? colors.green : colors.red; + values.push(color + decoration + colors.reset); + } + table.drawRow(values); } /** @@ -129,26 +129,26 @@ async function checkAndDrawRevisionAvailability(table, name, revision) { * @return {!Promise} */ function loadJSON(url) { - var resolve; - var promise = new Promise(x => resolve = x); - https.get(url, response => { - if (response.statusCode !== 200) { - resolve(null); - return; - } - var body = ''; - response.on('data', function(chunk){ - body += chunk; - }); - response.on('end', function(){ - var json = JSON.parse(body); - resolve(json); - }); - }).on('error', function(e){ - console.error('Error fetching json: ' + e); - resolve(null); + var resolve; + var promise = new Promise(x => resolve = x); + https.get(url, response => { + if (response.statusCode !== 200) { + resolve(null); + return; + } + var body = ''; + response.on('data', function(chunk){ + body += chunk; }); - return promise; + response.on('end', function(){ + var json = JSON.parse(body); + resolve(json); + }); + }).on('error', function(e){ + console.error('Error fetching json: ' + e); + resolve(null); + }); + return promise; } /** @@ -156,7 +156,7 @@ function loadJSON(url) { * @return {string} */ function spaceString(size) { - return new Array(size).fill(' ').join(''); + return new Array(size).fill(' ').join(''); } /** @@ -164,11 +164,11 @@ function spaceString(size) { * @return {string} */ function filterOutColors(text) { - for (var colorName in colors) { - var color = colors[colorName]; - text = text.replace(color, ''); - } - return text; + for (var colorName in colors) { + var color = colors[colorName]; + text = text.replace(color, ''); + } + return text; } /** @@ -177,8 +177,8 @@ function filterOutColors(text) { * @return {string} */ function padLeft(text, length) { - var printableCharacters = filterOutColors(text); - return printableCharacters.length >= length ? text : spaceString(length - text.length) + text; + var printableCharacters = filterOutColors(text); + return printableCharacters.length >= length ? text : spaceString(length - text.length) + text; } /** @@ -187,10 +187,10 @@ function padLeft(text, length) { * @return {string} */ function padCenter(text, length) { - var printableCharacters = filterOutColors(text); - if (printableCharacters.length >= length) - return text; - var left = Math.floor((length - printableCharacters.length) / 2); - var right = Math.ceil((length - printableCharacters.length) / 2); - return spaceString(left) + text + spaceString(right); + var printableCharacters = filterOutColors(text); + if (printableCharacters.length >= length) + return text; + var left = Math.floor((length - printableCharacters.length) / 2); + var right = Math.ceil((length - printableCharacters.length) / 2); + return spaceString(left) + text + spaceString(right); } diff --git a/yarn.lock b/yarn.lock index 9e03dc6eb1f..588bbae10d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -753,6 +753,10 @@ table@^4.0.1: slice-ansi "0.0.4" string-width "^2.0.0" +text-diff@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/text-diff/-/text-diff-1.0.1.tgz#6c105905435e337857375c9d2f6ca63e453ff565" + text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"