mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: Introduce BrowserFetcher class (#1983)
This patch introduces `BrowserFetcher` class that manages downloaded versions of products. This patch: - shapes Downloader API to be minimal yet usable for our needs. This includes removing such methods as `Downloader.supportedPlatforms` and `Downloader.defaultRevision`. - makes most of the fs-related methods in Downloader async. The only exception is the `Downloader.revisionInfo`: it has stay sync due to the `pptr.executablePath()` method being sync. - updates `install.js` and `utils/check_availability.js` to use new API - finally, renames `Downloader` into `BrowserFetcher` Fixes #1748.
This commit is contained in:
parent
18c975509f
commit
a363a733b7
73
docs/api.md
73
docs/api.md
@ -10,9 +10,17 @@
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [class: Puppeteer](#class-puppeteer)
|
||||
* [puppeteer.connect(options)](#puppeteerconnectoptions)
|
||||
* [puppeteer.createBrowserFetcher([options])](#puppeteercreatebrowserfetcheroptions)
|
||||
* [puppeteer.defaultArgs()](#puppeteerdefaultargs)
|
||||
* [puppeteer.executablePath()](#puppeteerexecutablepath)
|
||||
* [puppeteer.launch([options])](#puppeteerlaunchoptions)
|
||||
- [class: BrowserFetcher](#class-browserfetcher)
|
||||
* [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision)
|
||||
* [browserFetcher.download(revision[, progressCallback])](#browserfetcherdownloadrevision-progresscallback)
|
||||
* [browserFetcher.localRevisions()](#browserfetcherlocalrevisions)
|
||||
* [browserFetcher.platform()](#browserfetcherplatform)
|
||||
* [browserFetcher.remove(revision)](#browserfetcherremoverevision)
|
||||
* [browserFetcher.revisionInfo(revision)](#browserfetcherrevisioninforevision)
|
||||
- [class: Browser](#class-browser)
|
||||
* [event: 'disconnected'](#event-disconnected)
|
||||
* [event: 'targetchanged'](#event-targetchanged)
|
||||
@ -273,6 +281,13 @@ puppeteer.launch().then(async browser => {
|
||||
|
||||
This methods attaches Puppeteer to an existing Chromium instance.
|
||||
|
||||
#### puppeteer.createBrowserFetcher([options])
|
||||
- `options` <[Object]>
|
||||
- `host` <[string]> A download host to be used. Defaults to `https://storage.googleapis.com`.
|
||||
- `path` <[string]> A path for the downloads folder. Defaults to `<root>/.local-chromium`, where `<root>` is puppeteer's package root.
|
||||
- `platform` <[string]> Possible values are: `mac`, `win32`, `win64`, `linux`. Defaults to the current platform.
|
||||
- returns: <[BrowserFetcher]>
|
||||
|
||||
#### puppeteer.defaultArgs()
|
||||
- returns: <[Array]<[string]>> The default flags that Chromium will be launched with.
|
||||
|
||||
@ -307,6 +322,63 @@ If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://
|
||||
>
|
||||
> See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description
|
||||
of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkcr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
|
||||
|
||||
### class: BrowserFetcher
|
||||
|
||||
BrowserFetcher can download and manage different versions of Chromium.
|
||||
|
||||
BrowserFetcher operates on revision strings that specify a precise version of Chromium, e.g. `"533271"`. Revision strings can be obtained from [omahaproxy.appspot.com](http://omahaproxy.appspot.com/).
|
||||
|
||||
Example on how to use BrowserFetcher to download a specific version of Chromium and run
|
||||
puppeteer against it:
|
||||
|
||||
```js
|
||||
const browserFetcher = puppeteer.createBrowserFetcher();
|
||||
const revisionInfo = await browserFetcher.download('533271');
|
||||
const browser = await puppeteer.launch({executablePath: revisionInfo.executablePath})
|
||||
```
|
||||
|
||||
> **NOTE** BrowserFetcher is not designed to work concurrently with other
|
||||
> instances of BrowserFetcher that share the same downloads directory.
|
||||
|
||||
#### browserFetcher.canDownload(revision)
|
||||
- `revision` <[string]> a revision to check availability.
|
||||
- returns: <[Promise]<[boolean]>> returns `true` if the revision could be downloaded from the host.
|
||||
|
||||
The method initiates a HEAD request to check if the revision is available.
|
||||
|
||||
#### browserFetcher.download(revision[, progressCallback])
|
||||
- `revision` <[string]> a revision to download.
|
||||
- `progressCallback` <[function]([number], [number])> A function that will be called with two arguments:
|
||||
- `downloadedBytes` <[number]> how many bytes have been downloaded
|
||||
- `totalBytes` <[number]> how large is the total download.
|
||||
- returns: <[Promise]<[Object]>> Resolves with revision information when the revision is downloaded and extracted
|
||||
- `revision` <[string]> the revision the info was created from
|
||||
- `folderPath` <[string]> path to the extracted revision folder
|
||||
- `executablePath` <[string]> path to the revision executable
|
||||
- `url` <[string]> URL this revision can be downloaded from
|
||||
- `local` <[boolean]> whether the revision is locally available on disk
|
||||
|
||||
The method initiates a GET request to download the revision from the host.
|
||||
|
||||
#### browserFetcher.localRevisions()
|
||||
- returns: <[Promise]<[Array]<[string]>>> A list of all revisions available locally on disk.
|
||||
|
||||
#### browserFetcher.platform()
|
||||
- returns: <[string]> Returns one of `mac`, `linux`, `win32` or `win64`.
|
||||
|
||||
#### browserFetcher.remove(revision)
|
||||
- `revision` <[string]> a revision to remove. The method will throw if the revision has not been downloaded.
|
||||
- returns: <[Promise]> Resolves when the revision has been removed.
|
||||
|
||||
#### browserFetcher.revisionInfo(revision)
|
||||
- `revision` <[string]> a revision to get info for.
|
||||
- returns: <[Object]>
|
||||
- `revision` <[string]> the revision the info was created from
|
||||
- `folderPath` <[string]> path to the extracted revision folder
|
||||
- `executablePath` <[string]> path to the revision executable
|
||||
- `url` <[string]> URL this revision can be downloaded from
|
||||
- `local` <[boolean]> whether the revision is locally available on disk
|
||||
|
||||
### class: Browser
|
||||
|
||||
@ -2489,6 +2561,7 @@ reported.
|
||||
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "String"
|
||||
[stream.Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "stream.Readable"
|
||||
[CDPSession]: #class-cdpsession "CDPSession"
|
||||
[BrowserFetcher]: #class-browserfetcher "BrowserFetcher"
|
||||
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
|
||||
[Frame]: #class-frame "Frame"
|
||||
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
|
||||
|
37
install.js
37
install.js
@ -25,16 +25,16 @@ if (process.env.NPM_CONFIG_PUPPETEER_SKIP_CHROMIUM_DOWNLOAD || process.env.npm_c
|
||||
return;
|
||||
}
|
||||
|
||||
const Downloader = require('./lib/Downloader');
|
||||
const downloader = Downloader.createDefault();
|
||||
const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host;
|
||||
|
||||
const platform = downloader.currentPlatform();
|
||||
const revision = Downloader.defaultRevision();
|
||||
const ProgressBar = require('progress');
|
||||
const puppeteer = require('./index');
|
||||
const browserFetcher = puppeteer.createBrowserFetcher({ host: downloadHost });
|
||||
|
||||
const revision = require('./package.json').puppeteer.chromium_revision;
|
||||
const revisionInfo = browserFetcher.revisionInfo(revision);
|
||||
|
||||
const revisionInfo = downloader.revisionInfo(platform, revision);
|
||||
// Do nothing if the revision is already downloaded.
|
||||
if (revisionInfo.downloaded)
|
||||
if (revisionInfo.local)
|
||||
return;
|
||||
|
||||
// Override current environment proxy settings with npm configuration, if any.
|
||||
@ -49,21 +49,20 @@ if (NPM_HTTP_PROXY)
|
||||
if (NPM_NO_PROXY)
|
||||
process.env.NO_PROXY = NPM_NO_PROXY;
|
||||
|
||||
const allRevisions = downloader.downloadedRevisions();
|
||||
const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host;
|
||||
if (downloadHost)
|
||||
downloader.setDownloadHost(downloadHost);
|
||||
downloader.downloadRevision(platform, revision, onProgress)
|
||||
browserFetcher.download(revisionInfo.revision, onProgress)
|
||||
.then(() => browserFetcher.localRevisions())
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
|
||||
/**
|
||||
* @param {!Array<string>}
|
||||
* @return {!Promise}
|
||||
*/
|
||||
function onSuccess() {
|
||||
function onSuccess(localRevisions) {
|
||||
console.log('Chromium downloaded to ' + revisionInfo.folderPath);
|
||||
localRevisions = localRevisions.filter(revision => revision !== revisionInfo.revision);
|
||||
// Remove previous chromium revisions.
|
||||
const cleanupOldVersions = allRevisions.map(({platform, revision}) => downloader.removeRevision(platform, revision));
|
||||
const cleanupOldVersions = localRevisions.map(revision => browserFetcher.remove(revision));
|
||||
return Promise.all(cleanupOldVersions);
|
||||
}
|
||||
|
||||
@ -77,15 +76,19 @@ function onError(error) {
|
||||
}
|
||||
|
||||
let progressBar = null;
|
||||
function onProgress(bytesTotal, delta) {
|
||||
let lastDownloadedBytes = 0;
|
||||
function onProgress(downloadedBytes, totalBytes) {
|
||||
if (!progressBar) {
|
||||
progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, {
|
||||
const ProgressBar = require('progress');
|
||||
progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, {
|
||||
complete: '=',
|
||||
incomplete: ' ',
|
||||
width: 20,
|
||||
total: bytesTotal,
|
||||
total: totalBytes,
|
||||
});
|
||||
}
|
||||
const delta = downloadedBytes - lastDownloadedBytes;
|
||||
lastDownloadedBytes = downloadedBytes;
|
||||
progressBar.tick(delta);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ const path = require('path');
|
||||
const extract = require('extract-zip');
|
||||
const util = require('util');
|
||||
const URL = require('url');
|
||||
const {helper} = require('./helper');
|
||||
const removeRecursive = require('rimraf');
|
||||
// @ts-ignore
|
||||
const ProxyAgent = require('https-proxy-agent');
|
||||
@ -34,70 +35,52 @@ const downloadURLs = {
|
||||
win64: '%s/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip',
|
||||
};
|
||||
|
||||
// Project root will be different for node6-transpiled code.
|
||||
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..');
|
||||
const readdirAsync = helper.promisify(fs.readdir.bind(fs));
|
||||
const mkdirAsync = helper.promisify(fs.mkdir.bind(fs));
|
||||
const unlinkAsync = helper.promisify(fs.unlink.bind(fs));
|
||||
|
||||
class Downloader {
|
||||
function existsAsync(filePath) {
|
||||
let fulfill = null;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
fs.access(filePath, err => fulfill(!err));
|
||||
return promise;
|
||||
}
|
||||
|
||||
class BrowserFetcher {
|
||||
/**
|
||||
* @param {string} downloadsFolder
|
||||
* @param {!BrowserFetcher.Options=} options
|
||||
*/
|
||||
constructor(downloadsFolder) {
|
||||
this._downloadsFolder = downloadsFolder;
|
||||
this._downloadHost = DEFAULT_DOWNLOAD_HOST;
|
||||
constructor(options = {}) {
|
||||
this._downloadsFolder = options.path || path.join(helper.projectRoot(), '.local-chromium');
|
||||
this._downloadHost = options.host || DEFAULT_DOWNLOAD_HOST;
|
||||
this._platform = options.platform || '';
|
||||
if (!this._platform) {
|
||||
const platform = os.platform();
|
||||
if (platform === 'darwin')
|
||||
this._platform = 'mac';
|
||||
else if (platform === 'linux')
|
||||
this._platform = 'linux';
|
||||
else if (platform === 'win32')
|
||||
this._platform = os.arch() === 'x64' ? 'win64' : 'win32';
|
||||
console.assert(this._platform, 'Unsupported platform: ' + os.platform());
|
||||
}
|
||||
const supportedPlatforms = ['mac', 'linux', 'win32', 'win64'];
|
||||
console.assert(supportedPlatforms.includes(this._platform), 'Unsupported platform: ' + this._platform);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
static defaultRevision() {
|
||||
return require(path.join(PROJECT_ROOT, 'package.json')).puppeteer.chromium_revision;
|
||||
platform() {
|
||||
return this._platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Downloader}
|
||||
*/
|
||||
static createDefault() {
|
||||
const downloadsFolder = path.join(PROJECT_ROOT, '.local-chromium');
|
||||
return new Downloader(downloadsFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} downloadHost
|
||||
*/
|
||||
setDownloadHost(downloadHost) {
|
||||
this._downloadHost = downloadHost.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
supportedPlatforms() {
|
||||
return Object.keys(downloadURLs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
currentPlatform() {
|
||||
const platform = os.platform();
|
||||
if (platform === 'darwin')
|
||||
return 'mac';
|
||||
if (platform === 'linux')
|
||||
return 'linux';
|
||||
if (platform === 'win32')
|
||||
return os.arch() === 'x64' ? 'win64' : 'win32';
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {string} revision
|
||||
* @return {!Promise<boolean>}
|
||||
*/
|
||||
canDownloadRevision(platform, revision) {
|
||||
console.assert(downloadURLs[platform], 'Unknown platform: ' + platform);
|
||||
|
||||
const url = util.format(downloadURLs[platform], this._downloadHost, revision);
|
||||
canDownload(revision) {
|
||||
const url = util.format(downloadURLs[this._platform], this._downloadHost, revision);
|
||||
|
||||
let resolve;
|
||||
const promise = new Promise(x => resolve = x);
|
||||
@ -112,90 +95,80 @@ class Downloader {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {string} revision
|
||||
* @param {?function(number, number)} progressCallback
|
||||
* @return {!Promise}
|
||||
* @return {!Promise<!BrowserFetcher.RevisionInfo>}
|
||||
*/
|
||||
downloadRevision(platform, revision, progressCallback) {
|
||||
let url = downloadURLs[platform];
|
||||
console.assert(url, `Unsupported platform: ${platform}`);
|
||||
async download(revision, progressCallback) {
|
||||
let url = downloadURLs[this._platform];
|
||||
url = util.format(url, this._downloadHost, revision);
|
||||
const zipPath = path.join(this._downloadsFolder, `download-${platform}-${revision}.zip`);
|
||||
const folderPath = this._getFolderPath(platform, revision);
|
||||
if (fs.existsSync(folderPath))
|
||||
return;
|
||||
if (!fs.existsSync(this._downloadsFolder))
|
||||
fs.mkdirSync(this._downloadsFolder);
|
||||
return downloadFile(url, zipPath, progressCallback)
|
||||
.then(() => extractZip(zipPath, folderPath))
|
||||
.catch(err => err)
|
||||
.then(err => {
|
||||
if (fs.existsSync(zipPath))
|
||||
fs.unlinkSync(zipPath);
|
||||
if (err)
|
||||
throw err;
|
||||
});
|
||||
const zipPath = path.join(this._downloadsFolder, `download-${this._platform}-${revision}.zip`);
|
||||
const folderPath = this._getFolderPath(revision);
|
||||
if (await existsAsync(folderPath))
|
||||
return this.revisionInfo(revision);
|
||||
if (!(await existsAsync(this._downloadsFolder)))
|
||||
await mkdirAsync(this._downloadsFolder);
|
||||
try {
|
||||
await downloadFile(url, zipPath, progressCallback);
|
||||
await extractZip(zipPath, folderPath);
|
||||
} finally {
|
||||
if (await existsAsync(zipPath))
|
||||
await unlinkAsync(zipPath);
|
||||
}
|
||||
return this.revisionInfo(revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<!{platform:string, revision: string}>}
|
||||
* @return {!Promise<!Array<string>>}
|
||||
*/
|
||||
downloadedRevisions() {
|
||||
if (!fs.existsSync(this._downloadsFolder))
|
||||
async localRevisions() {
|
||||
if (!await existsAsync(this._downloadsFolder))
|
||||
return [];
|
||||
const fileNames = fs.readdirSync(this._downloadsFolder);
|
||||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(revision => !!revision);
|
||||
const fileNames = await readdirAsync(this._downloadsFolder);
|
||||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry.revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {string} revision
|
||||
* @return {!Promise}
|
||||
*/
|
||||
removeRevision(platform, revision) {
|
||||
console.assert(downloadURLs[platform], `Unsupported platform: ${platform}`);
|
||||
const folderPath = this._getFolderPath(platform, revision);
|
||||
console.assert(fs.existsSync(folderPath));
|
||||
return new Promise(fulfill => removeRecursive(folderPath, fulfill));
|
||||
async remove(revision) {
|
||||
const folderPath = this._getFolderPath(revision);
|
||||
console.assert(await existsAsync(folderPath), `Failed to remove: revision ${revision} is not downloaded`);
|
||||
await new Promise(fulfill => removeRecursive(folderPath, fulfill));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {string} revision
|
||||
* @return {!{revision: string, folderPath: string, executablePath: string, downloaded: boolean}}
|
||||
* @return {!BrowserFetcher.RevisionInfo}
|
||||
*/
|
||||
revisionInfo(platform, revision) {
|
||||
console.assert(downloadURLs[platform], `Unsupported platform: ${platform}`);
|
||||
const folderPath = this._getFolderPath(platform, revision);
|
||||
revisionInfo(revision) {
|
||||
const folderPath = this._getFolderPath(revision);
|
||||
let executablePath = '';
|
||||
if (platform === 'mac')
|
||||
if (this._platform === 'mac')
|
||||
executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
|
||||
else if (platform === 'linux')
|
||||
else if (this._platform === 'linux')
|
||||
executablePath = path.join(folderPath, 'chrome-linux', 'chrome');
|
||||
else if (platform === 'win32' || platform === 'win64')
|
||||
else if (this._platform === 'win32' || this._platform === 'win64')
|
||||
executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe');
|
||||
else
|
||||
throw 'Unsupported platform: ' + platform;
|
||||
return {
|
||||
revision,
|
||||
executablePath,
|
||||
folderPath,
|
||||
downloaded: fs.existsSync(folderPath)
|
||||
};
|
||||
throw 'Unsupported platform: ' + this._platform;
|
||||
let url = downloadURLs[this._platform];
|
||||
url = util.format(url, this._downloadHost, revision);
|
||||
const local = fs.existsSync(folderPath);
|
||||
return {revision, executablePath, folderPath, local, url};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {string} revision
|
||||
* @return {string}
|
||||
*/
|
||||
_getFolderPath(platform, revision) {
|
||||
return path.join(this._downloadsFolder, platform + '-' + revision);
|
||||
_getFolderPath(revision) {
|
||||
return path.join(this._downloadsFolder, this._platform + '-' + revision);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Downloader;
|
||||
module.exports = BrowserFetcher;
|
||||
|
||||
/**
|
||||
* @param {string} folderPath
|
||||
@ -220,6 +193,8 @@ function parseFolderPath(folderPath) {
|
||||
*/
|
||||
function downloadFile(url, destinationPath, progressCallback) {
|
||||
let fulfill, reject;
|
||||
let downloadedBytes = 0;
|
||||
let totalBytes = 0;
|
||||
|
||||
const promise = new Promise((x, y) => { fulfill = x; reject = y; });
|
||||
|
||||
@ -235,15 +210,16 @@ function downloadFile(url, destinationPath, progressCallback) {
|
||||
file.on('finish', () => fulfill());
|
||||
file.on('error', error => reject(error));
|
||||
response.pipe(file);
|
||||
const totalBytes = parseInt(/** @type {string} */ (response.headers['content-length']), 10);
|
||||
totalBytes = parseInt(/** @type {string} */ (response.headers['content-length']), 10);
|
||||
if (progressCallback)
|
||||
response.on('data', onData.bind(null, totalBytes));
|
||||
response.on('data', onData);
|
||||
});
|
||||
request.on('error', error => reject(error));
|
||||
return promise;
|
||||
|
||||
function onData(totalBytes, chunk) {
|
||||
progressCallback(totalBytes, chunk.length);
|
||||
function onData(chunk) {
|
||||
downloadedBytes += chunk.length;
|
||||
progressCallback(downloadedBytes, totalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,3 +257,19 @@ function httpRequest(url, method, response) {
|
||||
request.end();
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BrowserFetcher.Options
|
||||
* @property {string=} platform
|
||||
* @property {string=} path
|
||||
* @property {string=} host
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BrowserFetcher.RevisionInfo
|
||||
* @property {string} folderPath
|
||||
* @property {string} executablePath
|
||||
* @property {string} url
|
||||
* @property {boolean} local
|
||||
* @property {string} revision
|
||||
*/
|
@ -17,13 +17,13 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const removeFolder = require('rimraf');
|
||||
const childProcess = require('child_process');
|
||||
const Downloader = require('./Downloader');
|
||||
const BrowserFetcher = require('./BrowserFetcher');
|
||||
const {Connection} = require('./Connection');
|
||||
const {Browser} = require('./Browser');
|
||||
const readline = require('readline');
|
||||
const fs = require('fs');
|
||||
const {helper} = require('./helper');
|
||||
const ChromiumRevision = Downloader.defaultRevision();
|
||||
const ChromiumRevision = require(path.join(helper.projectRoot(), 'package.json')).puppeteer.chromium_revision;
|
||||
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const removeFolderAsync = helper.promisify(removeFolder);
|
||||
@ -90,9 +90,9 @@ class Launcher {
|
||||
}
|
||||
let chromeExecutable = options.executablePath;
|
||||
if (typeof chromeExecutable !== 'string') {
|
||||
const downloader = Downloader.createDefault();
|
||||
const revisionInfo = downloader.revisionInfo(downloader.currentPlatform(), ChromiumRevision);
|
||||
console.assert(revisionInfo.downloaded, `Chromium revision is not downloaded. Run "npm install" or "yarn install"`);
|
||||
const browserFetcher = new BrowserFetcher();
|
||||
const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
|
||||
console.assert(revisionInfo.local, `Chromium revision is not downloaded. Run "npm install" or "yarn install"`);
|
||||
chromeExecutable = revisionInfo.executablePath;
|
||||
}
|
||||
if (Array.isArray(options.args))
|
||||
@ -187,8 +187,8 @@ class Launcher {
|
||||
* @return {string}
|
||||
*/
|
||||
static executablePath() {
|
||||
const downloader = Downloader.createDefault();
|
||||
const revisionInfo = downloader.revisionInfo(downloader.currentPlatform(), ChromiumRevision);
|
||||
const browserFetcher = new BrowserFetcher();
|
||||
const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
|
||||
return revisionInfo.executablePath;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
const {helper} = require('./helper');
|
||||
const Launcher = require('./Launcher');
|
||||
const BrowserFetcher = require('./BrowserFetcher');
|
||||
|
||||
class Puppeteer {
|
||||
/**
|
||||
@ -46,6 +47,14 @@ class Puppeteer {
|
||||
static defaultArgs() {
|
||||
return Launcher.defaultArgs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Object=} options
|
||||
* @return {!BrowserFetcher}
|
||||
*/
|
||||
static createBrowserFetcher(options) {
|
||||
return new BrowserFetcher(options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Puppeteer;
|
||||
|
@ -13,10 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const debugError = require('debug')(`puppeteer:error`);
|
||||
/** @type {?Map<string, boolean>} */
|
||||
let apiCoverage = null;
|
||||
let projectRoot = null;
|
||||
class Helper {
|
||||
/**
|
||||
* @param {Function|string} fun
|
||||
@ -41,6 +44,17 @@ class Helper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
static projectRoot() {
|
||||
if (!projectRoot) {
|
||||
// Project root will be different for node6-transpiled code.
|
||||
projectRoot = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..');
|
||||
}
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Object} exceptionDetails
|
||||
* @return {string}
|
||||
|
BIN
test/assets/chromium-linux.zip
Normal file
BIN
test/assets/chromium-linux.zip
Normal file
Binary file not shown.
@ -185,18 +185,20 @@ class SimpleServer {
|
||||
if (this._requestSubscribers.has(pathName))
|
||||
this._requestSubscribers.get(pathName)[fulfillSymbol].call(null, request);
|
||||
const handler = this._routes.get(pathName);
|
||||
if (handler)
|
||||
if (handler) {
|
||||
handler.call(null, request, response);
|
||||
else
|
||||
this.defaultHandler(request, response);
|
||||
} else {
|
||||
const pathName = url.parse(request.url).path;
|
||||
this.serveFile(request, response, pathName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!IncomingMessage} request
|
||||
* @param {!ServerResponse} response
|
||||
* @param {string} pathName
|
||||
*/
|
||||
defaultHandler(request, response) {
|
||||
let pathName = url.parse(request.url).path;
|
||||
serveFile(request, response, pathName) {
|
||||
if (pathName === '/')
|
||||
pathName = '/index.html';
|
||||
const filePath = path.join(this._dirPath, pathName.substring(1));
|
||||
|
28
test/test.js
28
test/test.js
@ -21,6 +21,7 @@ const {helper} = require('../lib/helper');
|
||||
if (process.env.COVERAGE)
|
||||
helper.recordPublicAPICoverage();
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const readFileAsync = helper.promisify(fs.readFile);
|
||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
||||
|
||||
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..');
|
||||
@ -113,6 +114,33 @@ afterAll(async({server, httpsServer}) => {
|
||||
});
|
||||
|
||||
describe('Puppeteer', function() {
|
||||
describe('BrowserFetcher', function() {
|
||||
it('should download and extract linux binary', async({server}) => {
|
||||
const downloadsFolder = await mkdtempAsync(TMP_FOLDER);
|
||||
const browserFetcher = puppeteer.createBrowserFetcher({
|
||||
platform: 'linux',
|
||||
path: downloadsFolder,
|
||||
host: server.PREFIX
|
||||
});
|
||||
let revisionInfo = browserFetcher.revisionInfo('123456');
|
||||
server.setRoute(revisionInfo.url.substring(server.PREFIX.length), (req, res) => {
|
||||
server.serveFile(req, res, '/chromium-linux.zip');
|
||||
});
|
||||
|
||||
expect(revisionInfo.local).toBe(false);
|
||||
expect(browserFetcher.platform()).toBe('linux');
|
||||
expect(await browserFetcher.canDownload('100000')).toBe(false);
|
||||
expect(await browserFetcher.canDownload('123456')).toBe(true);
|
||||
|
||||
revisionInfo = await browserFetcher.download('123456');
|
||||
expect(revisionInfo.local).toBe(true);
|
||||
expect(await readFileAsync(revisionInfo.executablePath, 'utf8')).toBe('LINUX BINARY\n');
|
||||
expect(await browserFetcher.localRevisions()).toEqual(['123456']);
|
||||
await browserFetcher.remove('123456');
|
||||
expect(await browserFetcher.localRevisions()).toEqual([]);
|
||||
rm(downloadsFolder);
|
||||
});
|
||||
});
|
||||
describe('Puppeteer.launch', function() {
|
||||
it('should support ignoreHTTPSErrors option', async({httpsServer}) => {
|
||||
const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions);
|
||||
|
@ -15,11 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Downloader = require('../lib/Downloader');
|
||||
const puppeteer = require('..');
|
||||
const https = require('https');
|
||||
const OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json';
|
||||
const SUPPORTER_PLATFORMS = ['linux', 'mac', 'win32', 'win64'];
|
||||
|
||||
const downloader = Downloader.createDefault();
|
||||
const fetchers = SUPPORTER_PLATFORMS.map(platform => puppeteer.createBrowserFetcher({platform}));
|
||||
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
@ -73,7 +74,7 @@ async function checkOmahaProxyAvailability() {
|
||||
return;
|
||||
}
|
||||
const table = new Table([27, 7, 7, 7, 7]);
|
||||
table.drawRow([''].concat(downloader.supportedPlatforms()));
|
||||
table.drawRow([''].concat(SUPPORTER_PLATFORMS));
|
||||
for (const platform of platforms) {
|
||||
// Trust only to the main platforms.
|
||||
if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux')
|
||||
@ -95,7 +96,7 @@ async function checkOmahaProxyAvailability() {
|
||||
*/
|
||||
async function checkRangeAvailability(fromRevision, toRevision) {
|
||||
const table = new Table([10, 7, 7, 7, 7]);
|
||||
table.drawRow([''].concat(downloader.supportedPlatforms()));
|
||||
table.drawRow([''].concat(SUPPORTER_PLATFORMS));
|
||||
const inc = fromRevision < toRevision ? 1 : -1;
|
||||
for (let revision = fromRevision; revision !== toRevision; revision += inc)
|
||||
await checkAndDrawRevisionAvailability(table, '', revision);
|
||||
@ -107,9 +108,7 @@ async function checkRangeAvailability(fromRevision, toRevision) {
|
||||
* @param {number} revision
|
||||
*/
|
||||
async function checkAndDrawRevisionAvailability(table, name, revision) {
|
||||
const promises = [];
|
||||
for (const platform of downloader.supportedPlatforms())
|
||||
promises.push(downloader.canDownloadRevision(platform, revision));
|
||||
const promises = fetchers.map(fetcher => fetcher.canDownload(revision));
|
||||
const availability = await Promise.all(promises);
|
||||
const allAvailable = availability.every(e => !!e);
|
||||
const values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
|
||||
|
@ -22,7 +22,6 @@ const Message = require('../Message');
|
||||
const EXCLUDE_CLASSES = new Set([
|
||||
'CSSCoverage',
|
||||
'Connection',
|
||||
'Downloader',
|
||||
'EmulationManager',
|
||||
'FrameManager',
|
||||
'JSCoverage',
|
||||
|
Loading…
Reference in New Issue
Block a user