mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
Reformat code using 2 spaces
This commit is contained in:
parent
bc0655b587
commit
437a93b26e
@ -104,7 +104,7 @@ module.exports = {
|
|||||||
* Disabled, aspirational rules
|
* Disabled, aspirational rules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"indent": [0, 4, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
|
"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
|
||||||
|
|
||||||
// brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
|
// brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
|
||||||
"brace-style": [0, "allman", { "allowSingleLine": true }],
|
"brace-style": [0, "allman", { "allowSingleLine": true }],
|
||||||
|
2
index.js
2
index.js
@ -15,5 +15,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Browser: require('./lib/Browser')
|
Browser: require('./lib/Browser')
|
||||||
};
|
};
|
||||||
|
26
install.js
26
install.js
@ -20,28 +20,28 @@ var ProgressBar = require('progress');
|
|||||||
|
|
||||||
// Do nothing if the revision is already downloaded.
|
// Do nothing if the revision is already downloaded.
|
||||||
if (Downloader.revisionInfo(Downloader.currentPlatform(), revision))
|
if (Downloader.revisionInfo(Downloader.currentPlatform(), revision))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Downloader.downloadRevision(Downloader.currentPlatform(), revision, onProgress)
|
Downloader.downloadRevision(Downloader.currentPlatform(), revision, onProgress)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Download failed: ' + error.message);
|
console.error('Download failed: ' + error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
var progressBar = null;
|
var progressBar = null;
|
||||||
function onProgress(bytesTotal, delta) {
|
function onProgress(bytesTotal, delta) {
|
||||||
if (!progressBar) {
|
if (!progressBar) {
|
||||||
progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, {
|
progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, {
|
||||||
complete: '=',
|
complete: '=',
|
||||||
incomplete: ' ',
|
incomplete: ' ',
|
||||||
width: 20,
|
width: 20,
|
||||||
total: bytesTotal,
|
total: bytesTotal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
progressBar.tick(delta);
|
progressBar.tick(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMegabytes(bytes) {
|
function toMegabytes(bytes) {
|
||||||
var mb = bytes / 1024 / 1024;
|
var mb = bytes / 1024 / 1024;
|
||||||
return (Math.round(mb * 10) / 10) + ' Mb';
|
return (Math.round(mb * 10) / 10) + ' Mb';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
194
lib/Browser.js
194
lib/Browser.js
@ -26,125 +26,125 @@ var CHROME_PROFILE_PATH = path.resolve(__dirname, '..', '.dev_profile');
|
|||||||
var browserId = 0;
|
var browserId = 0;
|
||||||
|
|
||||||
var DEFAULT_ARGS = [
|
var DEFAULT_ARGS = [
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--no-first-run',
|
'--no-first-run',
|
||||||
];
|
];
|
||||||
|
|
||||||
class Browser {
|
class Browser {
|
||||||
/**
|
/**
|
||||||
* @param {(!Object|undefined)} options
|
* @param {(!Object|undefined)} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
++browserId;
|
++browserId;
|
||||||
this._userDataDir = CHROME_PROFILE_PATH + browserId;
|
this._userDataDir = CHROME_PROFILE_PATH + browserId;
|
||||||
this._remoteDebuggingPort = 9227;
|
this._remoteDebuggingPort = 9227;
|
||||||
if (typeof options.remoteDebuggingPort === 'number')
|
if (typeof options.remoteDebuggingPort === 'number')
|
||||||
this._remoteDebuggingPort = options.remoteDebuggingPort;
|
this._remoteDebuggingPort = options.remoteDebuggingPort;
|
||||||
this._chromeArguments = DEFAULT_ARGS.concat([
|
this._chromeArguments = DEFAULT_ARGS.concat([
|
||||||
`--user-data-dir=${this._userDataDir}`,
|
`--user-data-dir=${this._userDataDir}`,
|
||||||
`--remote-debugging-port=${this._remoteDebuggingPort}`,
|
`--remote-debugging-port=${this._remoteDebuggingPort}`,
|
||||||
]);
|
]);
|
||||||
if (typeof options.headless !== 'boolean' || options.headless) {
|
if (typeof options.headless !== 'boolean' || options.headless) {
|
||||||
this._chromeArguments.push(...[
|
this._chromeArguments.push(...[
|
||||||
`--headless`,
|
`--headless`,
|
||||||
`--disable-gpu`,
|
`--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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise<!Page>}
|
* @return {!Promise<!Page>}
|
||||||
*/
|
*/
|
||||||
async newPage() {
|
async newPage() {
|
||||||
await this._ensureChromeIsRunning();
|
await this._ensureChromeIsRunning();
|
||||||
if (!this._chromeProcess || this._terminated)
|
if (!this._chromeProcess || this._terminated)
|
||||||
throw new Error('ERROR: this chrome instance is not alive any more!');
|
throw new Error('ERROR: this chrome instance is not alive any more!');
|
||||||
var client = await Connection.create(this._remoteDebuggingPort);
|
var client = await Connection.create(this._remoteDebuggingPort);
|
||||||
var page = await Page.create(client);
|
var page = await Page.create(client);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
*/
|
*/
|
||||||
async closePage(page) {
|
async closePage(page) {
|
||||||
if (!this._chromeProcess || this._terminated)
|
if (!this._chromeProcess || this._terminated)
|
||||||
throw new Error('ERROR: this chrome instance is not running');
|
throw new Error('ERROR: this chrome instance is not running');
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
async version() {
|
async version() {
|
||||||
await this._ensureChromeIsRunning();
|
await this._ensureChromeIsRunning();
|
||||||
var version = await Connection.version(this._remoteDebuggingPort);
|
var version = await Connection.version(this._remoteDebuggingPort);
|
||||||
return version.Browser;
|
return version.Browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _ensureChromeIsRunning() {
|
async _ensureChromeIsRunning() {
|
||||||
if (this._chromeProcess)
|
if (this._chromeProcess)
|
||||||
return;
|
return;
|
||||||
this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {});
|
this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {});
|
||||||
var stderr = '';
|
var stderr = '';
|
||||||
this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8'));
|
this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8'));
|
||||||
// Cleanup as processes exit.
|
// Cleanup as processes exit.
|
||||||
process.on('exit', () => this._chromeProcess.kill());
|
process.on('exit', () => this._chromeProcess.kill());
|
||||||
this._chromeProcess.on('exit', () => {
|
this._chromeProcess.on('exit', () => {
|
||||||
this._terminated = true;
|
this._terminated = true;
|
||||||
removeRecursive(this._userDataDir);
|
removeRecursive(this._userDataDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated);
|
await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated);
|
||||||
if (this._terminated)
|
if (this._terminated)
|
||||||
throw new Error('Failed to launch chrome! ' + stderr);
|
throw new Error('Failed to launch chrome! ' + stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
if (!this._chromeProcess)
|
if (!this._chromeProcess)
|
||||||
return;
|
return;
|
||||||
this._chromeProcess.kill();
|
this._chromeProcess.kill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Browser;
|
module.exports = Browser;
|
||||||
|
|
||||||
function waitForChromeResponsive(remoteDebuggingPort, shouldWaitCallback) {
|
function waitForChromeResponsive(remoteDebuggingPort, shouldWaitCallback) {
|
||||||
var fulfill;
|
var fulfill;
|
||||||
var promise = new Promise(x => fulfill = x);
|
var promise = new Promise(x => fulfill = x);
|
||||||
var options = {
|
var options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: remoteDebuggingPort,
|
port: remoteDebuggingPort,
|
||||||
path: '/json/list'
|
path: '/json/list'
|
||||||
};
|
};
|
||||||
var probeTimeout = 100;
|
var probeTimeout = 100;
|
||||||
sendRequest();
|
sendRequest();
|
||||||
return promise;
|
return promise;
|
||||||
|
|
||||||
function sendRequest() {
|
function sendRequest() {
|
||||||
var req = http.request(options, res => {
|
var req = http.request(options, res => {
|
||||||
fulfill();
|
fulfill();
|
||||||
});
|
});
|
||||||
req.on('error', e => {
|
req.on('error', e => {
|
||||||
if (shouldWaitCallback())
|
if (shouldWaitCallback())
|
||||||
setTimeout(sendRequest, probeTimeout);
|
setTimeout(sendRequest, probeTimeout);
|
||||||
else
|
else
|
||||||
fulfill();
|
fulfill();
|
||||||
});
|
});
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,90 +20,90 @@ var http = require('http');
|
|||||||
const COMMAND_TIMEOUT = 10000;
|
const COMMAND_TIMEOUT = 10000;
|
||||||
|
|
||||||
class Connection extends EventEmitter {
|
class Connection extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {number} port
|
* @param {number} port
|
||||||
* @param {string} pageId
|
* @param {string} pageId
|
||||||
* @param {!WebSocket} ws
|
* @param {!WebSocket} ws
|
||||||
*/
|
*/
|
||||||
constructor(port, pageId, ws) {
|
constructor(port, pageId, ws) {
|
||||||
super();
|
super();
|
||||||
this._port = port;
|
this._port = port;
|
||||||
this._pageId = pageId;
|
this._pageId = pageId;
|
||||||
this._lastId = 0;
|
this._lastId = 0;
|
||||||
/** @type {!Map<number, {resolve: function(*), reject: function(*), method: string}>}*/
|
/** @type {!Map<number, {resolve: function(*), reject: function(*), method: string}>}*/
|
||||||
this._callbacks = new Map();
|
this._callbacks = new Map();
|
||||||
|
|
||||||
this._ws = ws;
|
this._ws = ws;
|
||||||
this._ws.on('message', this._onMessage.bind(this));
|
this._ws.on('message', this._onMessage.bind(this));
|
||||||
this._ws.on('close', this._onClose.bind(this));
|
this._ws.on('close', this._onClose.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} method
|
* @param {string} method
|
||||||
* @param {(!Object|undefined)} params
|
* @param {(!Object|undefined)} params
|
||||||
* @return {!Promise<?Object>}
|
* @return {!Promise<?Object>}
|
||||||
*/
|
*/
|
||||||
send(method, params = {}) {
|
send(method, params = {}) {
|
||||||
var id = ++this._lastId;
|
var id = ++this._lastId;
|
||||||
var message = JSON.stringify({id, method, params});
|
var message = JSON.stringify({id, method, params});
|
||||||
this._ws.send(message);
|
this._ws.send(message);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._callbacks.set(id, {resolve, reject, method});
|
this._callbacks.set(id, {resolve, reject, method});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
_onMessage(message) {
|
_onMessage(message) {
|
||||||
var object = JSON.parse(message);
|
var object = JSON.parse(message);
|
||||||
if (object.id && this._callbacks.has(object.id)) {
|
if (object.id && this._callbacks.has(object.id)) {
|
||||||
var callback = this._callbacks.get(object.id);
|
var callback = this._callbacks.get(object.id);
|
||||||
this._callbacks.delete(object.id);
|
this._callbacks.delete(object.id);
|
||||||
if (object.error)
|
if (object.error)
|
||||||
callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`));
|
callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`));
|
||||||
else
|
else
|
||||||
callback.resolve(object.result);
|
callback.resolve(object.result);
|
||||||
} else {
|
} else {
|
||||||
console.assert(!object.id);
|
console.assert(!object.id);
|
||||||
this.emit(object.method, object.params);
|
this.emit(object.method, object.params);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onClose() {
|
_onClose() {
|
||||||
this._ws.removeAllListeners();
|
this._ws.removeAllListeners();
|
||||||
this._ws.close();
|
this._ws.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async dispose() {
|
async dispose() {
|
||||||
await runJsonCommand(this._port, `close/${this._pageId}`);
|
await runJsonCommand(this._port, `close/${this._pageId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} port
|
* @param {number} port
|
||||||
* @return {!Promise<!Connection>}
|
* @return {!Promise<!Connection>}
|
||||||
*/
|
*/
|
||||||
static async create(port) {
|
static async create(port) {
|
||||||
var newTab = await runJsonCommand(port, 'new');
|
var newTab = await runJsonCommand(port, 'new');
|
||||||
var url = newTab.webSocketDebuggerUrl;
|
var url = newTab.webSocketDebuggerUrl;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var ws = new WebSocket(url, { perMessageDeflate: false });
|
var ws = new WebSocket(url, { perMessageDeflate: false });
|
||||||
ws.on('open', () => resolve(new Connection(port, newTab.id, ws)));
|
ws.on('open', () => resolve(new Connection(port, newTab.id, ws)));
|
||||||
ws.on('error', reject);
|
ws.on('error', reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} port
|
* @param {number} port
|
||||||
* @return {!Promise<!Object>}
|
* @return {!Promise<!Object>}
|
||||||
*/
|
*/
|
||||||
static version(port) {
|
static version(port) {
|
||||||
return runJsonCommand(port, 'version');
|
return runJsonCommand(port, 'version');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,41 +112,41 @@ class Connection extends EventEmitter {
|
|||||||
* @return {!Promise<!Object>}
|
* @return {!Promise<!Object>}
|
||||||
*/
|
*/
|
||||||
function runJsonCommand(port, command) {
|
function runJsonCommand(port, command) {
|
||||||
var request = http.get({
|
var request = http.get({
|
||||||
hostname: 'localhost',
|
hostname: 'localhost',
|
||||||
port: port,
|
port: port,
|
||||||
path: '/json/' + command
|
path: '/json/' + command
|
||||||
}, onResponse);
|
}, onResponse);
|
||||||
request.setTimeout(COMMAND_TIMEOUT, onTimeout);
|
request.setTimeout(COMMAND_TIMEOUT, onTimeout);
|
||||||
var resolve, reject;
|
var resolve, reject;
|
||||||
return new Promise((res, rej) => { resolve = res; reject = rej; });
|
return new Promise((res, rej) => { resolve = res; reject = rej; });
|
||||||
|
|
||||||
function onResponse(response) {
|
function onResponse(response) {
|
||||||
var data = '';
|
var data = '';
|
||||||
response.on('data', chunk => data += chunk);
|
response.on('data', chunk => data += chunk);
|
||||||
response.on('end', () => {
|
response.on('end', () => {
|
||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`));
|
reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD
|
// In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD
|
||||||
if (data === 'Target is closing' || data === 'Target activated') {
|
if (data === 'Target is closing' || data === 'Target activated') {
|
||||||
resolve({message: data});
|
resolve({message: data});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
resolve(JSON.parse(data));
|
resolve(JSON.parse(data));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTimeout() {
|
function onTimeout() {
|
||||||
request.abort();
|
request.abort();
|
||||||
// Reject on error with code specifically indicating timeout in connection setup.
|
// Reject on error with code specifically indicating timeout in connection setup.
|
||||||
reject(new Error('Timeout waiting for initial Debugger Protocol connection.'));
|
reject(new Error('Timeout waiting for initial Debugger Protocol connection.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Connection;
|
module.exports = Connection;
|
||||||
|
@ -15,55 +15,55 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class Dialog {
|
class Dialog {
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @param {!Dialog.Type} type
|
* @param {!Dialog.Type} type
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
constructor(client, type, message) {
|
constructor(client, type, message) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this._message = message;
|
this._message = message;
|
||||||
this._handled = false;
|
this._handled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
message() {
|
message() {
|
||||||
return this._message;
|
return this._message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string=} promptText
|
* @param {string=} promptText
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async accept(promptText) {
|
async accept(promptText) {
|
||||||
console.assert(!this._handled, 'Cannot accept dialog which is already handled!');
|
console.assert(!this._handled, 'Cannot accept dialog which is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
await this._client.send('Page.handleJavaScriptDialog', {
|
await this._client.send('Page.handleJavaScriptDialog', {
|
||||||
accept: true,
|
accept: true,
|
||||||
promptText: promptText
|
promptText: promptText
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async dismiss() {
|
async dismiss() {
|
||||||
console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
|
console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
await this._client.send('Page.handleJavaScriptDialog', {
|
await this._client.send('Page.handleJavaScriptDialog', {
|
||||||
accept: false
|
accept: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog.Type = {
|
Dialog.Type = {
|
||||||
Alert: 'alert',
|
Alert: 'alert',
|
||||||
BeforeUnload: 'beforeunload',
|
BeforeUnload: 'beforeunload',
|
||||||
Confirm: 'confirm',
|
Confirm: 'confirm',
|
||||||
Prompt: 'prompt'
|
Prompt: 'prompt'
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Dialog;
|
module.exports = Dialog;
|
||||||
|
@ -17,229 +17,229 @@
|
|||||||
var EventEmitter = require('events');
|
var EventEmitter = require('events');
|
||||||
|
|
||||||
class FrameManager extends EventEmitter {
|
class FrameManager extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @return {!FrameManager}
|
* @return {!FrameManager}
|
||||||
*/
|
*/
|
||||||
static async create(client) {
|
static async create(client) {
|
||||||
var mainFramePayload = await client.send('Page.getResourceTree');
|
var mainFramePayload = await client.send('Page.getResourceTree');
|
||||||
return new FrameManager(client, mainFramePayload.frameTree);
|
return new FrameManager(client, mainFramePayload.frameTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @param {!Object} frameTree
|
* @param {!Object} frameTree
|
||||||
*/
|
*/
|
||||||
constructor(client, frameTree) {
|
constructor(client, frameTree) {
|
||||||
super();
|
super();
|
||||||
this._client = client;
|
this._client = client;
|
||||||
/** @type {!Map<string, !Frame>} */
|
/** @type {!Map<string, !Frame>} */
|
||||||
this._frames = new Map();
|
this._frames = new Map();
|
||||||
this._mainFrame = this._addFramesRecursively(null, frameTree);
|
this._mainFrame = this._addFramesRecursively(null, frameTree);
|
||||||
|
|
||||||
this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId));
|
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.frameNavigated', event => this._frameNavigated(event.frame));
|
||||||
this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId));
|
this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Frame}
|
* @return {!Frame}
|
||||||
*/
|
*/
|
||||||
mainFrame() {
|
mainFrame() {
|
||||||
return this._mainFrame;
|
return this._mainFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Array<!Frame>}
|
* @return {!Array<!Frame>}
|
||||||
*/
|
*/
|
||||||
frames() {
|
frames() {
|
||||||
return Array.from(this._frames.values());
|
return Array.from(this._frames.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
* @param {?string} parentFrameId
|
* @param {?string} parentFrameId
|
||||||
* @return {?Frame}
|
* @return {?Frame}
|
||||||
*/
|
*/
|
||||||
_frameAttached(frameId, parentFrameId) {
|
_frameAttached(frameId, parentFrameId) {
|
||||||
if (this._frames.has(frameId))
|
if (this._frames.has(frameId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!parentFrameId) {
|
if (!parentFrameId) {
|
||||||
// Navigation to the new backend process.
|
// Navigation to the new backend process.
|
||||||
this._navigateFrame(this._mainFrame, frameId, null);
|
this._navigateFrame(this._mainFrame, frameId, null);
|
||||||
return;
|
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);
|
|
||||||
}
|
}
|
||||||
|
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 {!Object} framePayload
|
* @param {!Object} framePayload
|
||||||
*/
|
*/
|
||||||
_frameNavigated(framePayload) {
|
_frameNavigated(framePayload) {
|
||||||
var frame = this._frames.get(framePayload.id);
|
var frame = this._frames.get(framePayload.id);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
// Simulate missed "frameAttached" for a main frame navigation to the new backend process.
|
// 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.');
|
console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.');
|
||||||
frame = this._mainFrame;
|
frame = this._mainFrame;
|
||||||
}
|
|
||||||
this._navigateFrame(frame, framePayload.id, framePayload);
|
|
||||||
}
|
}
|
||||||
|
this._navigateFrame(frame, framePayload.id, framePayload);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
*/
|
*/
|
||||||
_frameDetached(frameId) {
|
_frameDetached(frameId) {
|
||||||
var frame = this._frames.get(frameId);
|
var frame = this._frames.get(frameId);
|
||||||
if (frame)
|
if (frame)
|
||||||
this._removeFramesRecursively(frame);
|
this._removeFramesRecursively(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Frame} frame
|
* @param {!Frame} frame
|
||||||
* @param {string} newFrameId
|
* @param {string} newFrameId
|
||||||
* @param {?Object} newFramePayload
|
* @param {?Object} newFramePayload
|
||||||
*/
|
*/
|
||||||
_navigateFrame(frame, newFrameId, newFramePayload) {
|
_navigateFrame(frame, newFrameId, newFramePayload) {
|
||||||
// Detach all child frames first.
|
// Detach all child frames first.
|
||||||
for (var child of frame.childFrames())
|
for (var child of frame.childFrames())
|
||||||
this._removeFramesRecursively(child);
|
this._removeFramesRecursively(child);
|
||||||
this._frames.delete(frame._id, frame);
|
this._frames.delete(frame._id, frame);
|
||||||
frame._id = newFrameId;
|
frame._id = newFrameId;
|
||||||
frame._adoptPayload(newFramePayload);
|
frame._adoptPayload(newFramePayload);
|
||||||
this._frames.set(newFrameId, frame);
|
this._frames.set(newFrameId, frame);
|
||||||
this.emit(FrameManager.Events.FrameNavigated, frame);
|
this.emit(FrameManager.Events.FrameNavigated, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {?Frame} parentFrame
|
* @param {?Frame} parentFrame
|
||||||
* @param {!Object} frameTreePayload
|
* @param {!Object} frameTreePayload
|
||||||
* @return {!Frame}
|
* @return {!Frame}
|
||||||
*/
|
*/
|
||||||
_addFramesRecursively(parentFrame, frameTreePayload) {
|
_addFramesRecursively(parentFrame, frameTreePayload) {
|
||||||
var framePayload = frameTreePayload.frame;
|
var framePayload = frameTreePayload.frame;
|
||||||
var frame = new Frame(parentFrame, framePayload.id, framePayload);
|
var frame = new Frame(parentFrame, framePayload.id, framePayload);
|
||||||
this._frames.set(frame._id, frame);
|
this._frames.set(frame._id, frame);
|
||||||
|
|
||||||
for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
|
for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
|
||||||
this._addFramesRecursively(frame, frameTreePayload.childFrames[i]);
|
this._addFramesRecursively(frame, frameTreePayload.childFrames[i]);
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Frame} frame
|
* @param {!Frame} frame
|
||||||
*/
|
*/
|
||||||
_removeFramesRecursively(frame) {
|
_removeFramesRecursively(frame) {
|
||||||
for (var child of frame.childFrames())
|
for (var child of frame.childFrames())
|
||||||
this._removeFramesRecursively(child);
|
this._removeFramesRecursively(child);
|
||||||
frame._detach();
|
frame._detach();
|
||||||
this._frames.delete(frame._id);
|
this._frames.delete(frame._id);
|
||||||
this.emit(FrameManager.Events.FrameDetached, frame);
|
this.emit(FrameManager.Events.FrameDetached, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
FrameManager.Events = {
|
FrameManager.Events = {
|
||||||
FrameAttached: 'frameattached',
|
FrameAttached: 'frameattached',
|
||||||
FrameNavigated: 'framenavigated',
|
FrameNavigated: 'framenavigated',
|
||||||
FrameDetached: 'framedetached'
|
FrameDetached: 'framedetached'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @unrestricted
|
* @unrestricted
|
||||||
*/
|
*/
|
||||||
class Frame {
|
class Frame {
|
||||||
/**
|
/**
|
||||||
* @param {?Frame} parentFrame
|
* @param {?Frame} parentFrame
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
* @param {?Object} payload
|
* @param {?Object} payload
|
||||||
*/
|
*/
|
||||||
constructor(parentFrame, frameId, payload) {
|
constructor(parentFrame, frameId, payload) {
|
||||||
this._parentFrame = parentFrame;
|
this._parentFrame = parentFrame;
|
||||||
this._url = '';
|
this._url = '';
|
||||||
this._id = frameId;
|
this._id = frameId;
|
||||||
this._adoptPayload(payload);
|
this._adoptPayload(payload);
|
||||||
|
|
||||||
/** @type {!Set<!Frame>} */
|
/** @type {!Set<!Frame>} */
|
||||||
this._childFrames = new Set();
|
this._childFrames = new Set();
|
||||||
if (this._parentFrame)
|
if (this._parentFrame)
|
||||||
this._parentFrame._childFrames.add(this);
|
this._parentFrame._childFrames.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
name() {
|
name() {
|
||||||
return this._name || '';
|
return this._name || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
url() {
|
url() {
|
||||||
return this._url;
|
return this._url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
securityOrigin() {
|
securityOrigin() {
|
||||||
return this._securityOrigin;
|
return this._securityOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {?Frame}
|
* @return {?Frame}
|
||||||
*/
|
*/
|
||||||
parentFrame() {
|
parentFrame() {
|
||||||
return this._parentFrame;
|
return this._parentFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Array.<!Frame>}
|
* @return {!Array.<!Frame>}
|
||||||
*/
|
*/
|
||||||
childFrames() {
|
childFrames() {
|
||||||
return Array.from(this._childFrames);
|
return Array.from(this._childFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isMainFrame() {
|
isMainFrame() {
|
||||||
return !this._detached && !this._parentFrame;
|
return !this._detached && !this._parentFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isDetached() {
|
isDetached() {
|
||||||
return this._detached;
|
return this._detached;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {?Object} framePayload
|
* @param {?Object} framePayload
|
||||||
*/
|
*/
|
||||||
_adoptPayload(framePayload) {
|
_adoptPayload(framePayload) {
|
||||||
framePayload = framePayload || {
|
framePayload = framePayload || {
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
securityOrigin: '',
|
securityOrigin: '',
|
||||||
mimeType: ''
|
mimeType: ''
|
||||||
};
|
};
|
||||||
this._name = framePayload.name;
|
this._name = framePayload.name;
|
||||||
this._url = framePayload.url;
|
this._url = framePayload.url;
|
||||||
this._securityOrigin = framePayload.securityOrigin;
|
this._securityOrigin = framePayload.securityOrigin;
|
||||||
this._mimeType = framePayload.mimeType;
|
this._mimeType = framePayload.mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
_detach() {
|
_detach() {
|
||||||
this._detached = true;
|
this._detached = true;
|
||||||
if (this._parentFrame)
|
if (this._parentFrame)
|
||||||
this._parentFrame._childFrames.delete(this);
|
this._parentFrame._childFrames.delete(this);
|
||||||
this._parentFrame = null;
|
this._parentFrame = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FrameManager;
|
module.exports = FrameManager;
|
||||||
|
110
lib/Navigator.js
110
lib/Navigator.js
@ -15,84 +15,84 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class Navigator {
|
class Navigator {
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
*/
|
*/
|
||||||
constructor(client, options) {
|
constructor(client, options) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
client.on('Network.requestWillBeSent', event => this._onRequestWillBeSent(event));
|
client.on('Network.requestWillBeSent', event => this._onRequestWillBeSent(event));
|
||||||
client.on('Network.loadingFinished', event => this._onLoadingFinished(event));
|
client.on('Network.loadingFinished', event => this._onLoadingFinished(event));
|
||||||
client.on('Network.loadingFailed', event => this._onLoadingFailed(event));
|
client.on('Network.loadingFailed', event => this._onLoadingFailed(event));
|
||||||
this._minTime = options && options['minTime'] ? options['minTime'] : 0;
|
this._minTime = options && options['minTime'] ? options['minTime'] : 0;
|
||||||
this._maxTime = options && options['maxTime'] ? options['maxTime'] : 30000;
|
this._maxTime = options && options['maxTime'] ? options['maxTime'] : 30000;
|
||||||
this._inflightRequests = 0;
|
this._inflightRequests = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {string=} referrer
|
* @param {string=} referrer
|
||||||
*/
|
*/
|
||||||
async navigate(url, referrer) {
|
async navigate(url, referrer) {
|
||||||
this._navigationStartTime = Date.now();
|
this._navigationStartTime = Date.now();
|
||||||
this._watchdogTimer = setTimeout(this._completeNavigation.bind(this, true), this._maxTime);
|
this._watchdogTimer = setTimeout(this._completeNavigation.bind(this, true), this._maxTime);
|
||||||
this._minimumTimer = setTimeout(this._completeNavigation.bind(this, false), this._minTime);
|
this._minimumTimer = setTimeout(this._completeNavigation.bind(this, false), this._minTime);
|
||||||
let onload = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill));
|
let onload = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill));
|
||||||
let networkIdle = new Promise(fulfill => this._navigationLoadCallback = fulfill);
|
let networkIdle = new Promise(fulfill => this._navigationLoadCallback = fulfill);
|
||||||
var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false);
|
var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false);
|
||||||
|
|
||||||
this._inflightRequests = 0;
|
this._inflightRequests = 0;
|
||||||
// Await for the command to throw exception in case of illegal arguments.
|
// Await for the command to throw exception in case of illegal arguments.
|
||||||
try {
|
try {
|
||||||
await this._client.send('Page.navigate', {url, referrer});
|
await this._client.send('Page.navigate', {url, referrer});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return await Promise.race([Promise.all([onload, networkIdle]).then(() => true), interstitialPromise]);
|
|
||||||
}
|
}
|
||||||
|
return await Promise.race([Promise.all([onload, networkIdle]).then(() => true), interstitialPromise]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} event
|
* @param {!Object} event
|
||||||
*/
|
*/
|
||||||
_onRequestWillBeSent(event) {
|
_onRequestWillBeSent(event) {
|
||||||
if (!event.redirectResponse)
|
if (!event.redirectResponse)
|
||||||
++this._inflightRequests;
|
++this._inflightRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} event
|
* @param {!Object} event
|
||||||
*/
|
*/
|
||||||
_onLoadingFinished(event) {
|
_onLoadingFinished(event) {
|
||||||
this._onLoadingCompleted(event);
|
this._onLoadingCompleted(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} event
|
* @param {!Object} event
|
||||||
*/
|
*/
|
||||||
_onLoadingFailed(event) {
|
_onLoadingFailed(event) {
|
||||||
this._onLoadingCompleted(event);
|
this._onLoadingCompleted(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLoadingCompleted(event) {
|
_onLoadingCompleted(event) {
|
||||||
--this._inflightRequests;
|
--this._inflightRequests;
|
||||||
if (Date.now() - this._navigationStartTime < this._minTime)
|
if (Date.now() - this._navigationStartTime < this._minTime)
|
||||||
return;
|
return;
|
||||||
this._completeNavigation(false);
|
this._completeNavigation(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} force
|
* @param {boolean} force
|
||||||
*/
|
*/
|
||||||
_completeNavigation(force) {
|
_completeNavigation(force) {
|
||||||
if (!this._navigationLoadCallback)
|
if (!this._navigationLoadCallback)
|
||||||
return;
|
return;
|
||||||
if (this._inflightRequests < 2 || force) {
|
if (this._inflightRequests < 2 || force) {
|
||||||
clearTimeout(this._minimumTimer);
|
clearTimeout(this._minimumTimer);
|
||||||
clearTimeout(this._watchdogTimer);
|
clearTimeout(this._watchdogTimer);
|
||||||
this._navigationLoadCallback();
|
this._navigationLoadCallback();
|
||||||
this._navigationLoadCallback = null;
|
this._navigationLoadCallback = null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Navigator;
|
module.exports = Navigator;
|
||||||
|
832
lib/Page.js
832
lib/Page.js
@ -23,526 +23,526 @@ var Dialog = require('./Dialog');
|
|||||||
var FrameManager = require('./FrameManager');
|
var FrameManager = require('./FrameManager');
|
||||||
|
|
||||||
class Page extends EventEmitter {
|
class Page extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @return {!Promise<!Page>}
|
* @return {!Promise<!Page>}
|
||||||
*/
|
*/
|
||||||
static async create(client) {
|
static async create(client) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
client.send('Network.enable', {}),
|
client.send('Network.enable', {}),
|
||||||
client.send('Page.enable', {}),
|
client.send('Page.enable', {}),
|
||||||
client.send('Runtime.enable', {}),
|
client.send('Runtime.enable', {}),
|
||||||
client.send('Security.enable', {}),
|
client.send('Security.enable', {}),
|
||||||
]);
|
]);
|
||||||
var expression = Page._evaluationString(() => window.devicePixelRatio);
|
var expression = Page._evaluationString(() => window.devicePixelRatio);
|
||||||
var {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true });
|
var {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true });
|
||||||
var frameManager = await FrameManager.create(client);
|
var frameManager = await FrameManager.create(client);
|
||||||
var page = new Page(client, frameManager, screenDPI);
|
var page = new Page(client, frameManager, screenDPI);
|
||||||
// Initialize default page size.
|
// Initialize default page size.
|
||||||
await page.setViewportSize({width: 400, height: 300});
|
await page.setViewportSize({width: 400, height: 300});
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @param {!FrameManager} frameManager
|
* @param {!FrameManager} frameManager
|
||||||
* @param {number} screenDPI
|
* @param {number} screenDPI
|
||||||
*/
|
*/
|
||||||
constructor(client, frameManager, screenDPI) {
|
constructor(client, frameManager, screenDPI) {
|
||||||
super();
|
super();
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._frameManager = frameManager;
|
this._frameManager = frameManager;
|
||||||
this._screenDPI = screenDPI;
|
this._screenDPI = screenDPI;
|
||||||
this._extraHeaders = {};
|
this._extraHeaders = {};
|
||||||
/** @type {!Map<string, function>} */
|
/** @type {!Map<string, function>} */
|
||||||
this._inPageCallbacks = new Map();
|
this._inPageCallbacks = new Map();
|
||||||
/** @type {?function(!Request)} */
|
/** @type {?function(!Request)} */
|
||||||
this._requestInterceptor = null;
|
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.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.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.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event));
|
||||||
|
|
||||||
client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response));
|
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.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event));
|
||||||
|
|
||||||
client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event));
|
client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event));
|
||||||
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||||
client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
|
client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
|
||||||
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
|
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Frame}
|
* @return {!Frame}
|
||||||
*/
|
*/
|
||||||
mainFrame() {
|
mainFrame() {
|
||||||
return this._frameManager.mainFrame();
|
return this._frameManager.mainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Array<!Frame>}
|
* @return {!Array<!Frame>}
|
||||||
*/
|
*/
|
||||||
frames() {
|
frames() {
|
||||||
return this._frameManager.frames();
|
return this._frameManager.frames();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {?function(!Request)} interceptor
|
* @param {?function(!Request)} interceptor
|
||||||
*/
|
*/
|
||||||
async setRequestInterceptor(interceptor) {
|
async setRequestInterceptor(interceptor) {
|
||||||
this._requestInterceptor = interceptor;
|
this._requestInterceptor = interceptor;
|
||||||
await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor});
|
await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} event
|
* @param {!Object} event
|
||||||
*/
|
*/
|
||||||
_onRequestIntercepted(event) {
|
_onRequestIntercepted(event) {
|
||||||
var request = new Request(this._client, event.InterceptionId, event.request);
|
var request = new Request(this._client, event.InterceptionId, event.request);
|
||||||
this._requestInterceptor(request);
|
this._requestInterceptor(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async addScriptTag(url) {
|
async addScriptTag(url) {
|
||||||
return this.evaluate(addScriptTag, url);
|
return this.evaluate(addScriptTag, url);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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} 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
|
* @param {string} filePath
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async injectFile(filePath) {
|
async injectFile(filePath) {
|
||||||
var callback;
|
var callback;
|
||||||
var promise = new Promise(fulfill => callback = fulfill);
|
var promise = new Promise(fulfill => callback = fulfill);
|
||||||
var expression = fs.readFile(filePath, 'utf8', (err, data) => callback({err, data}));
|
var expression = fs.readFile(filePath, 'utf8', (err, data) => callback({err, data}));
|
||||||
await promise;
|
await promise;
|
||||||
return this._client.send('Runtime.evaluate', { expression, returnByValue: true });
|
return this._client.send('Runtime.evaluate', { expression, returnByValue: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {function(?)} callback
|
* @param {function(?)} callback
|
||||||
*/
|
*/
|
||||||
async setInPageCallback(name, callback) {
|
async setInPageCallback(name, callback) {
|
||||||
if (this._inPageCallbacks[name])
|
if (this._inPageCallbacks[name])
|
||||||
throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`);
|
throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`);
|
||||||
this._inPageCallbacks[name] = callback;
|
this._inPageCallbacks[name] = callback;
|
||||||
|
|
||||||
var expression = Page._evaluationString(inPageCallback, name);
|
var expression = Page._evaluationString(inPageCallback, name);
|
||||||
await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression });
|
await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression });
|
||||||
await this._client.send('Runtime.evaluate', { expression, returnByValue: true });
|
await this._client.send('Runtime.evaluate', { expression, returnByValue: true });
|
||||||
|
|
||||||
function inPageCallback(callbackName) {
|
function inPageCallback(callbackName) {
|
||||||
window[callbackName] = async(...args) => {
|
window[callbackName] = async(...args) => {
|
||||||
const me = window[callbackName];
|
const me = window[callbackName];
|
||||||
let callbacks = me['callbacks'];
|
let callbacks = me['callbacks'];
|
||||||
if (!callbacks) {
|
if (!callbacks) {
|
||||||
callbacks = new Map();
|
callbacks = new Map();
|
||||||
me['callbacks'] = callbacks;
|
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;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
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
|
* @param {!Object} headers
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async setExtraHTTPHeaders(headers) {
|
async setExtraHTTPHeaders(headers) {
|
||||||
this._extraHeaders = {};
|
this._extraHeaders = {};
|
||||||
// Note: header names are case-insensitive.
|
// Note: header names are case-insensitive.
|
||||||
for (var key of Object.keys(headers))
|
for (var key of Object.keys(headers))
|
||||||
this._extraHeaders[key.toLowerCase()] = headers[key];
|
this._extraHeaders[key.toLowerCase()] = headers[key];
|
||||||
return this._client.send('Network.setExtraHTTPHeaders', { headers });
|
return this._client.send('Network.setExtraHTTPHeaders', { headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Object}
|
* @return {!Object}
|
||||||
*/
|
*/
|
||||||
extraHTTPHeaders() {
|
extraHTTPHeaders() {
|
||||||
return Object.assign({}, this._extraHeaders);
|
return Object.assign({}, this._extraHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} userAgent
|
* @param {string} userAgent
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async setUserAgentOverride(userAgent) {
|
async setUserAgentOverride(userAgent) {
|
||||||
this._userAgent = userAgent;
|
this._userAgent = userAgent;
|
||||||
return this._client.send('Network.setUserAgentOverride', { userAgent });
|
return this._client.send('Network.setUserAgentOverride', { userAgent });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
userAgentOverride() {
|
userAgentOverride() {
|
||||||
return this._userAgent;
|
return this._userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} exceptionDetails
|
* @param {!Object} exceptionDetails
|
||||||
*/
|
*/
|
||||||
async _handleException(exceptionDetails) {
|
async _handleException(exceptionDetails) {
|
||||||
var message = await this._getExceptionMessage(exceptionDetails);
|
var message = await this._getExceptionMessage(exceptionDetails);
|
||||||
this.emit(Page.Events.Error, new Error(message));
|
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 = Page._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(' '));
|
||||||
|
}
|
||||||
|
|
||||||
async _onConsoleAPI(event) {
|
_onDialog(event) {
|
||||||
if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:InPageCallback') {
|
var dialogType = null;
|
||||||
var {name, seq, args} = JSON.parse(event.args[1].value);
|
if (event.type === 'alert')
|
||||||
var result = await this._inPageCallbacks[name](...args);
|
dialogType = Dialog.Type.Alert;
|
||||||
var expression = Page._evaluationString(deliverResult, name, seq, result);
|
else if (event.type === 'confirm')
|
||||||
this._client.send('Runtime.evaluate', { expression });
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
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<string>}
|
* @return {!Promise<string>}
|
||||||
*/
|
*/
|
||||||
async url() {
|
async url() {
|
||||||
return this.evaluate(() => window.location.href);
|
return this.evaluate(() => window.location.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async setContent(html) {
|
async setContent(html) {
|
||||||
this.evaluate(() => {
|
this.evaluate(() => {
|
||||||
document.open();
|
document.open();
|
||||||
document.write(html);
|
document.write(html);
|
||||||
document.close();
|
document.close();
|
||||||
}, html);
|
}, html);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
* @return {!Promise<boolean>}
|
* @return {!Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
navigate(url, options) {
|
navigate(url, options) {
|
||||||
return new Navigator(this._client, options).navigate(url, this._extraHeaders.referer);
|
return new Navigator(this._client, options).navigate(url, this._extraHeaders.referer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!{width: number, height: number}} size
|
* @param {!{width: number, height: number}} size
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async setViewportSize(size) {
|
async setViewportSize(size) {
|
||||||
this._viewportSize = size;
|
this._viewportSize = size;
|
||||||
var width = size.width;
|
var width = size.width;
|
||||||
var height = size.height;
|
var height = size.height;
|
||||||
var zoom = this._screenDPI;
|
var zoom = this._screenDPI;
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this._client.send('Emulation.setDeviceMetricsOverride', {
|
this._client.send('Emulation.setDeviceMetricsOverride', {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
deviceScaleFactor: 1,
|
deviceScaleFactor: 1,
|
||||||
scale: 1 / zoom,
|
scale: 1 / zoom,
|
||||||
mobile: false,
|
mobile: false,
|
||||||
fitWindow: false
|
fitWindow: false
|
||||||
}),
|
}),
|
||||||
this._client.send('Emulation.setVisibleSize', {
|
this._client.send('Emulation.setVisibleSize', {
|
||||||
width: width / zoom,
|
width: width / zoom,
|
||||||
height: height / zoom,
|
height: height / zoom,
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!{width: number, height: number}}
|
* @return {!{width: number, height: number}}
|
||||||
*/
|
*/
|
||||||
viewportSize() {
|
viewportSize() {
|
||||||
return this._viewportSize;
|
return this._viewportSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} fun
|
* @param {function()} fun
|
||||||
* @param {!Array<*>} args
|
* @param {!Array<*>} args
|
||||||
* @return {!Promise<(!Object|undefined)>}
|
* @return {!Promise<(!Object|undefined)>}
|
||||||
*/
|
*/
|
||||||
async evaluate(fun, ...args) {
|
async evaluate(fun, ...args) {
|
||||||
var syncExpression = Page._evaluationString(fun, ...args);
|
var syncExpression = Page._evaluationString(fun, ...args);
|
||||||
var expression = `Promise.resolve(${syncExpression})`;
|
var expression = `Promise.resolve(${syncExpression})`;
|
||||||
var { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true });
|
var { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true });
|
||||||
if (exceptionDetails) {
|
if (exceptionDetails) {
|
||||||
var message = await this._getExceptionMessage(exceptionDetails);
|
var message = await this._getExceptionMessage(exceptionDetails);
|
||||||
throw new Error('Evaluation failed: ' + message);
|
throw new Error('Evaluation failed: ' + message);
|
||||||
}
|
|
||||||
return remoteObject.value;
|
|
||||||
}
|
}
|
||||||
|
return remoteObject.value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} exceptionDetails
|
* @param {!Object} exceptionDetails
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
async _getExceptionMessage(exceptionDetails) {
|
async _getExceptionMessage(exceptionDetails) {
|
||||||
var message = '';
|
var message = '';
|
||||||
var exception = exceptionDetails.exception;
|
var exception = exceptionDetails.exception;
|
||||||
if (exception) {
|
if (exception) {
|
||||||
var response = await this._client.send('Runtime.callFunctionOn', {
|
var response = await this._client.send('Runtime.callFunctionOn', {
|
||||||
objectId: exception.objectId,
|
objectId: exception.objectId,
|
||||||
functionDeclaration: 'function() { return this.message; }',
|
functionDeclaration: 'function() { return this.message; }',
|
||||||
returnByValue: true,
|
returnByValue: true,
|
||||||
});
|
});
|
||||||
message = response.result.value;
|
message = response.result.value;
|
||||||
} else {
|
} else {
|
||||||
message = exceptionDetails.text;
|
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 || '<anonymous>';
|
|
||||||
message += `\n at ${functionName} (${location})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (exceptionDetails.stackTrace) {
|
||||||
|
for (var callframe of exceptionDetails.stackTrace.callFrames) {
|
||||||
|
var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber;
|
||||||
|
var functionName = callframe.functionName || '<anonymous>';
|
||||||
|
message += `\n at ${functionName} (${location})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @param {function()} fun
|
* @param {function()} fun
|
||||||
* @param {!Array<*>} args
|
* @param {!Array<*>} args
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async evaluateOnInitialized(fun, ...args) {
|
async evaluateOnInitialized(fun, ...args) {
|
||||||
var scriptSource = Page._evaluationString(fun, ...args);
|
var scriptSource = Page._evaluationString(fun, ...args);
|
||||||
await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource });
|
await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} fun
|
* @param {function()} fun
|
||||||
* @param {!Array<*>} args
|
* @param {!Array<*>} args
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
static _evaluationString(fun, ...args) {
|
static _evaluationString(fun, ...args) {
|
||||||
return `(${fun})(${args.map(x => JSON.stringify(x)).join(',')})`;
|
return `(${fun})(${args.map(x => JSON.stringify(x)).join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
* @return {!Promise<!Buffer>}
|
* @return {!Promise<!Buffer>}
|
||||||
*/
|
*/
|
||||||
async screenshot(options) {
|
async screenshot(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var screenshotType = null;
|
var screenshotType = null;
|
||||||
if (options.path) {
|
if (options.path) {
|
||||||
var mimeType = mime.lookup(options.path);
|
var mimeType = mime.lookup(options.path);
|
||||||
if (mimeType === 'image/png')
|
if (mimeType === 'image/png')
|
||||||
screenshotType = 'png';
|
screenshotType = 'png';
|
||||||
else if (mimeType === 'image/jpeg')
|
else if (mimeType === 'image/jpeg')
|
||||||
screenshotType = 'jpeg';
|
screenshotType = 'jpeg';
|
||||||
console.assert(screenshotType, 'Unsupported screenshot mime type: ' + mimeType);
|
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;
|
|
||||||
}
|
}
|
||||||
|
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 {string} screenshotType
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
* @return {!Promise<!Buffer>}
|
* @return {!Promise<!Buffer>}
|
||||||
*/
|
*/
|
||||||
async _screenshotTask(screenshotType, options) {
|
async _screenshotTask(screenshotType, options) {
|
||||||
if (options.clip) {
|
if (options.clip) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._client.send('Emulation.setVisibleSize', {
|
this._client.send('Emulation.setVisibleSize', {
|
||||||
width: Math.ceil(options.clip.width / this._screenDPI),
|
width: Math.ceil(options.clip.width / this._screenDPI),
|
||||||
height: Math.ceil(options.clip.height / this._screenDPI),
|
height: Math.ceil(options.clip.height / this._screenDPI),
|
||||||
}),
|
}),
|
||||||
this._client.send('Emulation.forceViewport', {
|
this._client.send('Emulation.forceViewport', {
|
||||||
x: options.clip.x / this._screenDPI,
|
x: options.clip.x / this._screenDPI,
|
||||||
y: options.clip.y / this._screenDPI,
|
y: options.clip.y / this._screenDPI,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
} else if (options.fullPage) {
|
} else if (options.fullPage) {
|
||||||
var response = await this._client.send('Page.getLayoutMetrics');
|
var response = await this._client.send('Page.getLayoutMetrics');
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._client.send('Emulation.setVisibleSize', {
|
this._client.send('Emulation.setVisibleSize', {
|
||||||
width: Math.ceil(response.contentSize.width / this._screenDPI),
|
width: Math.ceil(response.contentSize.width / this._screenDPI),
|
||||||
height: Math.ceil(response.contentSize.height / this._screenDPI),
|
height: Math.ceil(response.contentSize.height / this._screenDPI),
|
||||||
}),
|
}),
|
||||||
this._client.send('Emulation.forceViewport', {
|
this._client.send('Emulation.forceViewport', {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
scale: 1,
|
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;
|
|
||||||
}
|
}
|
||||||
|
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 {string} filePath
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async printToPDF(filePath, options) {
|
async printToPDF(filePath, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
var scale = options.scale || 1;
|
var scale = options.scale || 1;
|
||||||
var displayHeaderFooter = options.displayHeaderFooter || false;
|
var displayHeaderFooter = options.displayHeaderFooter || false;
|
||||||
var printBackground = options.printBackground || true;
|
var printBackground = options.printBackground || true;
|
||||||
var landscape = options.landscape || false;
|
var landscape = options.landscape || false;
|
||||||
var pageRanges = options.pageRanges || '';
|
var pageRanges = options.pageRanges || '';
|
||||||
|
|
||||||
var paperWidth = 8.5;
|
var paperWidth = 8.5;
|
||||||
var paperHeight = 11;
|
var paperHeight = 11;
|
||||||
if (options.format) {
|
if (options.format) {
|
||||||
var format = Page.PaperFormats[options.format];
|
var format = Page.PaperFormats[options.format];
|
||||||
console.assert(format, 'Unknown paper format: ' + options.format);
|
console.assert(format, 'Unknown paper format: ' + options.format);
|
||||||
paperWidth = format.width;
|
paperWidth = format.width;
|
||||||
paperHeight = format.height;
|
paperHeight = format.height;
|
||||||
} else {
|
} else {
|
||||||
paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
|
paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
|
||||||
paperHeight = convertPrintParameterToInches(options.height) || paperHeight;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
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<string>}
|
* @return {!Promise<string>}
|
||||||
*/
|
*/
|
||||||
async plainText() {
|
async plainText() {
|
||||||
return this.evaluate(() => document.body.innerText);
|
return this.evaluate(() => document.body.innerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise<string>}
|
* @return {!Promise<string>}
|
||||||
*/
|
*/
|
||||||
async title() {
|
async title() {
|
||||||
return this.evaluate(() => document.title);
|
return this.evaluate(() => document.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async close() {
|
async close() {
|
||||||
await this._client.dispose();
|
await this._client.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
Page.PaperFormats = {
|
Page.PaperFormats = {
|
||||||
Letter: {width: 8.5, height: 11},
|
Letter: {width: 8.5, height: 11},
|
||||||
Legal: {width: 8.5, height: 14},
|
Legal: {width: 8.5, height: 14},
|
||||||
Tabloid: {width: 11, height: 17},
|
Tabloid: {width: 11, height: 17},
|
||||||
Ledger: {width: 17, height: 11},
|
Ledger: {width: 17, height: 11},
|
||||||
A0: {width: 33.1, height: 46.8 },
|
A0: {width: 33.1, height: 46.8 },
|
||||||
A1: {width: 23.4, height: 33.1 },
|
A1: {width: 23.4, height: 33.1 },
|
||||||
A2: {width: 16.5, height: 23.4 },
|
A2: {width: 16.5, height: 23.4 },
|
||||||
A3: {width: 11.7, height: 16.5 },
|
A3: {width: 11.7, height: 16.5 },
|
||||||
A4: {width: 8.27, height: 11.7 },
|
A4: {width: 8.27, height: 11.7 },
|
||||||
A5: {width: 5.83, height: 8.27 },
|
A5: {width: 5.83, height: 8.27 },
|
||||||
};
|
};
|
||||||
|
|
||||||
var unitToPixels = {
|
var unitToPixels = {
|
||||||
'px': 1,
|
'px': 1,
|
||||||
'in': 96,
|
'in': 96,
|
||||||
'cm': 37.8,
|
'cm': 37.8,
|
||||||
'mm': 3.78
|
'mm': 3.78
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -550,42 +550,42 @@ var unitToPixels = {
|
|||||||
* @return {(number|undefined)}
|
* @return {(number|undefined)}
|
||||||
*/
|
*/
|
||||||
function convertPrintParameterToInches(parameter) {
|
function convertPrintParameterToInches(parameter) {
|
||||||
if (typeof parameter === 'undefined')
|
if (typeof parameter === 'undefined')
|
||||||
return undefined;
|
return undefined;
|
||||||
var pixels;
|
var pixels;
|
||||||
if (typeof parameter === 'number') {
|
if (typeof parameter === 'number') {
|
||||||
// Treat numbers as pixel values to be aligned with phantom's paperSize.
|
// Treat numbers as pixel values to be aligned with phantom's paperSize.
|
||||||
pixels = /** @type {number} */ (parameter);
|
pixels = /** @type {number} */ (parameter);
|
||||||
} else if (typeof parameter === 'string') {
|
} else if (typeof parameter === 'string') {
|
||||||
var text = parameter;
|
var text = parameter;
|
||||||
var unit = text.substring(text.length - 2).toLowerCase();
|
var unit = text.substring(text.length - 2).toLowerCase();
|
||||||
var valueText = '';
|
var valueText = '';
|
||||||
if (unitToPixels.hasOwnProperty(unit)) {
|
if (unitToPixels.hasOwnProperty(unit)) {
|
||||||
valueText = text.substring(0, text.length - 2);
|
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];
|
|
||||||
} else {
|
} 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 = {
|
Page.Events = {
|
||||||
ConsoleMessage: 'consolemessage',
|
ConsoleMessage: 'consolemessage',
|
||||||
Dialog: 'dialog',
|
Dialog: 'dialog',
|
||||||
Error: 'error',
|
Error: 'error',
|
||||||
ResourceLoadingFailed: 'resourceloadingfailed',
|
ResourceLoadingFailed: 'resourceloadingfailed',
|
||||||
ResponseReceived: 'responsereceived',
|
ResponseReceived: 'responsereceived',
|
||||||
FrameAttached: 'frameattached',
|
FrameAttached: 'frameattached',
|
||||||
FrameDetached: 'framedetached',
|
FrameDetached: 'framedetached',
|
||||||
FrameNavigated: 'framenavigated',
|
FrameNavigated: 'framenavigated',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Page;
|
module.exports = Page;
|
||||||
|
140
lib/Request.js
140
lib/Request.js
@ -15,112 +15,112 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
/**
|
/**
|
||||||
* @param {!Connection} client
|
* @param {!Connection} client
|
||||||
* @param {string} interceptionId
|
* @param {string} interceptionId
|
||||||
* @param {!Object} payload
|
* @param {!Object} payload
|
||||||
*/
|
*/
|
||||||
constructor(client, interceptionId, payload) {
|
constructor(client, interceptionId, payload) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._interceptionId = interceptionId;
|
this._interceptionId = interceptionId;
|
||||||
this._url = payload.url;
|
this._url = payload.url;
|
||||||
this._method = payload.method;
|
this._method = payload.method;
|
||||||
this._headers = payload.headers;
|
this._headers = payload.headers;
|
||||||
this._postData = payload.postData;
|
this._postData = payload.postData;
|
||||||
|
|
||||||
this._urlOverride = undefined;
|
this._urlOverride = undefined;
|
||||||
this._methodOverride = undefined;
|
this._methodOverride = undefined;
|
||||||
this._postDataOverride = undefined;
|
this._postDataOverride = undefined;
|
||||||
|
|
||||||
this._handled = false;
|
this._handled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
url() {
|
url() {
|
||||||
return this._urlOverride || this._url;
|
return this._urlOverride || this._url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
*/
|
*/
|
||||||
setUrl(url) {
|
setUrl(url) {
|
||||||
this._urlOverride = url;
|
this._urlOverride = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
method() {
|
method() {
|
||||||
return this._methodOverride || this._method;
|
return this._methodOverride || this._method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} method
|
* @param {string} method
|
||||||
*/
|
*/
|
||||||
setMethod(method) {
|
setMethod(method) {
|
||||||
this._methodOverride = method;
|
this._methodOverride = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Object}
|
* @return {!Object}
|
||||||
*/
|
*/
|
||||||
headers() {
|
headers() {
|
||||||
return Object.assign({}, this._headersOverride || this._headers);
|
return Object.assign({}, this._headersOverride || this._headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
setHeader(key, value) {
|
setHeader(key, value) {
|
||||||
if (!this._headersOverride)
|
if (!this._headersOverride)
|
||||||
this._headersOverride = Object.assign({}, this._headers);
|
this._headersOverride = Object.assign({}, this._headers);
|
||||||
this._headersOverride[key] = value;
|
this._headersOverride[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {(string|undefined)}
|
* @return {(string|undefined)}
|
||||||
*/
|
*/
|
||||||
postData() {
|
postData() {
|
||||||
return this._postDataOverride || this._postData;
|
return this._postDataOverride || this._postData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {(string|undefined)}
|
* @return {(string|undefined)}
|
||||||
*/
|
*/
|
||||||
setPostData(data) {
|
setPostData(data) {
|
||||||
this._postDataOverride = data;
|
this._postDataOverride = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
abort() {
|
abort() {
|
||||||
console.assert(!this._handled, 'This request is already handled!');
|
console.assert(!this._handled, 'This request is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
this._client.send('Network.continueInterceptedRequest', {
|
this._client.send('Network.continueInterceptedRequest', {
|
||||||
interceptionId: this._interceptionId,
|
interceptionId: this._interceptionId,
|
||||||
errorReason: 'Aborted'
|
errorReason: 'Aborted'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
continue() {
|
continue() {
|
||||||
console.assert(!this._handled, 'This request is already handled!');
|
console.assert(!this._handled, 'This request is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
this._client.send('Network.continueInterceptedRequest', {
|
this._client.send('Network.continueInterceptedRequest', {
|
||||||
interceptionId: this._interceptionId,
|
interceptionId: this._interceptionId,
|
||||||
url: this._urlOverride,
|
url: this._urlOverride,
|
||||||
method: this._methodOverride,
|
method: this._methodOverride,
|
||||||
postData: this._postDataOverride,
|
postData: this._postDataOverride,
|
||||||
headers: this._headersOverride
|
headers: this._headersOverride
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
handled() {
|
handled() {
|
||||||
return this._handled;
|
return this._handled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Request;
|
module.exports = Request;
|
||||||
|
@ -21,355 +21,355 @@ var removeRecursive = require('rimraf').sync;
|
|||||||
var copyRecursive = deasync(require('ncp').ncp);
|
var copyRecursive = deasync(require('ncp').ncp);
|
||||||
|
|
||||||
class FileSystem {
|
class FileSystem {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.separator = path.sep;
|
this.separator = path.sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get workingDirectory() {
|
get workingDirectory() {
|
||||||
return process.cwd();
|
return process.cwd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} directoryPath
|
* @param {string} directoryPath
|
||||||
*/
|
*/
|
||||||
changeWorkingDirectory(directoryPath) {
|
changeWorkingDirectory(directoryPath) {
|
||||||
try {
|
try {
|
||||||
process.chdir(directoryPath);
|
process.chdir(directoryPath);
|
||||||
return true;
|
return true;
|
||||||
} catch (e){
|
} catch (e){
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} relativePath
|
* @param {string} relativePath
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
absolute(relativePath) {
|
absolute(relativePath) {
|
||||||
relativePath = path.normalize(relativePath);
|
relativePath = path.normalize(relativePath);
|
||||||
if (path.isAbsolute(relativePath))
|
if (path.isAbsolute(relativePath))
|
||||||
return relativePath;
|
return relativePath;
|
||||||
return path.resolve(path.join(process.cwd(), relativePath));
|
return path.resolve(path.join(process.cwd(), relativePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
exists(filePath) {
|
exists(filePath) {
|
||||||
return fs.existsSync(filePath);
|
return fs.existsSync(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} fromPath
|
* @param {string} fromPath
|
||||||
* @param {string} toPath
|
* @param {string} toPath
|
||||||
*/
|
*/
|
||||||
copy(fromPath, toPath) {
|
copy(fromPath, toPath) {
|
||||||
var content = fs.readFileSync(fromPath);
|
var content = fs.readFileSync(fromPath);
|
||||||
fs.writeFileSync(toPath, content);
|
fs.writeFileSync(toPath, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} fromPath
|
* @param {string} fromPath
|
||||||
* @param {string} toPath
|
* @param {string} toPath
|
||||||
*/
|
*/
|
||||||
move(fromPath, toPath) {
|
move(fromPath, toPath) {
|
||||||
var content = fs.readFileSync(fromPath);
|
var content = fs.readFileSync(fromPath);
|
||||||
fs.writeFileSync(toPath, content);
|
fs.writeFileSync(toPath, content);
|
||||||
fs.unlinkSync(fromPath);
|
fs.unlinkSync(fromPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
size(filePath) {
|
size(filePath) {
|
||||||
return fs.statSync(filePath).size;
|
return fs.statSync(filePath).size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
*/
|
*/
|
||||||
touch(filePath) {
|
touch(filePath) {
|
||||||
fs.closeSync(fs.openSync(filePath, 'a'));
|
fs.closeSync(fs.openSync(filePath, 'a'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
*/
|
*/
|
||||||
remove(filePath) {
|
remove(filePath) {
|
||||||
fs.unlinkSync(filePath);
|
fs.unlinkSync(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
lastModified(filePath) {
|
lastModified(filePath) {
|
||||||
return fs.statSync(filePath).mtime;
|
return fs.statSync(filePath).mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
makeDirectory(dirPath) {
|
makeDirectory(dirPath) {
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(dirPath);
|
fs.mkdirSync(dirPath);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
makeTree(dirPath) {
|
makeTree(dirPath) {
|
||||||
return this.makeDirectory(dirPath);
|
return this.makeDirectory(dirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
*/
|
*/
|
||||||
removeTree(dirPath) {
|
removeTree(dirPath) {
|
||||||
removeRecursive(dirPath);
|
removeRecursive(dirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} fromPath
|
* @param {string} fromPath
|
||||||
* @param {string} toPath
|
* @param {string} toPath
|
||||||
*/
|
*/
|
||||||
copyTree(fromPath, toPath) {
|
copyTree(fromPath, toPath) {
|
||||||
copyRecursive(fromPath, toPath);
|
copyRecursive(fromPath, toPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
* @return {!Array<string>}
|
* @return {!Array<string>}
|
||||||
*/
|
*/
|
||||||
list(dirPath) {
|
list(dirPath) {
|
||||||
return fs.readdirSync(dirPath);
|
return fs.readdirSync(dirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} linkPath
|
* @param {string} linkPath
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
readLink(linkPath) {
|
readLink(linkPath) {
|
||||||
return fs.readlinkSync(linkPath);
|
return fs.readlinkSync(linkPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
*/
|
*/
|
||||||
write(filePath, data, mode) {
|
write(filePath, data, mode) {
|
||||||
var fd = new FileDescriptor(filePath, mode, 'utf8');
|
var fd = new FileDescriptor(filePath, mode, 'utf8');
|
||||||
fd.write(data);
|
fd.write(data);
|
||||||
fd.close();
|
fd.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} somePath
|
* @param {string} somePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isAbsolute(somePath) {
|
isAbsolute(somePath) {
|
||||||
return path.isAbsolute(somePath);
|
return path.isAbsolute(somePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
read(filePath) {
|
read(filePath) {
|
||||||
return fs.readFileSync(filePath, 'utf8');
|
return fs.readFileSync(filePath, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isFile(filePath) {
|
isFile(filePath) {
|
||||||
return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile();
|
return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isDirectory(dirPath) {
|
isDirectory(dirPath) {
|
||||||
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();
|
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isLink(filePath) {
|
isLink(filePath) {
|
||||||
return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink();
|
return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isReadable(filePath) {
|
isReadable(filePath) {
|
||||||
try {
|
try {
|
||||||
fs.accessSync(filePath, fs.constants.R_OK);
|
fs.accessSync(filePath, fs.constants.R_OK);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isWritable(filePath) {
|
isWritable(filePath) {
|
||||||
try {
|
try {
|
||||||
fs.accessSync(filePath, fs.constants.W_OK);
|
fs.accessSync(filePath, fs.constants.W_OK);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isExecutable(filePath) {
|
isExecutable(filePath) {
|
||||||
try {
|
try {
|
||||||
fs.accessSync(filePath, fs.constants.X_OK);
|
fs.accessSync(filePath, fs.constants.X_OK);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} somePath
|
* @param {string} somePath
|
||||||
* @return {!Array<string>}
|
* @return {!Array<string>}
|
||||||
*/
|
*/
|
||||||
split(somePath) {
|
split(somePath) {
|
||||||
somePath = path.normalize(somePath);
|
somePath = path.normalize(somePath);
|
||||||
if (somePath.endsWith(path.sep))
|
if (somePath.endsWith(path.sep))
|
||||||
somePath = somePath.substring(0, somePath.length - path.sep.length);
|
somePath = somePath.substring(0, somePath.length - path.sep.length);
|
||||||
return somePath.split(path.sep);
|
return somePath.split(path.sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} path1
|
* @param {string} path1
|
||||||
* @param {string} path2
|
* @param {string} path2
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
join(...args) {
|
join(...args) {
|
||||||
if (args[0] === '' && args.length > 1)
|
if (args[0] === '' && args.length > 1)
|
||||||
args[0] = path.sep;
|
args[0] = path.sep;
|
||||||
args = args.filter(part => typeof part === 'string');
|
args = args.filter(part => typeof part === 'string');
|
||||||
return path.join.apply(path, args);
|
return path.join.apply(path, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @param {(string|!Object)} option
|
* @param {(string|!Object)} option
|
||||||
* @return {!FileDescriptor}
|
* @return {!FileDescriptor}
|
||||||
*/
|
*/
|
||||||
open(filePath, option) {
|
open(filePath, option) {
|
||||||
if (typeof option === 'string')
|
if (typeof option === 'string')
|
||||||
return new FileDescriptor(filePath, option);
|
return new FileDescriptor(filePath, option);
|
||||||
return new FileDescriptor(filePath, option.mode);
|
return new FileDescriptor(filePath, option.mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fdwrite = deasync(fs.write);
|
var fdwrite = deasync(fs.write);
|
||||||
var fdread = deasync(fs.read);
|
var fdread = deasync(fs.read);
|
||||||
|
|
||||||
class FileDescriptor {
|
class FileDescriptor {
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
*/
|
*/
|
||||||
constructor(filePath, mode) {
|
constructor(filePath, mode) {
|
||||||
this._position = 0;
|
this._position = 0;
|
||||||
this._encoding = 'utf8';
|
this._encoding = 'utf8';
|
||||||
if (mode === 'rb') {
|
if (mode === 'rb') {
|
||||||
this._mode = 'r';
|
this._mode = 'r';
|
||||||
this._encoding = 'latin1';
|
this._encoding = 'latin1';
|
||||||
} else if (mode === 'wb' || mode === 'b') {
|
} else if (mode === 'wb' || mode === 'b') {
|
||||||
this._mode = 'w';
|
this._mode = 'w';
|
||||||
this._encoding = 'latin1';
|
this._encoding = 'latin1';
|
||||||
} else if (mode === 'rw+') {
|
} else if (mode === 'rw+') {
|
||||||
this._mode = 'a+';
|
this._mode = 'a+';
|
||||||
this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0;
|
this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0;
|
||||||
} else {
|
} else {
|
||||||
this._mode = mode;
|
this._mode = mode;
|
||||||
}
|
|
||||||
this._fd = fs.openSync(filePath, this._mode);
|
|
||||||
}
|
}
|
||||||
|
this._fd = fs.openSync(filePath, this._mode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} data
|
* @param {string} data
|
||||||
*/
|
*/
|
||||||
write(data) {
|
write(data) {
|
||||||
var buffer = Buffer.from(data, this._encoding);
|
var buffer = Buffer.from(data, this._encoding);
|
||||||
var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position);
|
var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position);
|
||||||
this._position += written;
|
this._position += written;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEncoding() {
|
getEncoding() {
|
||||||
return 'UTF-8';
|
return 'UTF-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} data
|
* @param {string} data
|
||||||
*/
|
*/
|
||||||
writeLine(data) {
|
writeLine(data) {
|
||||||
this.write(data + '\n');
|
this.write(data + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number=} size
|
* @param {number=} size
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
read(size) {
|
read(size) {
|
||||||
var position = this._position;
|
var position = this._position;
|
||||||
if (!size) {
|
if (!size) {
|
||||||
size = fs.fstatSync(this._fd).size;
|
size = fs.fstatSync(this._fd).size;
|
||||||
position = 0;
|
position = 0;
|
||||||
}
|
|
||||||
var buffer = new Buffer(size);
|
|
||||||
var bytesRead = fdread(this._fd, buffer, 0, size, position);
|
|
||||||
this._position += bytesRead;
|
|
||||||
return buffer.toString(this._encoding);
|
|
||||||
}
|
}
|
||||||
|
var buffer = new Buffer(size);
|
||||||
|
var bytesRead = fdread(this._fd, buffer, 0, size, position);
|
||||||
|
this._position += bytesRead;
|
||||||
|
return buffer.toString(this._encoding);
|
||||||
|
}
|
||||||
|
|
||||||
flush() {
|
flush() {
|
||||||
// noop.
|
// noop.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} position
|
* @param {number} position
|
||||||
*/
|
*/
|
||||||
seek(position) {
|
seek(position) {
|
||||||
this._position = position;
|
this._position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
fs.closeSync(this._fd);
|
fs.closeSync(this._fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
atEnd() {
|
atEnd() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FileSystem;
|
module.exports = FileSystem;
|
||||||
|
@ -24,116 +24,116 @@ var url = require('url');
|
|||||||
* @param {string} scriptPath
|
* @param {string} scriptPath
|
||||||
*/
|
*/
|
||||||
module.exports.create = function(context, scriptPath) {
|
module.exports.create = function(context, scriptPath) {
|
||||||
var phantom = {
|
var phantom = {
|
||||||
page: {
|
page: {
|
||||||
onConsoleMessage: null,
|
onConsoleMessage: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} relative
|
* @param {string} relative
|
||||||
* @param {string} base
|
* @param {string} base
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
resolveRelativeUrl: function(relative, base) {
|
resolveRelativeUrl: function(relative, base) {
|
||||||
return url.resolve(base, relative);
|
return url.resolve(base, relative);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
fullyDecodeUrl: function(url) {
|
fullyDecodeUrl: function(url) {
|
||||||
return decodeURI(url);
|
return decodeURI(url);
|
||||||
},
|
},
|
||||||
|
|
||||||
libraryPath: path.dirname(scriptPath),
|
libraryPath: path.dirname(scriptPath),
|
||||||
|
|
||||||
onError: null,
|
onError: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get outputEncoding() {
|
get outputEncoding() {
|
||||||
return 'UTF-8';
|
return 'UTF-8';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
set outputEncoding(value) {
|
set outputEncoding(value) {
|
||||||
throw new Error('Phantom.outputEncoding setter is not implemented');
|
throw new Error('Phantom.outputEncoding setter is not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
get cookiesEnabled() {
|
get cookiesEnabled() {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} value
|
* @param {boolean} value
|
||||||
*/
|
*/
|
||||||
set cookiesEnabled(value) {
|
set cookiesEnabled(value) {
|
||||||
throw new Error('Phantom.cookiesEnabled setter is not implemented');
|
throw new Error('Phantom.cookiesEnabled setter is not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!{major: number, minor: number, patch: number}}
|
* @return {!{major: number, minor: number, patch: number}}
|
||||||
*/
|
*/
|
||||||
get version() {
|
get version() {
|
||||||
var versionParts = require('../package.json').version.split('.');
|
var versionParts = require('../package.json').version.split('.');
|
||||||
return {
|
return {
|
||||||
major: parseInt(versionParts[0], 10),
|
major: parseInt(versionParts[0], 10),
|
||||||
minor: parseInt(versionParts[1], 10),
|
minor: parseInt(versionParts[1], 10),
|
||||||
patch: parseInt(versionParts[2], 10),
|
patch: parseInt(versionParts[2], 10),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number=} code
|
* @param {number=} code
|
||||||
*/
|
*/
|
||||||
exit: function(code) {
|
exit: function(code) {
|
||||||
process.exit(code);
|
process.exit(code);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
injectJs: function(filePath) {
|
injectJs: function(filePath) {
|
||||||
filePath = path.resolve(phantom.libraryPath, filePath);
|
filePath = path.resolve(phantom.libraryPath, filePath);
|
||||||
if (!fs.existsSync(filePath))
|
if (!fs.existsSync(filePath))
|
||||||
return false;
|
return false;
|
||||||
var code = fs.readFileSync(filePath, 'utf8');
|
var code = fs.readFileSync(filePath, 'utf8');
|
||||||
if (code.startsWith('#!'))
|
if (code.startsWith('#!'))
|
||||||
code = code.substring(code.indexOf('\n'));
|
code = code.substring(code.indexOf('\n'));
|
||||||
vm.runInContext(code, context, {
|
vm.runInContext(code, context, {
|
||||||
filename: filePath,
|
filename: filePath,
|
||||||
displayErrors: true
|
displayErrors: true
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} moduleSource
|
* @param {string} moduleSource
|
||||||
* @param {string} filename
|
* @param {string} filename
|
||||||
*/
|
*/
|
||||||
loadModule: function(moduleSource, filename) {
|
loadModule: function(moduleSource, filename) {
|
||||||
var code = [
|
var code = [
|
||||||
'(function(require, exports, module) {\n',
|
'(function(require, exports, module) {\n',
|
||||||
moduleSource,
|
moduleSource,
|
||||||
'\n}.call({},',
|
'\n}.call({},',
|
||||||
'require.cache[\'' + filename + '\']._getRequire(),',
|
'require.cache[\'' + filename + '\']._getRequire(),',
|
||||||
'require.cache[\'' + filename + '\'].exports,',
|
'require.cache[\'' + filename + '\'].exports,',
|
||||||
'require.cache[\'' + filename + '\']',
|
'require.cache[\'' + filename + '\']',
|
||||||
'));'
|
'));'
|
||||||
].join('');
|
].join('');
|
||||||
vm.runInContext(code, context, {
|
vm.runInContext(code, context, {
|
||||||
filename: filename,
|
filename: filename,
|
||||||
displayErrors: true
|
displayErrors: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return phantom;
|
return phantom;
|
||||||
};
|
};
|
||||||
|
@ -19,100 +19,100 @@ var await = require('./utilities').await;
|
|||||||
var os = require('os');
|
var os = require('os');
|
||||||
|
|
||||||
class System {
|
class System {
|
||||||
/**
|
/**
|
||||||
* @param {!Array<string>} args
|
* @param {!Array<string>} args
|
||||||
*/
|
*/
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.env = {};
|
this.env = {};
|
||||||
Object.assign(this.env, process.env);
|
Object.assign(this.env, process.env);
|
||||||
this.stdin = new StandardInput(process.stdin);
|
this.stdin = new StandardInput(process.stdin);
|
||||||
this.stdout = new StandardOutput(process.stdout);
|
this.stdout = new StandardOutput(process.stdout);
|
||||||
this.stderr = new StandardOutput(process.stderr);
|
this.stderr = new StandardOutput(process.stderr);
|
||||||
this.platform = 'phantomjs';
|
this.platform = 'phantomjs';
|
||||||
this.pid = process.pid;
|
this.pid = process.pid;
|
||||||
this.isSSLSupported = false;
|
this.isSSLSupported = false;
|
||||||
this.os = {
|
this.os = {
|
||||||
architecture: os.arch(),
|
architecture: os.arch(),
|
||||||
name: os.type(),
|
name: os.type(),
|
||||||
version: os.release()
|
version: os.release()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardInput {
|
class StandardInput {
|
||||||
/**
|
/**
|
||||||
* @param {!Readable} readableStream
|
* @param {!Readable} readableStream
|
||||||
*/
|
*/
|
||||||
constructor(readableStream) {
|
constructor(readableStream) {
|
||||||
this._readline = readline.createInterface({
|
this._readline = readline.createInterface({
|
||||||
input: readableStream
|
input: readableStream
|
||||||
});
|
});
|
||||||
this._lines = [];
|
this._lines = [];
|
||||||
this._closed = false;
|
this._closed = false;
|
||||||
this._readline.on('line', line => this._lines.push(line));
|
this._readline.on('line', line => this._lines.push(line));
|
||||||
this._readline.on('close', () => this._closed = true);
|
this._readline.on('close', () => this._closed = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
readLine() {
|
readLine() {
|
||||||
if (this._closed && !this._lines.length)
|
if (this._closed && !this._lines.length)
|
||||||
return '';
|
return '';
|
||||||
if (!this._lines.length) {
|
if (!this._lines.length) {
|
||||||
var linePromise = new Promise(fulfill => this._readline.once('line', fulfill));
|
var linePromise = new Promise(fulfill => this._readline.once('line', fulfill));
|
||||||
await(linePromise);
|
await(linePromise);
|
||||||
}
|
|
||||||
return this._lines.shift();
|
|
||||||
}
|
}
|
||||||
|
return this._lines.shift();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
read() {
|
read() {
|
||||||
if (!this._closed) {
|
if (!this._closed) {
|
||||||
var closePromise = new Promise(fulfill => this._readline.once('close', fulfill));
|
var closePromise = new Promise(fulfill => this._readline.once('close', fulfill));
|
||||||
await(closePromise);
|
await(closePromise);
|
||||||
}
|
|
||||||
var text = this._lines.join('\n');
|
|
||||||
this._lines = [];
|
|
||||||
return text;
|
|
||||||
}
|
}
|
||||||
|
var text = this._lines.join('\n');
|
||||||
|
this._lines = [];
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this._readline.close();
|
this._readline.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardOutput {
|
class StandardOutput {
|
||||||
/**
|
/**
|
||||||
* @param {!Writable} writableStream
|
* @param {!Writable} writableStream
|
||||||
*/
|
*/
|
||||||
constructor(writableStream) {
|
constructor(writableStream) {
|
||||||
this._stream = writableStream;
|
this._stream = writableStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} data
|
* @param {string} data
|
||||||
*/
|
*/
|
||||||
write(data) {
|
write(data) {
|
||||||
this._stream.write(data);
|
this._stream.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} data
|
* @param {string} data
|
||||||
*/
|
*/
|
||||||
writeLine(data) {
|
writeLine(data) {
|
||||||
this._stream.write(data + '\n');
|
this._stream.write(data + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
flush() {
|
flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this._stream.end();
|
this._stream.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = System;
|
module.exports = System;
|
||||||
|
@ -23,319 +23,319 @@ var PageEvents = require('../lib/Page').Events;
|
|||||||
var noop = function() { };
|
var noop = function() { };
|
||||||
|
|
||||||
class WebPage {
|
class WebPage {
|
||||||
/**
|
/**
|
||||||
* @param {!Browser} browser
|
* @param {!Browser} browser
|
||||||
* @param {string} scriptPath
|
* @param {string} scriptPath
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
*/
|
*/
|
||||||
constructor(browser, scriptPath, options) {
|
constructor(browser, scriptPath, options) {
|
||||||
this._page = await(browser.newPage());
|
this._page = await(browser.newPage());
|
||||||
this.settings = new WebPageSettings(this._page);
|
this.settings = new WebPageSettings(this._page);
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.settings = options.settings || {};
|
options.settings = options.settings || {};
|
||||||
if (options.settings.userAgent)
|
if (options.settings.userAgent)
|
||||||
this.settings.userAgent = options.settings.userAgent;
|
this.settings.userAgent = options.settings.userAgent;
|
||||||
if (options.viewportSize)
|
if (options.viewportSize)
|
||||||
await(this._page.setViewportSize(options.viewportSize));
|
await(this._page.setViewportSize(options.viewportSize));
|
||||||
|
|
||||||
this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0};
|
this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0};
|
||||||
this.onConsoleMessage = null;
|
this.onConsoleMessage = null;
|
||||||
this.onLoadFinished = null;
|
this.onLoadFinished = null;
|
||||||
this.onResourceError = null;
|
this.onResourceError = null;
|
||||||
this.onResourceReceived = null;
|
this.onResourceReceived = null;
|
||||||
this.onInitialized = null;
|
this.onInitialized = null;
|
||||||
this._deferEvaluate = false;
|
this._deferEvaluate = false;
|
||||||
|
|
||||||
this.libraryPath = path.dirname(scriptPath);
|
this.libraryPath = path.dirname(scriptPath);
|
||||||
|
|
||||||
this._onResourceRequestedCallback = undefined;
|
this._onResourceRequestedCallback = undefined;
|
||||||
this._onConfirmCallback = undefined;
|
this._onConfirmCallback = undefined;
|
||||||
this._onAlertCallback = undefined;
|
this._onAlertCallback = undefined;
|
||||||
this._onError = noop;
|
this._onError = noop;
|
||||||
|
|
||||||
this._pageEvents = new AsyncEmitter(this._page);
|
this._pageEvents = new AsyncEmitter(this._page);
|
||||||
this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response));
|
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.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event));
|
||||||
this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg));
|
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.Confirm, message => this._onConfirm(message));
|
||||||
this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message));
|
this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message));
|
||||||
this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog));
|
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.on(PageEvents.Error, error => (this._onError || noop).call(null, error.message, error.stack));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {?function(!Object, !Request)}
|
* @return {?function(!Object, !Request)}
|
||||||
*/
|
*/
|
||||||
get onResourceRequested() {
|
get onResourceRequested() {
|
||||||
return this._onResourceRequestedCallback;
|
return this._onResourceRequestedCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {?function(!Object, !Request)} callback
|
* @return {?function(!Object, !Request)} callback
|
||||||
*/
|
*/
|
||||||
set onResourceRequested(callback) {
|
set onResourceRequested(callback) {
|
||||||
this._onResourceRequestedCallback = callback;
|
this._onResourceRequestedCallback = callback;
|
||||||
this._page.setRequestInterceptor(callback ? resourceInterceptor : null);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {function()} callback
|
* @param {function()} callback
|
||||||
*/
|
*/
|
||||||
includeJs(url, callback) {
|
includeJs(url, callback) {
|
||||||
this._page.addScriptTag(url).then(callback);
|
this._page.addScriptTag(url).then(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!{width: number, height: number}}
|
* @return {!{width: number, height: number}}
|
||||||
*/
|
*/
|
||||||
get viewportSize() {
|
get viewportSize() {
|
||||||
return this._page.viewportSize();
|
return this._page.viewportSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Object}
|
* @return {!Object}
|
||||||
*/
|
*/
|
||||||
get customHeaders() {
|
get customHeaders() {
|
||||||
return this._page.extraHTTPHeaders();
|
return this._page.extraHTTPHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} value
|
* @param {!Object} value
|
||||||
*/
|
*/
|
||||||
set customHeaders(value) {
|
set customHeaders(value) {
|
||||||
await(this._page.setExtraHTTPHeaders(value));
|
await(this._page.setExtraHTTPHeaders(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
*/
|
*/
|
||||||
injectJs(filePath) {
|
injectJs(filePath) {
|
||||||
if (!fs.existsSync(filePath))
|
if (!fs.existsSync(filePath))
|
||||||
filePath = path.resolve(this.libraryPath, filePath);
|
filePath = path.resolve(this.libraryPath, filePath);
|
||||||
if (!fs.existsSync(filePath))
|
if (!fs.existsSync(filePath))
|
||||||
return false;
|
return false;
|
||||||
await(this._page.injectFile(filePath));
|
await(this._page.injectFile(filePath));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get plainText() {
|
get plainText() {
|
||||||
return await(this._page.plainText());
|
return await(this._page.plainText());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get title() {
|
get title() {
|
||||||
return await(this._page.title());
|
return await(this._page.title());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {(function()|undefined)}
|
* @return {(function()|undefined)}
|
||||||
*/
|
*/
|
||||||
get onError() {
|
get onError() {
|
||||||
return this._onError;
|
return this._onError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {(function()|undefined)} handler
|
* @param {(function()|undefined)} handler
|
||||||
*/
|
*/
|
||||||
set onError(handler) {
|
set onError(handler) {
|
||||||
if (typeof handler !== 'function')
|
if (typeof handler !== 'function')
|
||||||
handler = undefined;
|
handler = undefined;
|
||||||
this._onError = handler;
|
this._onError = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {(function()|undefined)}
|
* @return {(function()|undefined)}
|
||||||
*/
|
*/
|
||||||
get onConfirm() {
|
get onConfirm() {
|
||||||
return this._onConfirmCallback;
|
return this._onConfirmCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} handler
|
* @param {function()} handler
|
||||||
*/
|
*/
|
||||||
set onConfirm(handler) {
|
set onConfirm(handler) {
|
||||||
if (typeof handler !== 'function')
|
if (typeof handler !== 'function')
|
||||||
handler = undefined;
|
handler = undefined;
|
||||||
this._onConfirmCallback = handler;
|
this._onConfirmCallback = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {(function()|undefined)}
|
* @return {(function()|undefined)}
|
||||||
*/
|
*/
|
||||||
get onAlert() {
|
get onAlert() {
|
||||||
return this._onAlertCallback;
|
return this._onAlertCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} handler
|
* @param {function()} handler
|
||||||
*/
|
*/
|
||||||
set onAlert(handler) {
|
set onAlert(handler) {
|
||||||
if (typeof handler !== 'function')
|
if (typeof handler !== 'function')
|
||||||
handler = undefined;
|
handler = undefined;
|
||||||
this._onAlertCallback = handler;
|
this._onAlertCallback = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Dialog} dialog
|
* @param {!Dialog} dialog
|
||||||
*/
|
*/
|
||||||
_onDialog(dialog) {
|
_onDialog(dialog) {
|
||||||
if (dialog.type === 'alert' && this._onAlertCallback) {
|
if (dialog.type === 'alert' && this._onAlertCallback) {
|
||||||
this._onAlertCallback.call(null, dialog.message());
|
this._onAlertCallback.call(null, dialog.message());
|
||||||
await(dialog.accept());
|
await(dialog.accept());
|
||||||
} else if (dialog.type === 'confirm' && this._onConfirmCallback) {
|
} else if (dialog.type === 'confirm' && this._onConfirmCallback) {
|
||||||
var result = this._onConfirmCallback.call(null, dialog.message());
|
var result = this._onConfirmCallback.call(null, dialog.message());
|
||||||
await(result ? dialog.accept() : dialog.dismiss());
|
await(result ? dialog.accept() : dialog.dismiss());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get url() {
|
get url() {
|
||||||
return await(this._page.url());
|
return await(this._page.url());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
*/
|
*/
|
||||||
set content(html) {
|
set content(html) {
|
||||||
await(this._page.setContent(html));
|
await(this._page.setContent(html));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
* @param {function()=} callback
|
* @param {function()=} callback
|
||||||
*/
|
*/
|
||||||
open(url, callback) {
|
open(url, callback) {
|
||||||
console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments');
|
console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments');
|
||||||
this._deferEvaluate = true;
|
this._deferEvaluate = true;
|
||||||
if (typeof this.onInitialized === 'function')
|
if (typeof this.onInitialized === 'function')
|
||||||
this.onInitialized();
|
this.onInitialized();
|
||||||
this._deferEvaluate = false;
|
this._deferEvaluate = false;
|
||||||
this._page.navigate(url).then(result => {
|
this._page.navigate(url).then(result => {
|
||||||
var status = result ? 'success' : 'fail';
|
var status = result ? 'success' : 'fail';
|
||||||
if (!result) {
|
if (!result) {
|
||||||
this.onResourceError.call(null, {
|
this.onResourceError.call(null, {
|
||||||
url,
|
url,
|
||||||
errorString: 'SSL handshake failed'
|
errorString: 'SSL handshake failed'
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.onLoadFinished)
|
|
||||||
this.onLoadFinished.call(null, status);
|
|
||||||
if (callback)
|
|
||||||
callback.call(null, status);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (this.onLoadFinished)
|
||||||
|
this.onLoadFinished.call(null, status);
|
||||||
|
if (callback)
|
||||||
|
callback.call(null, status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!{width: number, height: number}} options
|
* @param {!{width: number, height: number}} options
|
||||||
*/
|
*/
|
||||||
set viewportSize(options) {
|
set viewportSize(options) {
|
||||||
await(this._page.setViewportSize(options));
|
await(this._page.setViewportSize(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} fun
|
* @param {function()} fun
|
||||||
* @param {!Array<!Object>} args
|
* @param {!Array<!Object>} args
|
||||||
*/
|
*/
|
||||||
evaluate(fun, ...args) {
|
evaluate(fun, ...args) {
|
||||||
if (this._deferEvaluate)
|
if (this._deferEvaluate)
|
||||||
return await(this._page.evaluateOnInitialized(fun, ...args));
|
return await(this._page.evaluateOnInitialized(fun, ...args));
|
||||||
return await(this._page.evaluate(fun, ...args));
|
return await(this._page.evaluate(fun, ...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {string} fileName
|
* {string} fileName
|
||||||
*/
|
*/
|
||||||
render(fileName) {
|
render(fileName) {
|
||||||
if (fileName.endsWith('pdf')) {
|
if (fileName.endsWith('pdf')) {
|
||||||
var options = {};
|
var options = {};
|
||||||
var paperSize = this.paperSize || {};
|
var paperSize = this.paperSize || {};
|
||||||
options.margin = paperSize.margin;
|
options.margin = paperSize.margin;
|
||||||
options.format = paperSize.format;
|
options.format = paperSize.format;
|
||||||
options.landscape = paperSize.orientation === 'landscape';
|
options.landscape = paperSize.orientation === 'landscape';
|
||||||
options.width = paperSize.width;
|
options.width = paperSize.width;
|
||||||
options.height = paperSize.height;
|
options.height = paperSize.height;
|
||||||
await(this._page.printToPDF(fileName, options));
|
await(this._page.printToPDF(fileName, options));
|
||||||
} else {
|
} else {
|
||||||
var options = {};
|
var options = {};
|
||||||
if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) {
|
if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) {
|
||||||
options.clip = {
|
options.clip = {
|
||||||
x: this.clipRect.left,
|
x: this.clipRect.left,
|
||||||
y: this.clipRect.top,
|
y: this.clipRect.top,
|
||||||
width: this.clipRect.width,
|
width: this.clipRect.width,
|
||||||
height: this.clipRect.height
|
height: this.clipRect.height
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
options.path = fileName;
|
options.path = fileName;
|
||||||
await(this._page.screenshot(options));
|
await(this._page.screenshot(options));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
release() {
|
release() {
|
||||||
this._page.close();
|
this._page.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this._page.close();
|
this._page.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebPageSettings {
|
class WebPageSettings {
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
*/
|
*/
|
||||||
constructor(page) {
|
constructor(page) {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
set userAgent(value) {
|
set userAgent(value) {
|
||||||
await(this._page.setUserAgentOverride(value));
|
await(this._page.setUserAgentOverride(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get userAgent() {
|
get userAgent() {
|
||||||
return this._page.userAgentOverride();
|
return this._page.userAgentOverride();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To prevent reenterability, eventemitters should emit events
|
// 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,
|
// This class is a wrapper around EventEmitter which re-emits events asynchronously,
|
||||||
// helping to overcome the issue.
|
// helping to overcome the issue.
|
||||||
class AsyncEmitter extends EventEmitter {
|
class AsyncEmitter extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
*/
|
*/
|
||||||
constructor(page) {
|
constructor(page) {
|
||||||
super();
|
super();
|
||||||
this._page = page;
|
this._page = page;
|
||||||
this._symbol = Symbol('AsyncEmitter');
|
this._symbol = Symbol('AsyncEmitter');
|
||||||
this.on('newListener', this._onListenerAdded);
|
this.on('newListener', this._onListenerAdded);
|
||||||
this.on('removeListener', this._onListenerRemoved);
|
this.on('removeListener', this._onListenerRemoved);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onListenerAdded(event, listener) {
|
_onListenerAdded(event, listener) {
|
||||||
// Async listener calls original listener on next tick.
|
// Async listener calls original listener on next tick.
|
||||||
var asyncListener = (...args) => {
|
var asyncListener = (...args) => {
|
||||||
process.nextTick(() => listener.apply(null, args));
|
process.nextTick(() => listener.apply(null, args));
|
||||||
};
|
};
|
||||||
listener[this._symbol] = asyncListener;
|
listener[this._symbol] = asyncListener;
|
||||||
this._page.on(event, asyncListener);
|
this._page.on(event, asyncListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onListenerRemoved(event, listener) {
|
_onListenerRemoved(event, listener) {
|
||||||
this._page.removeListener(event, listener[this._symbol]);
|
this._page.removeListener(event, listener[this._symbol]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WebPage;
|
module.exports = WebPage;
|
||||||
|
@ -18,66 +18,66 @@ var http = require('http');
|
|||||||
var await = require('./utilities').await;
|
var await = require('./utilities').await;
|
||||||
|
|
||||||
class WebServer {
|
class WebServer {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._server = http.createServer();
|
this._server = http.createServer();
|
||||||
this.objectName = 'WebServer';
|
this.objectName = 'WebServer';
|
||||||
this.listenOnPort = this.listen;
|
this.listenOnPort = this.listen;
|
||||||
this.newRequest = function(req, res) { };
|
this.newRequest = function(req, res) { };
|
||||||
Object.defineProperty(this, 'port', {
|
Object.defineProperty(this, 'port', {
|
||||||
get: () => {
|
get: () => {
|
||||||
if (!this._server.listening)
|
if (!this._server.listening)
|
||||||
return '';
|
return '';
|
||||||
return this._server.address().port + '';
|
return this._server.address().port + '';
|
||||||
},
|
},
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: false
|
configurable: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this._server.close();
|
this._server.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {nubmer} port
|
* @param {nubmer} port
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
listen(port, callback) {
|
listen(port, callback) {
|
||||||
if (this._server.listening)
|
if (this._server.listening)
|
||||||
return false;
|
return false;
|
||||||
this.newRequest = callback;
|
this.newRequest = callback;
|
||||||
this._server.listen(port);
|
this._server.listen(port);
|
||||||
var errorPromise = new Promise(x => this._server.once('error', x));
|
var errorPromise = new Promise(x => this._server.once('error', x));
|
||||||
var successPromise = new Promise(x => this._server.once('listening', x));
|
var successPromise = new Promise(x => this._server.once('listening', x));
|
||||||
await(Promise.race([errorPromise, successPromise]));
|
await(Promise.race([errorPromise, successPromise]));
|
||||||
if (!this._server.listening)
|
if (!this._server.listening)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._server.on('request', (req, res) => {
|
this._server.on('request', (req, res) => {
|
||||||
res.close = res.end.bind(res);
|
res.close = res.end.bind(res);
|
||||||
var headers = res.getHeaders();
|
var headers = res.getHeaders();
|
||||||
res.headers = [];
|
res.headers = [];
|
||||||
for (var key in headers) {
|
for (var key in headers) {
|
||||||
res.headers.push({
|
res.headers.push({
|
||||||
name: key,
|
name: key,
|
||||||
value: headers[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);
|
|
||||||
});
|
});
|
||||||
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;
|
module.exports = WebServer;
|
||||||
|
@ -27,39 +27,39 @@ var child_process = require('child_process');
|
|||||||
var Browser = require('..').Browser;
|
var Browser = require('..').Browser;
|
||||||
var version = require('../package.json').version;
|
var version = require('../package.json').version;
|
||||||
var argv = require('minimist')(process.argv.slice(2), {
|
var argv = require('minimist')(process.argv.slice(2), {
|
||||||
alias: { v: 'version' },
|
alias: { v: 'version' },
|
||||||
boolean: ['headless'],
|
boolean: ['headless'],
|
||||||
default: {'headless': true },
|
default: {'headless': true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (argv.version) {
|
if (argv.version) {
|
||||||
console.log('Puppeteer v' + version);
|
console.log('Puppeteer v' + version);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv['ssl-certificates-path']) {
|
if (argv['ssl-certificates-path']) {
|
||||||
console.error('Flag --ssl-certificates-path is not supported.');
|
console.error('Flag --ssl-certificates-path is not supported.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var scriptArguments = argv._;
|
var scriptArguments = argv._;
|
||||||
if (!scriptArguments.length) {
|
if (!scriptArguments.length) {
|
||||||
console.log(__filename.split('/').pop() + ' [scriptfile]');
|
console.log(__filename.split('/').pop() + ' [scriptfile]');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var scriptPath = path.resolve(process.cwd(), scriptArguments[0]);
|
var scriptPath = path.resolve(process.cwd(), scriptArguments[0]);
|
||||||
if (!fs.existsSync(scriptPath)) {
|
if (!fs.existsSync(scriptPath)) {
|
||||||
console.error(`script not found: ${scriptPath}`);
|
console.error(`script not found: ${scriptPath}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var browser = new Browser({
|
var browser = new Browser({
|
||||||
remoteDebuggingPort: 9229,
|
remoteDebuggingPort: 9229,
|
||||||
headless: argv.headless,
|
headless: argv.headless,
|
||||||
args: ['--no-sandbox']
|
args: ['--no-sandbox']
|
||||||
});
|
});
|
||||||
|
|
||||||
var context = createPhantomContext(browser, scriptPath, argv);
|
var context = createPhantomContext(browser, scriptPath, argv);
|
||||||
@ -73,38 +73,38 @@ vm.runInContext(scriptContent, context);
|
|||||||
* @return {!Object}
|
* @return {!Object}
|
||||||
*/
|
*/
|
||||||
function createPhantomContext(browser, scriptPath, argv) {
|
function createPhantomContext(browser, scriptPath, argv) {
|
||||||
var context = {};
|
var context = {};
|
||||||
context.setInterval = setInterval;
|
context.setInterval = setInterval;
|
||||||
context.setTimeout = setTimeout;
|
context.setTimeout = setTimeout;
|
||||||
context.clearInterval = clearInterval;
|
context.clearInterval = clearInterval;
|
||||||
context.clearTimeout = clearTimeout;
|
context.clearTimeout = clearTimeout;
|
||||||
|
|
||||||
context.phantom = Phantom.create(context, scriptPath);
|
context.phantom = Phantom.create(context, scriptPath);
|
||||||
context.console = console;
|
context.console = console;
|
||||||
context.window = context;
|
context.window = context;
|
||||||
context.WebPage = options => new WebPage(browser, scriptPath, options);
|
context.WebPage = options => new WebPage(browser, scriptPath, options);
|
||||||
|
|
||||||
vm.createContext(context);
|
vm.createContext(context);
|
||||||
|
|
||||||
var nativeExports = {
|
var nativeExports = {
|
||||||
fs: new FileSystem(),
|
fs: new FileSystem(),
|
||||||
system: new System(argv._),
|
system: new System(argv._),
|
||||||
webpage: {
|
webpage: {
|
||||||
create: context.WebPage,
|
create: context.WebPage,
|
||||||
},
|
},
|
||||||
webserver: {
|
webserver: {
|
||||||
create: () => new WebServer(),
|
create: () => new WebServer(),
|
||||||
},
|
},
|
||||||
cookiejar: {
|
cookiejar: {
|
||||||
create: () => {},
|
create: () => {},
|
||||||
},
|
},
|
||||||
child_process: child_process
|
child_process: child_process
|
||||||
};
|
};
|
||||||
var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js');
|
var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js');
|
||||||
var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8');
|
var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8');
|
||||||
vm.runInContext(bootstrapCode, context, {
|
vm.runInContext(bootstrapCode, context, {
|
||||||
filename: 'bootstrap.js'
|
filename: 'bootstrap.js'
|
||||||
})(nativeExports);
|
})(nativeExports);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,16 +17,16 @@
|
|||||||
var loopWhile = require('deasync').loopWhile;
|
var loopWhile = require('deasync').loopWhile;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
await: function(promise) {
|
await: function(promise) {
|
||||||
var error;
|
var error;
|
||||||
var result;
|
var result;
|
||||||
var done = false;
|
var done = false;
|
||||||
promise.then(r => result = r)
|
promise.then(r => result = r)
|
||||||
.catch(err => error = err)
|
.catch(err => error = err)
|
||||||
.then(() => done = true);
|
.then(() => done = true);
|
||||||
loopWhile(() => !done);
|
loopWhile(() => !done);
|
||||||
if (error)
|
if (error)
|
||||||
throw error;
|
throw error;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -21,35 +21,35 @@ var path = require('path');
|
|||||||
var mime = require('mime');
|
var mime = require('mime');
|
||||||
|
|
||||||
class StaticServer {
|
class StaticServer {
|
||||||
/**
|
/**
|
||||||
* @param {string} dirPath
|
* @param {string} dirPath
|
||||||
* @param {number} port
|
* @param {number} port
|
||||||
*/
|
*/
|
||||||
constructor(dirPath, port) {
|
constructor(dirPath, port) {
|
||||||
this._server = http.createServer(this._onRequest.bind(this));
|
this._server = http.createServer(this._onRequest.bind(this));
|
||||||
this._server.listen(port);
|
this._server.listen(port);
|
||||||
this._dirPath = dirPath;
|
this._dirPath = dirPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this._server.close();
|
this._server.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequest(request, response) {
|
_onRequest(request, response) {
|
||||||
var pathName = url.parse(request.url).path;
|
var pathName = url.parse(request.url).path;
|
||||||
if (pathName === '/')
|
if (pathName === '/')
|
||||||
pathName = '/index.html';
|
pathName = '/index.html';
|
||||||
pathName = path.join(this._dirPath, pathName.substring(1));
|
pathName = path.join(this._dirPath, pathName.substring(1));
|
||||||
fs.readFile(pathName, function(err, data) {
|
fs.readFile(pathName, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
response.statusCode = 404;
|
response.statusCode = 404;
|
||||||
response.end(`File not found: ${pathName}`);
|
response.end(`File not found: ${pathName}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
response.setHeader('Content-Type', mime.lookup(pathName));
|
response.setHeader('Content-Type', mime.lookup(pathName));
|
||||||
response.end(data);
|
response.end(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = StaticServer;
|
module.exports = StaticServer;
|
||||||
|
@ -1,62 +1,62 @@
|
|||||||
var utils = module.exports = {
|
var utils = module.exports = {
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
attachFrame: async function(page, frameId, url) {
|
attachFrame: async function(page, frameId, url) {
|
||||||
await page.evaluate(attachFrame, frameId, url);
|
await page.evaluate(attachFrame, frameId, url);
|
||||||
|
|
||||||
function attachFrame(frameId, url) {
|
function attachFrame(frameId, url) {
|
||||||
var frame = document.createElement('iframe');
|
var frame = document.createElement('iframe');
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
frame.id = frameId;
|
frame.id = frameId;
|
||||||
document.body.appendChild(frame);
|
document.body.appendChild(frame);
|
||||||
return new Promise(x => frame.onload = x);
|
return new Promise(x => frame.onload = x);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
detachFrame: async function(page, frameId) {
|
detachFrame: async function(page, frameId) {
|
||||||
await page.evaluate(detachFrame, frameId);
|
await page.evaluate(detachFrame, frameId);
|
||||||
|
|
||||||
function detachFrame(frameId) {
|
function detachFrame(frameId) {
|
||||||
var frame = document.getElementById(frameId);
|
var frame = document.getElementById(frameId);
|
||||||
frame.remove();
|
frame.remove();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Page} page
|
* @param {!Page} page
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
navigateFrame: async function(page, frameId, url) {
|
navigateFrame: async function(page, frameId, url) {
|
||||||
await page.evaluate(navigateFrame, frameId, url);
|
await page.evaluate(navigateFrame, frameId, url);
|
||||||
|
|
||||||
function navigateFrame(frameId, url) {
|
function navigateFrame(frameId, url) {
|
||||||
var frame = document.getElementById(frameId);
|
var frame = document.getElementById(frameId);
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
return new Promise(x => frame.onload = x);
|
return new Promise(x => frame.onload = x);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Frame} frame
|
* @param {!Frame} frame
|
||||||
* @param {string=} indentation
|
* @param {string=} indentation
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
dumpFrames: function(frame, indentation) {
|
dumpFrames: function(frame, indentation) {
|
||||||
indentation = indentation || '';
|
indentation = indentation || '';
|
||||||
var result = indentation + frame.url();
|
var result = indentation + frame.url();
|
||||||
for (var child of frame.childFrames())
|
for (var child of frame.childFrames())
|
||||||
result += '\n' + utils.dumpFrames(child, ' ' + indentation);
|
result += '\n' + utils.dumpFrames(child, ' ' + indentation);
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -25,19 +25,19 @@ var GOLDEN_DIR = path.join(__dirname, 'golden');
|
|||||||
var OUTPUT_DIR = path.join(__dirname, 'output');
|
var OUTPUT_DIR = path.join(__dirname, 'output');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
addMatchers: function(jasmine) {
|
addMatchers: function(jasmine) {
|
||||||
jasmine.addMatchers(customMatchers);
|
jasmine.addMatchers(customMatchers);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeOutputDir: function() {
|
removeOutputDir: function() {
|
||||||
if (fs.existsSync(OUTPUT_DIR))
|
if (fs.existsSync(OUTPUT_DIR))
|
||||||
rm(OUTPUT_DIR);
|
rm(OUTPUT_DIR);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var GoldenComparators = {
|
var GoldenComparators = {
|
||||||
'image/png': compareImages,
|
'image/png': compareImages,
|
||||||
'text/plain': compareText
|
'text/plain': compareText
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,19 +46,19 @@ var GoldenComparators = {
|
|||||||
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
|
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
|
||||||
*/
|
*/
|
||||||
function compareImages(actualBuffer, expectedBuffer) {
|
function compareImages(actualBuffer, expectedBuffer) {
|
||||||
if (!actualBuffer || !(actualBuffer instanceof Buffer))
|
if (!actualBuffer || !(actualBuffer instanceof Buffer))
|
||||||
return { errorMessage: 'Actual result should be Buffer.' };
|
return { errorMessage: 'Actual result should be Buffer.' };
|
||||||
|
|
||||||
var actual = PNG.sync.read(actualBuffer);
|
var actual = PNG.sync.read(actualBuffer);
|
||||||
var expected = PNG.sync.read(expectedBuffer);
|
var expected = PNG.sync.read(expectedBuffer);
|
||||||
if (expected.width !== actual.width || expected.height !== actual.height) {
|
if (expected.width !== actual.width || expected.height !== actual.height) {
|
||||||
return {
|
return {
|
||||||
errorMessage: `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px. `
|
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 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});
|
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;
|
return count > 0 ? { diff: PNG.sync.write(diff) } : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,80 +67,80 @@ function compareImages(actualBuffer, expectedBuffer) {
|
|||||||
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
|
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
|
||||||
*/
|
*/
|
||||||
function compareText(actual, expectedBuffer) {
|
function compareText(actual, expectedBuffer) {
|
||||||
if (typeof actual !== 'string')
|
if (typeof actual !== 'string')
|
||||||
return { errorMessage: 'Actual result should be string' };
|
return { errorMessage: 'Actual result should be string' };
|
||||||
var expected = expectedBuffer.toString('utf-8');
|
var expected = expectedBuffer.toString('utf-8');
|
||||||
if (expected === actual)
|
if (expected === actual)
|
||||||
return null;
|
return null;
|
||||||
var diff = new Diff();
|
var diff = new Diff();
|
||||||
var result = diff.main(expected, actual);
|
var result = diff.main(expected, actual);
|
||||||
diff.cleanupSemantic(result);
|
diff.cleanupSemantic(result);
|
||||||
var html = diff.prettyHtml(result);
|
var html = diff.prettyHtml(result);
|
||||||
var diffStylePath = path.join(__dirname, 'diffstyle.css');
|
var diffStylePath = path.join(__dirname, 'diffstyle.css');
|
||||||
html = `<link rel="stylesheet" href="file://${diffStylePath}">` + html;
|
html = `<link rel="stylesheet" href="file://${diffStylePath}">` + html;
|
||||||
return {
|
return {
|
||||||
diff: html,
|
diff: html,
|
||||||
diffExtension: '.html'
|
diffExtension: '.html'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var customMatchers = {
|
var customMatchers = {
|
||||||
toBeGolden: function(util, customEqualityTesters) {
|
toBeGolden: function(util, customEqualityTesters) {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* @param {?Object} actual
|
* @param {?Object} actual
|
||||||
* @param {string} goldenName
|
* @param {string} goldenName
|
||||||
* @return {!{pass: boolean, message: (undefined|string)}}
|
* @return {!{pass: boolean, message: (undefined|string)}}
|
||||||
*/
|
*/
|
||||||
compare: function(actual, goldenName) {
|
compare: function(actual, goldenName) {
|
||||||
var expectedPath = path.join(GOLDEN_DIR, goldenName);
|
var expectedPath = path.join(GOLDEN_DIR, goldenName);
|
||||||
var actualPath = path.join(OUTPUT_DIR, goldenName);
|
var actualPath = path.join(OUTPUT_DIR, goldenName);
|
||||||
|
|
||||||
var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory');
|
var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory');
|
||||||
|
|
||||||
if (!fs.existsSync(expectedPath)) {
|
if (!fs.existsSync(expectedPath)) {
|
||||||
ensureOutputDir();
|
ensureOutputDir();
|
||||||
fs.writeFileSync(actualPath, actual);
|
fs.writeFileSync(actualPath, actual);
|
||||||
return {
|
return {
|
||||||
pass: false,
|
pass: false,
|
||||||
message: goldenName + ' is missing in golden results. ' + messageSuffix
|
message: goldenName + ' is missing in golden results. ' + messageSuffix
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var expected = fs.readFileSync(expectedPath);
|
var expected = fs.readFileSync(expectedPath);
|
||||||
var comparator = GoldenComparators[mime.lookup(goldenName)];
|
var comparator = GoldenComparators[mime.lookup(goldenName)];
|
||||||
if (!comparator) {
|
if (!comparator) {
|
||||||
return {
|
return {
|
||||||
pass: false,
|
pass: false,
|
||||||
message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName
|
message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var result = comparator(actual, expected);
|
var result = comparator(actual, expected);
|
||||||
if (!result)
|
if (!result)
|
||||||
return { pass: true };
|
return { pass: true };
|
||||||
ensureOutputDir();
|
ensureOutputDir();
|
||||||
fs.writeFileSync(actualPath, actual);
|
fs.writeFileSync(actualPath, actual);
|
||||||
// Copy expected to the output/ folder for convenience.
|
// Copy expected to the output/ folder for convenience.
|
||||||
fs.writeFileSync(addSuffix(actualPath, '-expected'), expected);
|
fs.writeFileSync(addSuffix(actualPath, '-expected'), expected);
|
||||||
if (result.diff) {
|
if (result.diff) {
|
||||||
var diffPath = addSuffix(actualPath, '-diff', result.diffExtension);
|
var diffPath = addSuffix(actualPath, '-diff', result.diffExtension);
|
||||||
fs.writeFileSync(diffPath, result.diff);
|
fs.writeFileSync(diffPath, result.diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = goldenName + ' mismatch!';
|
var message = goldenName + ' mismatch!';
|
||||||
if (result.errorMessage)
|
if (result.errorMessage)
|
||||||
message += ' ' + result.errorMessage;
|
message += ' ' + result.errorMessage;
|
||||||
return {
|
return {
|
||||||
pass: false,
|
pass: false,
|
||||||
message: message + ' ' + messageSuffix
|
message: message + ' ' + messageSuffix
|
||||||
};
|
|
||||||
|
|
||||||
function ensureOutputDir() {
|
|
||||||
if (!fs.existsSync(OUTPUT_DIR))
|
|
||||||
fs.mkdirSync(OUTPUT_DIR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
function ensureOutputDir() {
|
||||||
|
if (!fs.existsSync(OUTPUT_DIR))
|
||||||
|
fs.mkdirSync(OUTPUT_DIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,8 +150,8 @@ var customMatchers = {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function addSuffix(filePath, suffix, customExtension) {
|
function addSuffix(filePath, suffix, customExtension) {
|
||||||
var dirname = path.dirname(filePath);
|
var dirname = path.dirname(filePath);
|
||||||
var ext = path.extname(filePath);
|
var ext = path.extname(filePath);
|
||||||
var name = path.basename(filePath, ext);
|
var name = path.basename(filePath, ext);
|
||||||
return path.join(dirname, name + suffix + (customExtension || ext));
|
return path.join(dirname, name + suffix + (customExtension || ext));
|
||||||
}
|
}
|
||||||
|
568
test/test.js
568
test/test.js
@ -26,308 +26,308 @@ var EMPTY_PAGE = STATIC_PREFIX + '/empty.html';
|
|||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000;
|
||||||
|
|
||||||
describe('Puppeteer', function() {
|
describe('Puppeteer', function() {
|
||||||
var browser;
|
var browser;
|
||||||
var staticServer;
|
var staticServer;
|
||||||
var page;
|
var page;
|
||||||
|
|
||||||
beforeAll(function() {
|
beforeAll(function() {
|
||||||
browser = new Browser({args: ['--no-sandbox']});
|
browser = new Browser({args: ['--no-sandbox']});
|
||||||
staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT);
|
staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT);
|
||||||
GoldenUtils.removeOutputDir();
|
GoldenUtils.removeOutputDir();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(function() {
|
afterAll(function() {
|
||||||
browser.close();
|
browser.close();
|
||||||
staticServer.stop();
|
staticServer.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(SX(async function() {
|
beforeEach(SX(async function() {
|
||||||
page = await browser.newPage();
|
page = await browser.newPage();
|
||||||
GoldenUtils.addMatchers(jasmine);
|
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);
|
||||||
}));
|
}));
|
||||||
|
it('should await promise', SX(async function() {
|
||||||
afterEach(function() {
|
var result = await page.evaluate(() => Promise.resolve(8 * 7));
|
||||||
page.close();
|
expect(result).toBe(56);
|
||||||
});
|
|
||||||
|
|
||||||
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 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('Page Events: ConsoleMessage', SX(async function() {
|
||||||
it('should fail when navigating to bad url', SX(async function() {
|
var msgs = [];
|
||||||
var success = await page.navigate('asdfasdf');
|
page.on('consolemessage', msg => msgs.push(msg));
|
||||||
expect(success).toBe(false);
|
await page.evaluate(() => console.log('Message!'));
|
||||||
}));
|
expect(msgs).toEqual(['Message!']);
|
||||||
it('should succeed when navigating to good url', SX(async function() {
|
}));
|
||||||
var success = await page.navigate(EMPTY_PAGE);
|
|
||||||
expect(success).toBe(true);
|
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() {
|
describe('Page.Events.Error', function() {
|
||||||
it('should work', SX(async function() {
|
it('should fire', function(done) {
|
||||||
await page.setInPageCallback('callController', function(a, b) {
|
page.on('error', error => {
|
||||||
return a * b;
|
expect(error.message).toContain('Fancy');
|
||||||
});
|
done();
|
||||||
var result = await page.evaluate(async function() {
|
});
|
||||||
return await callController(9, 4);
|
page.navigate(STATIC_PREFIX + '/error.html');
|
||||||
});
|
|
||||||
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() {
|
describe('Page.screenshot', function() {
|
||||||
it('should intercept', SX(async function() {
|
it('should work', SX(async function() {
|
||||||
page.setRequestInterceptor(request => {
|
await page.setViewportSize({width: 500, height: 500});
|
||||||
expect(request.url()).toContain('empty.html');
|
await page.navigate(STATIC_PREFIX + '/grid.html');
|
||||||
expect(request.headers()['User-Agent']).toBeTruthy();
|
var screenshot = await page.screenshot();
|
||||||
expect(request.method()).toBe('GET');
|
expect(screenshot).toBeGolden('screenshot-sanity.png');
|
||||||
expect(request.postData()).toBe(undefined);
|
}));
|
||||||
request.continue();
|
it('should clip rect', SX(async function() {
|
||||||
});
|
await page.setViewportSize({width: 500, height: 500});
|
||||||
var success = await page.navigate(EMPTY_PAGE);
|
await page.navigate(STATIC_PREFIX + '/grid.html');
|
||||||
expect(success).toBe(true);
|
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({
|
var screenshot = await promises[1];
|
||||||
foo: 'bar'
|
expect(screenshot).toBeGolden('screenshot-parallel-calls.png');
|
||||||
});
|
}));
|
||||||
page.setRequestInterceptor(request => {
|
it('should take fullPage screenshots', SX(async function() {
|
||||||
expect(request.headers()['foo']).toBe('bar');
|
await page.setViewportSize({width: 500, height: 500});
|
||||||
request.continue();
|
await page.navigate(STATIC_PREFIX + '/grid.html');
|
||||||
});
|
var screenshot = await page.screenshot({
|
||||||
var success = await page.navigate(EMPTY_PAGE);
|
fullPage: true
|
||||||
expect(success).toBe(true);
|
});
|
||||||
}));
|
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
|
||||||
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() {
|
describe('Frame Management', function() {
|
||||||
it('should fire', function(done) {
|
var FrameUtils = require('./frame-utils');
|
||||||
page.on('dialog', dialog => {
|
it('should handle nested frames', SX(async function() {
|
||||||
expect(dialog.type).toBe('alert');
|
await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html');
|
||||||
expect(dialog.message()).toBe('yo');
|
expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt');
|
||||||
done();
|
}));
|
||||||
});
|
it('should send events when frames are manipulated dynamically', SX(async function() {
|
||||||
page.evaluate(() => alert('yo'));
|
await page.navigate(EMPTY_PAGE);
|
||||||
});
|
// validate frameattached events
|
||||||
// TODO Enable this when crbug.com/718235 is fixed.
|
var attachedFrames = [];
|
||||||
xit('should allow accepting prompts', SX(async function(done) {
|
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||||
page.on('dialog', dialog => {
|
await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html');
|
||||||
expect(dialog.type).toBe('prompt');
|
expect(attachedFrames.length).toBe(1);
|
||||||
expect(dialog.message()).toBe('question?');
|
expect(attachedFrames[0].url()).toContain('/assets/frame.html');
|
||||||
dialog.accept('answer!');
|
|
||||||
});
|
|
||||||
var result = await page.evaluate(() => prompt('question?'));
|
|
||||||
expect(result).toBe('answer!');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Page.Events.Error', function() {
|
// validate framenavigated events
|
||||||
it('should fire', function(done) {
|
var navigatedFrames = [];
|
||||||
page.on('error', error => {
|
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||||
expect(error.message).toContain('Fancy');
|
await FrameUtils.navigateFrame(page, 'frame1', './empty.html');
|
||||||
done();
|
expect(navigatedFrames.length).toBe(1);
|
||||||
});
|
expect(navigatedFrames[0].url()).toContain('/empty.html');
|
||||||
page.navigate(STATIC_PREFIX + '/error.html');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Page.screenshot', function() {
|
// validate framedetached events
|
||||||
it('should work', SX(async function() {
|
var detachedFrames = [];
|
||||||
await page.setViewportSize({width: 500, height: 500});
|
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||||
await page.navigate(STATIC_PREFIX + '/grid.html');
|
await FrameUtils.detachFrame(page, 'frame1');
|
||||||
var screenshot = await page.screenshot();
|
expect(detachedFrames.length).toBe(1);
|
||||||
expect(screenshot).toBeGolden('screenshot-sanity.png');
|
expect(detachedFrames[0].isDetached()).toBe(true);
|
||||||
}));
|
}));
|
||||||
it('should clip rect', SX(async function() {
|
it('should persist mainFrame on cross-process navigation', SX(async function() {
|
||||||
await page.setViewportSize({width: 500, height: 500});
|
await page.navigate(EMPTY_PAGE);
|
||||||
await page.navigate(STATIC_PREFIX + '/grid.html');
|
var mainFrame = page.mainFrame();
|
||||||
var screenshot = await page.screenshot({
|
await page.navigate('http://127.0.0.1:' + PORT + '/empty.html');
|
||||||
clip: {
|
expect(page.mainFrame() === mainFrame).toBeTruthy();
|
||||||
x: 50,
|
}));
|
||||||
y: 100,
|
it('should not send attach/detach events for main frame', SX(async function() {
|
||||||
width: 150,
|
var hasEvents = false;
|
||||||
height: 100
|
page.on('frameattached', frame => hasEvents = true);
|
||||||
}
|
page.on('framedetached', frame => hasEvents = true);
|
||||||
});
|
await page.navigate(EMPTY_PAGE);
|
||||||
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
|
expect(hasEvents).toBe(false);
|
||||||
}));
|
}));
|
||||||
it('should work for offscreen clip', SX(async function() {
|
it('should detach child frames on navigation', SX(async function() {
|
||||||
await page.setViewportSize({width: 500, height: 500});
|
var attachedFrames = [];
|
||||||
await page.navigate(STATIC_PREFIX + '/grid.html');
|
var detachedFrames = [];
|
||||||
var screenshot = await page.screenshot({
|
var navigatedFrames = [];
|
||||||
clip: {
|
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||||
x: 50,
|
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||||
y: 600,
|
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||||
width: 100,
|
await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html');
|
||||||
height: 100
|
expect(attachedFrames.length).toBe(4);
|
||||||
}
|
expect(detachedFrames.length).toBe(0);
|
||||||
});
|
expect(navigatedFrames.length).toBe(5);
|
||||||
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');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Frame Management', function() {
|
var attachedFrames = [];
|
||||||
var FrameUtils = require('./frame-utils');
|
var detachedFrames = [];
|
||||||
it('should handle nested frames', SX(async function() {
|
var navigatedFrames = [];
|
||||||
await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html');
|
await page.navigate(EMPTY_PAGE);
|
||||||
expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt');
|
expect(attachedFrames.length).toBe(0);
|
||||||
}));
|
expect(detachedFrames.length).toBe(4);
|
||||||
it('should send events when frames are manipulated dynamically', SX(async function() {
|
expect(navigatedFrames.length).toBe(1);
|
||||||
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);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Since Jasmine doesn't like async functions, they should be wrapped
|
// Since Jasmine doesn't like async functions, they should be wrapped
|
||||||
// in a SX function.
|
// in a SX function.
|
||||||
function SX(fun) {
|
function SX(fun) {
|
||||||
return done => Promise.resolve(fun()).then(done).catch(done.fail);
|
return done => Promise.resolve(fun()).then(done).catch(done.fail);
|
||||||
}
|
}
|
||||||
|
@ -25,107 +25,107 @@ var URL = require('url');
|
|||||||
var CHROMIUM_PATH = path.join(__dirname, '..', '.local-chromium');
|
var CHROMIUM_PATH = path.join(__dirname, '..', '.local-chromium');
|
||||||
|
|
||||||
var downloadURLs = {
|
var downloadURLs = {
|
||||||
linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.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',
|
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',
|
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',
|
win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* @return {!Array<string>}
|
* @return {!Array<string>}
|
||||||
*/
|
*/
|
||||||
supportedPlatforms: function() {
|
supportedPlatforms: function() {
|
||||||
return Object.keys(downloadURLs);
|
return Object.keys(downloadURLs);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
currentPlatform: function() {
|
currentPlatform: function() {
|
||||||
var platform = os.platform();
|
var platform = os.platform();
|
||||||
if (platform === 'darwin')
|
if (platform === 'darwin')
|
||||||
return 'mac';
|
return 'mac';
|
||||||
if (platform === 'linux')
|
if (platform === 'linux')
|
||||||
return 'linux';
|
return 'linux';
|
||||||
if (platform === 'win32')
|
if (platform === 'win32')
|
||||||
return os.arch() === 'x64' ? 'win64' : 'win32';
|
return os.arch() === 'x64' ? 'win64' : 'win32';
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
* @param {string} revision
|
* @param {string} revision
|
||||||
* @return {!Promise<boolean>}
|
* @return {!Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
canDownloadRevision: function(platform, revision) {
|
canDownloadRevision: function(platform, revision) {
|
||||||
console.assert(downloadURLs[platform], 'Unknown platform: ' + platform);
|
console.assert(downloadURLs[platform], 'Unknown platform: ' + platform);
|
||||||
var url = URL.parse(util.format(downloadURLs[platform], revision));
|
var url = URL.parse(util.format(downloadURLs[platform], revision));
|
||||||
var options = {
|
var options = {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
host: url.host,
|
host: url.host,
|
||||||
path: url.pathname,
|
path: url.pathname,
|
||||||
};
|
};
|
||||||
var resolve;
|
var resolve;
|
||||||
var promise = new Promise(x => resolve = x);
|
var promise = new Promise(x => resolve = x);
|
||||||
var request = https.request(options, response => {
|
var request = https.request(options, response => {
|
||||||
resolve(response.statusCode === 200);
|
resolve(response.statusCode === 200);
|
||||||
});
|
});
|
||||||
request.on('error', error => {
|
request.on('error', error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
});
|
});
|
||||||
request.end();
|
request.end();
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
* @param {string} revision
|
* @param {string} revision
|
||||||
* @param {?function(number, number)} progressCallback
|
* @param {?function(number, number)} progressCallback
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
downloadRevision: async function(platform, revision, progressCallback) {
|
downloadRevision: async function(platform, revision, progressCallback) {
|
||||||
var url = downloadURLs[platform];
|
var url = downloadURLs[platform];
|
||||||
console.assert(url, `Unsupported platform: ${platform}`);
|
console.assert(url, `Unsupported platform: ${platform}`);
|
||||||
url = util.format(url, revision);
|
url = util.format(url, revision);
|
||||||
var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`);
|
var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`);
|
||||||
var folderPath = getFolderPath(platform, revision);
|
var folderPath = getFolderPath(platform, revision);
|
||||||
if (fs.existsSync(folderPath))
|
if (fs.existsSync(folderPath))
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(CHROMIUM_PATH))
|
if (!fs.existsSync(CHROMIUM_PATH))
|
||||||
fs.mkdirSync(CHROMIUM_PATH);
|
fs.mkdirSync(CHROMIUM_PATH);
|
||||||
await downloadFile(url, zipPath, progressCallback);
|
await downloadFile(url, zipPath, progressCallback);
|
||||||
await extractZip(zipPath, folderPath);
|
await extractZip(zipPath, folderPath);
|
||||||
} finally {
|
} finally {
|
||||||
if (fs.existsSync(zipPath))
|
if (fs.existsSync(zipPath))
|
||||||
fs.unlinkSync(zipPath);
|
fs.unlinkSync(zipPath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
* @param {string} revision
|
* @param {string} revision
|
||||||
* @return {?{executablePath: string}}
|
* @return {?{executablePath: string}}
|
||||||
*/
|
*/
|
||||||
revisionInfo: function(platform, revision) {
|
revisionInfo: function(platform, revision) {
|
||||||
var folderPath = getFolderPath(platform, revision);
|
var folderPath = getFolderPath(platform, revision);
|
||||||
if (!fs.existsSync(folderPath))
|
if (!fs.existsSync(folderPath))
|
||||||
return null;
|
return null;
|
||||||
var executablePath = '';
|
var executablePath = '';
|
||||||
if (platform === 'mac')
|
if (platform === 'mac')
|
||||||
executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
||||||
else if (platform === 'linux')
|
else if (platform === 'linux')
|
||||||
executablePath = path.join(folderPath, 'chrome-linux', 'chrome');
|
executablePath = path.join(folderPath, 'chrome-linux', 'chrome');
|
||||||
else if (platform === 'win32' || platform === 'win64')
|
else if (platform === 'win32' || platform === 'win64')
|
||||||
executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe');
|
executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe');
|
||||||
else
|
else
|
||||||
throw 'Unsupported platfrom: ' + platfrom;
|
throw 'Unsupported platfrom: ' + platfrom;
|
||||||
return {
|
return {
|
||||||
executablePath: executablePath
|
executablePath: executablePath
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +134,7 @@ module.exports = {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function getFolderPath(platform, revision) {
|
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}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
function downloadFile(url, destinationPath, progressCallback) {
|
function downloadFile(url, destinationPath, progressCallback) {
|
||||||
var fulfill, reject;
|
var fulfill, reject;
|
||||||
var promise = new Promise((x, y) => { fulfill = x; reject = y; });
|
var promise = new Promise((x, y) => { fulfill = x; reject = y; });
|
||||||
var request = https.get(url, response => {
|
var request = https.get(url, response => {
|
||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
|
var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
|
||||||
// consume response data to free up memory
|
// consume response data to free up memory
|
||||||
response.resume();
|
response.resume();
|
||||||
reject(error);
|
reject(error);
|
||||||
return;
|
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 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<?Error>}
|
* @return {!Promise<?Error>}
|
||||||
*/
|
*/
|
||||||
function extractZip(zipPath, folderPath) {
|
function extractZip(zipPath, folderPath) {
|
||||||
return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill));
|
return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill));
|
||||||
}
|
}
|
||||||
|
@ -20,43 +20,43 @@ var https = require('https');
|
|||||||
var OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json';
|
var OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json';
|
||||||
|
|
||||||
var colors = {
|
var colors = {
|
||||||
reset: '\x1b[0m',
|
reset: '\x1b[0m',
|
||||||
red: '\x1b[31m',
|
red: '\x1b[31m',
|
||||||
green: '\x1b[32m',
|
green: '\x1b[32m',
|
||||||
yellow: '\x1b[33m'
|
yellow: '\x1b[33m'
|
||||||
};
|
};
|
||||||
|
|
||||||
class Table {
|
class Table {
|
||||||
/**
|
/**
|
||||||
* @param {!Array<number>} columnWidths
|
* @param {!Array<number>} columnWidths
|
||||||
*/
|
*/
|
||||||
constructor(columnWidths) {
|
constructor(columnWidths) {
|
||||||
this.widths = columnWidths;
|
this.widths = columnWidths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Array<string>} values
|
* @param {!Array<string>} values
|
||||||
*/
|
*/
|
||||||
drawRow(values) {
|
drawRow(values) {
|
||||||
console.assert(values.length === this.widths.length);
|
console.assert(values.length === this.widths.length);
|
||||||
var row = '';
|
var row = '';
|
||||||
for (var i = 0; i < values.length; ++i)
|
for (var i = 0; i < values.length; ++i)
|
||||||
row += padCenter(values[i], this.widths[i]);
|
row += padCenter(values[i], this.widths[i]);
|
||||||
console.log(row);
|
console.log(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.argv.length === 2) {
|
if (process.argv.length === 2) {
|
||||||
checkOmahaProxyAvailability();
|
checkOmahaProxyAvailability();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (process.argv.length !== 4) {
|
if (process.argv.length !== 4) {
|
||||||
console.log(`
|
console.log(`
|
||||||
Usage: node check_revisions.js [fromRevision] [toRevision]
|
Usage: node check_revisions.js [fromRevision] [toRevision]
|
||||||
|
|
||||||
This script checks availability of different prebuild chromium revisions.
|
This script checks availability of different prebuild chromium revisions.
|
||||||
Running command without arguments will check against omahaproxy revisions.`);
|
Running command without arguments will check against omahaproxy revisions.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromRevision = parseInt(process.argv[2], 10);
|
var fromRevision = parseInt(process.argv[2], 10);
|
||||||
@ -67,27 +67,27 @@ checkRangeAvailability(fromRevision, toRevision);
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async function checkOmahaProxyAvailability() {
|
async function checkOmahaProxyAvailability() {
|
||||||
console.log('Fetching revisions from ' + OMAHA_PROXY);
|
console.log('Fetching revisions from ' + OMAHA_PROXY);
|
||||||
var platforms = await loadJSON(OMAHA_PROXY);
|
var platforms = await loadJSON(OMAHA_PROXY);
|
||||||
if (!platforms) {
|
if (!platforms) {
|
||||||
console.error('ERROR: failed to fetch chromium revisions from omahaproxy.');
|
console.error('ERROR: failed to fetch chromium revisions from omahaproxy.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var table = new Table([27, 7, 7, 7, 7]);
|
var table = new Table([27, 7, 7, 7, 7]);
|
||||||
table.drawRow([''].concat(Downloader.supportedPlatforms()));
|
table.drawRow([''].concat(Downloader.supportedPlatforms()));
|
||||||
for (var platform of platforms) {
|
for (var platform of platforms) {
|
||||||
// Trust only to the main platforms.
|
// Trust only to the main platforms.
|
||||||
if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux')
|
if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux')
|
||||||
continue;
|
continue;
|
||||||
var osName = platform.os === 'win' ? 'win32' : platform.os;
|
var osName = platform.os === 'win' ? 'win32' : platform.os;
|
||||||
for (var version of platform.versions) {
|
for (var version of platform.versions) {
|
||||||
if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable')
|
if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable')
|
||||||
continue;
|
continue;
|
||||||
var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15);
|
var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15);
|
||||||
var revision = parseInt(version.branch_base_position, 10);
|
var revision = parseInt(version.branch_base_position, 10);
|
||||||
await checkAndDrawRevisionAvailability(table, revisionName, revision);
|
await checkAndDrawRevisionAvailability(table, revisionName, revision);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,11 +96,11 @@ async function checkOmahaProxyAvailability() {
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async function checkRangeAvailability(fromRevision, toRevision) {
|
async function checkRangeAvailability(fromRevision, toRevision) {
|
||||||
var table = new Table([10, 7, 7, 7, 7]);
|
var table = new Table([10, 7, 7, 7, 7]);
|
||||||
table.drawRow([''].concat(Downloader.supportedPlatforms()));
|
table.drawRow([''].concat(Downloader.supportedPlatforms()));
|
||||||
var inc = fromRevision < toRevision ? 1 : -1;
|
var inc = fromRevision < toRevision ? 1 : -1;
|
||||||
for (var revision = fromRevision; revision !== toRevision; revision += inc)
|
for (var revision = fromRevision; revision !== toRevision; revision += inc)
|
||||||
await checkAndDrawRevisionAvailability(table, '', revision);
|
await checkAndDrawRevisionAvailability(table, '', revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,18 +110,18 @@ async function checkRangeAvailability(fromRevision, toRevision) {
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
async function checkAndDrawRevisionAvailability(table, name, revision) {
|
async function checkAndDrawRevisionAvailability(table, name, revision) {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
for (var platform of Downloader.supportedPlatforms())
|
for (var platform of Downloader.supportedPlatforms())
|
||||||
promises.push(Downloader.canDownloadRevision(platform, revision));
|
promises.push(Downloader.canDownloadRevision(platform, revision));
|
||||||
var availability = await Promise.all(promises);
|
var availability = await Promise.all(promises);
|
||||||
var allAvailable = availability.every(e => !!e);
|
var allAvailable = availability.every(e => !!e);
|
||||||
var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
|
var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
|
||||||
for (var i = 0; i < availability.length; ++i) {
|
for (var i = 0; i < availability.length; ++i) {
|
||||||
var decoration = availability[i] ? '+' : '-';
|
var decoration = availability[i] ? '+' : '-';
|
||||||
var color = availability[i] ? colors.green : colors.red;
|
var color = availability[i] ? colors.green : colors.red;
|
||||||
values.push(color + decoration + colors.reset);
|
values.push(color + decoration + colors.reset);
|
||||||
}
|
}
|
||||||
table.drawRow(values);
|
table.drawRow(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,26 +129,26 @@ async function checkAndDrawRevisionAvailability(table, name, revision) {
|
|||||||
* @return {!Promise<?Object>}
|
* @return {!Promise<?Object>}
|
||||||
*/
|
*/
|
||||||
function loadJSON(url) {
|
function loadJSON(url) {
|
||||||
var resolve;
|
var resolve;
|
||||||
var promise = new Promise(x => resolve = x);
|
var promise = new Promise(x => resolve = x);
|
||||||
https.get(url, response => {
|
https.get(url, response => {
|
||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var body = '';
|
var body = '';
|
||||||
response.on('data', function(chunk){
|
response.on('data', function(chunk){
|
||||||
body += 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);
|
|
||||||
});
|
});
|
||||||
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}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function spaceString(size) {
|
function spaceString(size) {
|
||||||
return new Array(size).fill(' ').join('');
|
return new Array(size).fill(' ').join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,11 +164,11 @@ function spaceString(size) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function filterOutColors(text) {
|
function filterOutColors(text) {
|
||||||
for (var colorName in colors) {
|
for (var colorName in colors) {
|
||||||
var color = colors[colorName];
|
var color = colors[colorName];
|
||||||
text = text.replace(color, '');
|
text = text.replace(color, '');
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,8 +177,8 @@ function filterOutColors(text) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function padLeft(text, length) {
|
function padLeft(text, length) {
|
||||||
var printableCharacters = filterOutColors(text);
|
var printableCharacters = filterOutColors(text);
|
||||||
return printableCharacters.length >= length ? text : spaceString(length - text.length) + text;
|
return printableCharacters.length >= length ? text : spaceString(length - text.length) + text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,10 +187,10 @@ function padLeft(text, length) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function padCenter(text, length) {
|
function padCenter(text, length) {
|
||||||
var printableCharacters = filterOutColors(text);
|
var printableCharacters = filterOutColors(text);
|
||||||
if (printableCharacters.length >= length)
|
if (printableCharacters.length >= length)
|
||||||
return text;
|
return text;
|
||||||
var left = Math.floor((length - printableCharacters.length) / 2);
|
var left = Math.floor((length - printableCharacters.length) / 2);
|
||||||
var right = Math.ceil((length - printableCharacters.length) / 2);
|
var right = Math.ceil((length - printableCharacters.length) / 2);
|
||||||
return spaceString(left) + text + spaceString(right);
|
return spaceString(left) + text + spaceString(right);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user