playwright-core
Advanced tools
Comparing version 0.11.1-next.1584577781703 to 0.11.1-next.1584644386005
@@ -16,11 +16,14 @@ /** | ||
*/ | ||
const fs = require('fs'); | ||
const browserFetcher = require('./lib/server/browserFetcher.js'); | ||
const packageJSON = require('./package.json'); | ||
async function downloadBrowser(browserType) { | ||
const browser = browserType.name(); | ||
async function downloadBrowserWithProgressBar(downloadPath, browser, version = '') { | ||
let progressBar = null; | ||
let lastDownloadedBytes = 0; | ||
function onProgress(downloadedBytes, totalBytes) { | ||
const revision = packageJSON.playwright[`${browser}_revision`]; | ||
function progress(downloadedBytes, totalBytes) { | ||
if (!progressBar) { | ||
const ProgressBar = require('progress'); | ||
progressBar = new ProgressBar(`Downloading ${browser} ${browserType._revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, { | ||
progressBar = new ProgressBar(`Downloading ${browser} r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, { | ||
complete: '=', | ||
@@ -30,2 +33,3 @@ incomplete: ' ', | ||
total: totalBytes, | ||
host: getFromENV('PLAYWRIGHT_DOWNLOAD_HOST'), | ||
}); | ||
@@ -37,14 +41,12 @@ } | ||
} | ||
const fetcher = browserType._createBrowserFetcher(); | ||
const revisionInfo = fetcher.revisionInfo(); | ||
// Do nothing if the revision is already downloaded. | ||
if (revisionInfo.local) | ||
return revisionInfo; | ||
await browserType.downloadBrowserIfNeeded(onProgress); | ||
logPolitely(`${browser} downloaded to ${revisionInfo.folderPath}`); | ||
return revisionInfo; | ||
const executablePath = await browserFetcher.downloadBrowser({ | ||
downloadPath, | ||
browser, | ||
revision, | ||
progress, | ||
}); | ||
logPolitely(`${browser} downloaded to ${downloadPath}`); | ||
return executablePath; | ||
} | ||
function toMegabytes(bytes) { | ||
@@ -63,2 +65,9 @@ const mb = bytes / 1024 / 1024; | ||
module.exports = {downloadBrowser}; | ||
function getFromENV(name) { | ||
let value = process.env[name]; | ||
value = value || process.env[`npm_config_${name.toLowerCase()}`]; | ||
value = value || process.env[`npm_package_config_${name.toLowerCase()}`]; | ||
return value; | ||
} | ||
module.exports = {downloadBrowserWithProgressBar}; |
14
index.js
@@ -18,7 +18,15 @@ /** | ||
module.exports = new Playwright({ | ||
downloadPath: __dirname, | ||
const playwright = new Playwright({ | ||
browsers: ['webkit', 'chromium', 'firefox'], | ||
respectEnvironmentVariables: false, | ||
}); | ||
try { | ||
const downloadedBrowsers = require('./.downloaded-browsers.json'); | ||
playwright.chromium._executablePath = downloadedBrowsers.crExecutablePath; | ||
playwright.firefox._executablePath = downloadedBrowsers.ffExecutablePath; | ||
playwright.webkit._executablePath = downloadedBrowsers.wkExecutablePath; | ||
} catch (e) { | ||
} | ||
module.exports = playwright; | ||
@@ -83,3 +83,3 @@ /** | ||
private _setContentCounter; | ||
private _detachedPromise; | ||
readonly _detachedPromise: Promise<void>; | ||
private _detachedCallback; | ||
@@ -90,11 +90,2 @@ constructor(page: Page, id: string, parentFrame: Frame | null); | ||
waitForLoadState(options?: types.NavigateOptions): Promise<void>; | ||
_waitForSpecificDocument(expectedDocumentId: string): Disposable<Promise<Error | void>>; | ||
_waitForNewDocument(url?: types.URLMatch): Disposable<Promise<{ | ||
error?: Error; | ||
documentId: string; | ||
}>>; | ||
_waitForSameDocumentNavigation(url?: types.URLMatch): Disposable<Promise<void>>; | ||
_waitForLifecycle(waitUntil?: types.LifecycleEvent): Disposable<Promise<void>>; | ||
_trackDocumentRequests(): Disposable<Map<string, network.Request>>; | ||
_createFrameDestroyedPromise(): Promise<Error>; | ||
frameElement(): Promise<dom.ElementHandle>; | ||
@@ -154,6 +145,17 @@ _context(contextType: ContextType): Promise<dom.FrameExecutionContext>; | ||
} | ||
declare type Disposable<T> = { | ||
value: T; | ||
dispose: () => void; | ||
}; | ||
export declare class FrameTask { | ||
private _frame; | ||
private _failurePromise; | ||
private _requestMap; | ||
private _disposables; | ||
private _url; | ||
constructor(frame: Frame, options: types.TimeoutOptions, url?: string); | ||
raceAgainstFailures<T>(promise: Promise<T>): Promise<T>; | ||
request(id: string): network.Request | undefined; | ||
waitForSameDocumentNavigation(url?: types.URLMatch): Promise<void>; | ||
waitForSpecificDocument(expectedDocumentId: string): Promise<void>; | ||
waitForNewDocument(url?: types.URLMatch): Promise<string>; | ||
waitForLifecycle(waitUntil?: types.LifecycleEvent): Promise<void>; | ||
done(): void; | ||
} | ||
export {}; |
@@ -322,164 +322,39 @@ "use strict"; | ||
url = helper_1.helper.completeUserURL(url); | ||
const { timeout = this._page._timeoutSettings.navigationTimeout() } = options; | ||
const disposer = new Disposer(); | ||
const timeoutPromise = disposer.add(createTimeoutPromise(timeout)); | ||
const frameDestroyedPromise = this._createFrameDestroyedPromise(); | ||
const sameDocumentPromise = disposer.add(this._waitForSameDocumentNavigation()); | ||
const requestWatcher = disposer.add(this._trackDocumentRequests()); | ||
let navigateResult; | ||
const navigate = async () => { | ||
try { | ||
navigateResult = await this._page._delegate.navigateFrame(this, url, referer); | ||
} | ||
catch (error) { | ||
return error; | ||
} | ||
}; | ||
throwIfError(await Promise.race([ | ||
navigate(), | ||
timeoutPromise, | ||
frameDestroyedPromise, | ||
])); | ||
const promises = [timeoutPromise, frameDestroyedPromise]; | ||
if (navigateResult.newDocumentId) | ||
promises.push(disposer.add(this._waitForSpecificDocument(navigateResult.newDocumentId))); | ||
else | ||
promises.push(sameDocumentPromise); | ||
throwIfError(await Promise.race(promises)); | ||
const request = (navigateResult && navigateResult.newDocumentId) ? requestWatcher.get(navigateResult.newDocumentId) : null; | ||
const waitForLifecyclePromise = disposer.add(this._waitForLifecycle(options.waitUntil)); | ||
throwIfError(await Promise.race([timeoutPromise, frameDestroyedPromise, waitForLifecyclePromise])); | ||
disposer.dispose(); | ||
const frameTask = new FrameTask(this, options, url); | ||
const sameDocumentPromise = frameTask.waitForSameDocumentNavigation(); | ||
const navigateResult = await frameTask.raceAgainstFailures(this._page._delegate.navigateFrame(this, url, referer)).catch(e => { | ||
// Do not leave sameDocumentPromise unhandled. | ||
sameDocumentPromise.catch(e => { }); | ||
throw e; | ||
}); | ||
if (navigateResult.newDocumentId) { | ||
// Do not leave sameDocumentPromise unhandled. | ||
sameDocumentPromise.catch(e => { }); | ||
await frameTask.waitForSpecificDocument(navigateResult.newDocumentId); | ||
} | ||
else { | ||
await sameDocumentPromise; | ||
} | ||
const request = (navigateResult && navigateResult.newDocumentId) ? frameTask.request(navigateResult.newDocumentId) : null; | ||
await frameTask.waitForLifecycle(options.waitUntil); | ||
frameTask.done(); | ||
return request ? request._finalRequest().response() : null; | ||
function throwIfError(error) { | ||
if (!error) | ||
return; | ||
disposer.dispose(); | ||
const message = `While navigating to ${url}: ${error.message}`; | ||
if (error instanceof errors_1.TimeoutError) | ||
throw new errors_1.TimeoutError(message); | ||
throw new Error(message); | ||
} | ||
} | ||
async waitForNavigation(options = {}) { | ||
const disposer = new Disposer(); | ||
const requestWatcher = disposer.add(this._trackDocumentRequests()); | ||
const { timeout = this._page._timeoutSettings.navigationTimeout() } = options; | ||
const failurePromise = Promise.race([ | ||
this._createFrameDestroyedPromise(), | ||
disposer.add(createTimeoutPromise(timeout)), | ||
const frameTask = new FrameTask(this, options); | ||
let documentId; | ||
await Promise.race([ | ||
frameTask.waitForNewDocument(options.url).then(id => documentId = id), | ||
frameTask.waitForSameDocumentNavigation(options.url), | ||
]); | ||
let documentId = null; | ||
let error = await Promise.race([ | ||
failurePromise, | ||
disposer.add(this._waitForNewDocument(options.url)).then(result => { | ||
if (result.error) | ||
return result.error; | ||
documentId = result.documentId; | ||
}), | ||
disposer.add(this._waitForSameDocumentNavigation(options.url)), | ||
]); | ||
const request = requestWatcher.get(documentId); | ||
if (!error) { | ||
error = await Promise.race([ | ||
failurePromise, | ||
disposer.add(this._waitForLifecycle(options.waitUntil)), | ||
]); | ||
} | ||
disposer.dispose(); | ||
if (error) | ||
throw error; | ||
const request = documentId ? frameTask.request(documentId) : null; | ||
await frameTask.waitForLifecycle(options.waitUntil); | ||
frameTask.done(); | ||
return request ? request._finalRequest().response() : null; | ||
} | ||
async waitForLoadState(options = {}) { | ||
const { timeout = this._page._timeoutSettings.navigationTimeout() } = options; | ||
const disposer = new Disposer(); | ||
const error = await Promise.race([ | ||
this._createFrameDestroyedPromise(), | ||
disposer.add(createTimeoutPromise(timeout)), | ||
disposer.add(this._waitForLifecycle(options.waitUntil)), | ||
]); | ||
disposer.dispose(); | ||
if (error) | ||
throw error; | ||
const frameTask = new FrameTask(this, options); | ||
await frameTask.waitForLifecycle(options.waitUntil); | ||
frameTask.done(); | ||
} | ||
_waitForSpecificDocument(expectedDocumentId) { | ||
let resolve; | ||
const promise = new Promise(x => resolve = x); | ||
const watch = (documentId, error) => { | ||
if (documentId === expectedDocumentId) | ||
resolve(error); | ||
else if (!error) | ||
resolve(new Error('Navigation interrupted by another one')); | ||
}; | ||
const dispose = () => this._documentWatchers.delete(watch); | ||
this._documentWatchers.add(watch); | ||
return { value: promise, dispose }; | ||
} | ||
_waitForNewDocument(url) { | ||
let resolve; | ||
const promise = new Promise(x => resolve = x); | ||
const watch = (documentId, error) => { | ||
if (!error && !helper_1.helper.urlMatches(this.url(), url)) | ||
return; | ||
resolve({ error, documentId }); | ||
}; | ||
const dispose = () => this._documentWatchers.delete(watch); | ||
this._documentWatchers.add(watch); | ||
return { value: promise, dispose }; | ||
} | ||
_waitForSameDocumentNavigation(url) { | ||
let resolve; | ||
const promise = new Promise(x => resolve = x); | ||
const watch = () => { | ||
if (helper_1.helper.urlMatches(this.url(), url)) | ||
resolve(); | ||
}; | ||
const dispose = () => this._sameDocumentNavigationWatchers.delete(watch); | ||
this._sameDocumentNavigationWatchers.add(watch); | ||
return { value: promise, dispose }; | ||
} | ||
_waitForLifecycle(waitUntil = 'load') { | ||
let resolve; | ||
if (!types.kLifecycleEvents.has(waitUntil)) | ||
throw new Error(`Unsupported waitUntil option ${String(waitUntil)}`); | ||
const checkLifecycleComplete = () => { | ||
if (!checkLifecycleRecursively(this)) | ||
return; | ||
resolve(); | ||
}; | ||
const promise = new Promise(x => resolve = x); | ||
const dispose = () => this._page._frameManager._lifecycleWatchers.delete(checkLifecycleComplete); | ||
this._page._frameManager._lifecycleWatchers.add(checkLifecycleComplete); | ||
checkLifecycleComplete(); | ||
return { value: promise, dispose }; | ||
function checkLifecycleRecursively(frame) { | ||
if (!frame._firedLifecycleEvents.has(waitUntil)) | ||
return false; | ||
for (const child of frame.childFrames()) { | ||
if (!checkLifecycleRecursively(child)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
_trackDocumentRequests() { | ||
const requestMap = new Map(); | ||
const dispose = () => { | ||
this._requestWatchers.delete(onRequest); | ||
}; | ||
const onRequest = (request) => { | ||
if (!request._documentId || request.redirectedFrom()) | ||
return; | ||
requestMap.set(request._documentId, request); | ||
}; | ||
this._requestWatchers.add(onRequest); | ||
return { dispose, value: requestMap }; | ||
} | ||
_createFrameDestroyedPromise() { | ||
return Promise.race([ | ||
this._page._disconnectedPromise.then(() => new Error('Navigation failed because browser has disconnected!')), | ||
this._detachedPromise.then(() => new Error('Navigating frame was detached!')), | ||
]); | ||
} | ||
async frameElement() { | ||
@@ -881,31 +756,2 @@ return this._page._delegate.getFrameElement(this); | ||
} | ||
class Disposer { | ||
constructor() { | ||
this._disposes = []; | ||
} | ||
add({ value, dispose }) { | ||
this._disposes.push(dispose); | ||
return value; | ||
} | ||
dispose() { | ||
for (const dispose of this._disposes) | ||
dispose(); | ||
this._disposes = []; | ||
} | ||
} | ||
function createTimeoutPromise(timeout) { | ||
if (!timeout) | ||
return { value: new Promise(() => { }), dispose: () => void 0 }; | ||
let timer; | ||
const errorMessage = 'Navigation timeout of ' + timeout + ' ms exceeded'; | ||
const promise = new Promise(fulfill => timer = setTimeout(fulfill, timeout)) | ||
.then(() => new errors_1.TimeoutError(errorMessage)); | ||
const dispose = () => { | ||
clearTimeout(timer); | ||
}; | ||
return { | ||
value: promise, | ||
dispose | ||
}; | ||
} | ||
function selectorToString(selector, waitFor) { | ||
@@ -959,2 +805,128 @@ let label; | ||
} | ||
class FrameTask { | ||
constructor(frame, options, url) { | ||
this._requestMap = new Map(); | ||
this._disposables = []; | ||
this._frame = frame; | ||
this._url = url; | ||
// Process timeouts | ||
let timeoutPromise = new Promise(() => { }); | ||
const { timeout = frame._page._timeoutSettings.navigationTimeout() } = options; | ||
if (timeout) { | ||
const errorMessage = 'Navigation timeout of ' + timeout + ' ms exceeded'; | ||
let timer; | ||
timeoutPromise = new Promise(fulfill => timer = setTimeout(fulfill, timeout)) | ||
.then(() => { throw new errors_1.TimeoutError(errorMessage); }); | ||
this._disposables.push(() => clearTimeout(timer)); | ||
} | ||
// Process detached frames | ||
this._failurePromise = Promise.race([ | ||
timeoutPromise, | ||
this._frame._page._disconnectedPromise.then(() => { throw new Error('Navigation failed because browser has disconnected!'); }), | ||
this._frame._detachedPromise.then(() => { throw new Error('Navigating frame was detached!'); }), | ||
]); | ||
// Collect requests during the task. | ||
const watcher = (request) => { | ||
if (!request._documentId || request.redirectedFrom()) | ||
return; | ||
this._requestMap.set(request._documentId, request); | ||
}; | ||
this._disposables.push(() => this._frame._requestWatchers.delete(watcher)); | ||
this._frame._requestWatchers.add(watcher); | ||
} | ||
async raceAgainstFailures(promise) { | ||
let result; | ||
let error; | ||
await Promise.race([ | ||
this._failurePromise.catch(e => error = e), | ||
promise.then(r => result = r).catch(e => error = e) | ||
]); | ||
if (!error) | ||
return result; | ||
this.done(); | ||
if (this._url) | ||
error.message = error.message + ` while navigating to ${this._url}`; | ||
throw error; | ||
} | ||
request(id) { | ||
return this._requestMap.get(id); | ||
} | ||
async waitForSameDocumentNavigation(url) { | ||
let resolve; | ||
const promise = new Promise(x => resolve = x); | ||
const watch = () => { | ||
if (helper_1.helper.urlMatches(this._frame.url(), url)) | ||
resolve(); | ||
}; | ||
this._disposables.push(() => this._frame._sameDocumentNavigationWatchers.delete(watch)); | ||
this._frame._sameDocumentNavigationWatchers.add(watch); | ||
return this.raceAgainstFailures(promise); | ||
} | ||
async waitForSpecificDocument(expectedDocumentId) { | ||
let resolve; | ||
let reject; | ||
const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); | ||
const watch = (documentId, error) => { | ||
if (documentId === expectedDocumentId) { | ||
if (!error) | ||
resolve(); | ||
else | ||
reject(error); | ||
} | ||
else if (!error) { | ||
reject(new Error('Navigation interrupted by another one')); | ||
} | ||
}; | ||
this._disposables.push(() => this._frame._documentWatchers.delete(watch)); | ||
this._frame._documentWatchers.add(watch); | ||
await this.raceAgainstFailures(promise); | ||
} | ||
waitForNewDocument(url) { | ||
let resolve; | ||
let reject; | ||
const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); | ||
const watch = (documentId, error) => { | ||
if (!error && !helper_1.helper.urlMatches(this._frame.url(), url)) | ||
return; | ||
if (error) | ||
reject(error); | ||
else | ||
resolve(documentId); | ||
}; | ||
this._disposables.push(() => this._frame._documentWatchers.delete(watch)); | ||
this._frame._documentWatchers.add(watch); | ||
return this.raceAgainstFailures(promise); | ||
} | ||
waitForLifecycle(waitUntil = 'load') { | ||
let resolve; | ||
if (!types.kLifecycleEvents.has(waitUntil)) | ||
throw new Error(`Unsupported waitUntil option ${String(waitUntil)}`); | ||
const checkLifecycleComplete = () => { | ||
if (!checkLifecycleRecursively(this._frame)) | ||
return; | ||
resolve(); | ||
}; | ||
const promise = new Promise(x => resolve = x); | ||
this._disposables.push(() => this._frame._page._frameManager._lifecycleWatchers.delete(checkLifecycleComplete)); | ||
this._frame._page._frameManager._lifecycleWatchers.add(checkLifecycleComplete); | ||
checkLifecycleComplete(); | ||
return this.raceAgainstFailures(promise); | ||
function checkLifecycleRecursively(frame) { | ||
if (!frame._firedLifecycleEvents.has(waitUntil)) | ||
return false; | ||
for (const child of frame.childFrames()) { | ||
if (!checkLifecycleRecursively(child)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
done() { | ||
this._failurePromise.catch(e => { }); | ||
for (const disposable of this._disposables) | ||
disposable(); | ||
this._disposables = []; | ||
} | ||
} | ||
exports.FrameTask = FrameTask; | ||
//# sourceMappingURL=frames.js.map |
@@ -43,10 +43,10 @@ "use strict"; | ||
return page; | ||
const { dispose, value: waitForLifecycle } = page.mainFrame()._waitForLifecycle(lifecycle); | ||
const error = await Promise.race([ | ||
page.mainFrame()._createFrameDestroyedPromise(), | ||
waitForLifecycle, | ||
]); | ||
dispose(); | ||
if (error) | ||
try { | ||
const frameTask = new frames.FrameTask(page.mainFrame(), { timeout: 0 }); | ||
await frameTask.waitForLifecycle(lifecycle); | ||
frameTask.done(); | ||
} | ||
catch (error) { | ||
return error; | ||
} | ||
return page; | ||
@@ -53,0 +53,0 @@ } |
@@ -17,32 +17,14 @@ /** | ||
*/ | ||
declare type ParamsGetter = (platform: string, revision: string) => { | ||
downloadUrl: string; | ||
executablePath: string; | ||
}; | ||
export declare type OnProgressCallback = (downloadedBytes: number, totalBytes: number) => void; | ||
export declare class BrowserFetcher { | ||
private _downloadsFolder; | ||
private _platform; | ||
private _preferredRevision; | ||
private _params; | ||
constructor(downloadsFolder: string, platform: string, preferredRevision: string, params: ParamsGetter); | ||
canDownload(revision?: string): Promise<boolean>; | ||
download(revision?: string, progressCallback?: OnProgressCallback): Promise<BrowserFetcherRevisionInfo>; | ||
localRevisions(): Promise<string[]>; | ||
remove(revision?: string): Promise<void>; | ||
revisionInfo(revision?: string): BrowserFetcherRevisionInfo; | ||
_getFolderPath(revision: string): string; | ||
} | ||
export declare type BrowserFetcherOptions = { | ||
platform?: string; | ||
path?: string; | ||
export declare type BrowserName = ('chromium' | 'webkit' | 'firefox'); | ||
export declare type BrowserPlatform = ('win32' | 'win64' | 'mac10.14' | 'mac10.15' | 'linux'); | ||
export declare type DownloadOptions = { | ||
browser: BrowserName; | ||
revision: string; | ||
downloadPath: string; | ||
platform?: BrowserPlatform; | ||
host?: string; | ||
progress?: OnProgressCallback; | ||
}; | ||
export declare type BrowserFetcherRevisionInfo = { | ||
folderPath: string; | ||
executablePath: string; | ||
url: string; | ||
local: boolean; | ||
revision: string; | ||
}; | ||
export {}; | ||
export declare function downloadBrowser(options: DownloadOptions): Promise<string>; | ||
export declare function canDownload(options: DownloadOptions): Promise<boolean>; |
@@ -21,90 +21,120 @@ "use strict"; | ||
const fs = require("fs"); | ||
const os = require("os"); | ||
const util = require("util"); | ||
const child_process_1 = require("child_process"); | ||
const ProxyAgent = require("https-proxy-agent"); | ||
const path = require("path"); | ||
const platform = require("../platform"); | ||
const proxy_from_env_1 = require("proxy-from-env"); | ||
const removeRecursive = require("rimraf"); | ||
const URL = require("url"); | ||
const helper_1 = require("../helper"); | ||
const readdirAsync = platform.promisify(fs.readdir.bind(fs)); | ||
const mkdirAsync = platform.promisify(fs.mkdir.bind(fs)); | ||
const platform = require("../platform"); | ||
const unlinkAsync = platform.promisify(fs.unlink.bind(fs)); | ||
const chmodAsync = platform.promisify(fs.chmod.bind(fs)); | ||
function existsAsync(filePath) { | ||
let fulfill; | ||
const promise = new Promise(x => fulfill = x); | ||
fs.access(filePath, err => fulfill(!err)); | ||
return promise; | ||
const existsAsync = (path) => new Promise(resolve => fs.stat(path, err => resolve(!err))); | ||
const DEFAULT_DOWNLOAD_HOSTS = { | ||
chromium: 'https://storage.googleapis.com', | ||
firefox: 'https://playwright.azureedge.net', | ||
webkit: 'https://playwright.azureedge.net', | ||
}; | ||
const DOWNLOAD_URLS = { | ||
chromium: { | ||
'linux': '%s/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip', | ||
'mac10.14': '%s/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', | ||
'mac10.15': '%s/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', | ||
'win32': '%s/chromium-browser-snapshots/Win/%d/chrome-win.zip', | ||
'win64': '%s/chromium-browser-snapshots/Win_x64/%d/chrome-win.zip', | ||
}, | ||
firefox: { | ||
'linux': '%s/builds/firefox/%s/firefox-linux.zip', | ||
'mac10.14': '%s/builds/firefox/%s/firefox-mac.zip', | ||
'mac10.15': '%s/builds/firefox/%s/firefox-mac.zip', | ||
'win32': '%s/builds/firefox/%s/firefox-win32.zip', | ||
'win64': '%s/builds/firefox/%s/firefox-win64.zip', | ||
}, | ||
webkit: { | ||
'linux': '%s/builds/webkit/%s/minibrowser-gtk-wpe.zip', | ||
'mac10.14': '%s/builds/webkit/%s/minibrowser-mac-10.14.zip', | ||
'mac10.15': '%s/builds/webkit/%s/minibrowser-mac-10.15.zip', | ||
'win32': '%s/builds/webkit/%s/minibrowser-win64.zip', | ||
'win64': '%s/builds/webkit/%s/minibrowser-win64.zip', | ||
}, | ||
}; | ||
const RELATIVE_EXECUTABLE_PATHS = { | ||
chromium: { | ||
'linux': ['chrome-linux', 'chrome'], | ||
'mac10.14': ['chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'], | ||
'mac10.15': ['chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'], | ||
'win32': ['chrome-win', 'chrome.exe'], | ||
'win64': ['chrome-win', 'chrome.exe'], | ||
}, | ||
firefox: { | ||
'linux': ['firefox', 'firefox'], | ||
'mac10.14': ['firefox', 'Nightly.app', 'Contents', 'MacOS', 'firefox'], | ||
'mac10.15': ['firefox', 'Nightly.app', 'Contents', 'MacOS', 'firefox'], | ||
'win32': ['firefox', 'firefox.exe'], | ||
'win64': ['firefox', 'firefox.exe'], | ||
}, | ||
webkit: { | ||
'linux': ['pw_run.sh'], | ||
'mac10.14': ['pw_run.sh'], | ||
'mac10.15': ['pw_run.sh'], | ||
'win32': ['MiniBrowser.exe'], | ||
'win64': ['MiniBrowser.exe'], | ||
}, | ||
}; | ||
const CURRENT_HOST_PLATFORM = (() => { | ||
const platform = os.platform(); | ||
if (platform === 'darwin') { | ||
const macVersion = child_process_1.execSync('sw_vers -productVersion').toString('utf8').trim().split('.').slice(0, 2).join('.'); | ||
return `mac${macVersion}`; | ||
} | ||
if (platform === 'linux') | ||
return 'linux'; | ||
if (platform === 'win32') | ||
return os.arch() === 'x64' ? 'win64' : 'win32'; | ||
return platform; | ||
})(); | ||
function revisionURL(options) { | ||
const { browser, revision, platform = CURRENT_HOST_PLATFORM, host = DEFAULT_DOWNLOAD_HOSTS[browser], } = options; | ||
helper_1.assert(revision, `'revision' must be specified`); | ||
helper_1.assert(DOWNLOAD_URLS[browser], 'Unsupported browser: ' + browser); | ||
const urlTemplate = DOWNLOAD_URLS[browser][platform]; | ||
helper_1.assert(urlTemplate, `ERROR: Playwright does not support ${browser} on ${platform}`); | ||
return util.format(urlTemplate, host, revision); | ||
} | ||
class BrowserFetcher { | ||
constructor(downloadsFolder, platform, preferredRevision, params) { | ||
this._downloadsFolder = downloadsFolder; | ||
this._platform = platform; | ||
this._preferredRevision = preferredRevision; | ||
this._params = params; | ||
async function downloadBrowser(options) { | ||
const { browser, revision, downloadPath, platform = CURRENT_HOST_PLATFORM, progress, } = options; | ||
helper_1.assert(downloadPath, '`downloadPath` must be provided'); | ||
const url = revisionURL(options); | ||
const zipPath = path.join(os.tmpdir(), `playwright-download-${browser}-${platform}-${revision}.zip`); | ||
if (await existsAsync(downloadPath)) | ||
throw new Error('ERROR: downloadPath folder already exists!'); | ||
try { | ||
await downloadFile(url, zipPath, progress); | ||
// await mkdirAsync(downloadPath, {recursive: true}); | ||
await extractZip(zipPath, downloadPath); | ||
} | ||
canDownload(revision = this._preferredRevision) { | ||
const url = this._params(this._platform, revision).downloadUrl; | ||
let resolve = () => { }; | ||
const promise = new Promise(x => resolve = x); | ||
const request = httpRequest(url, 'HEAD', response => { | ||
resolve(response.statusCode === 200); | ||
}); | ||
request.on('error', (error) => { | ||
console.error(error); | ||
resolve(false); | ||
}); | ||
return promise; | ||
finally { | ||
if (await existsAsync(zipPath)) | ||
await unlinkAsync(zipPath); | ||
} | ||
async download(revision = this._preferredRevision, progressCallback) { | ||
const url = this._params(this._platform, revision).downloadUrl; | ||
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); | ||
} | ||
const revisionInfo = this.revisionInfo(revision); | ||
if (revisionInfo) | ||
await chmodAsync(revisionInfo.executablePath, 0o755); | ||
return revisionInfo; | ||
} | ||
async localRevisions() { | ||
if (!await existsAsync(this._downloadsFolder)) | ||
return []; | ||
const fileNames = await readdirAsync(this._downloadsFolder); | ||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry.revision); | ||
} | ||
async remove(revision = this._preferredRevision) { | ||
const folderPath = this._getFolderPath(revision); | ||
helper_1.assert(await existsAsync(folderPath), `Failed to remove: revision ${revision} is not downloaded`); | ||
await new Promise(fulfill => removeRecursive(folderPath, fulfill)); | ||
} | ||
revisionInfo(revision = this._preferredRevision) { | ||
const folderPath = this._getFolderPath(revision); | ||
const params = this._params(this._platform, revision); | ||
const local = fs.existsSync(folderPath); | ||
return { revision, executablePath: path.join(folderPath, params.executablePath), folderPath, local, url: params.downloadUrl }; | ||
} | ||
_getFolderPath(revision) { | ||
return path.join(this._downloadsFolder, this._platform + '-' + revision); | ||
} | ||
const executablePath = path.join(downloadPath, ...RELATIVE_EXECUTABLE_PATHS[browser][platform]); | ||
await chmodAsync(executablePath, 0o755); | ||
return executablePath; | ||
} | ||
exports.BrowserFetcher = BrowserFetcher; | ||
function parseFolderPath(folderPath) { | ||
const name = path.basename(folderPath); | ||
const splits = name.split('-'); | ||
if (splits.length !== 2) | ||
return null; | ||
const [platform, revision] = splits; | ||
return { platform, revision }; | ||
exports.downloadBrowser = downloadBrowser; | ||
async function canDownload(options) { | ||
const url = revisionURL(options); | ||
let resolve = () => { }; | ||
const promise = new Promise(x => resolve = x); | ||
const request = httpRequest(url, 'HEAD', response => { | ||
resolve(response.statusCode === 200); | ||
}); | ||
request.on('error', (error) => { | ||
console.error(error); | ||
resolve(false); | ||
}); | ||
return promise; | ||
} | ||
exports.canDownload = canDownload; | ||
function downloadFile(url, destinationPath, progressCallback) { | ||
@@ -111,0 +141,0 @@ let fulfill = () => { }; |
@@ -19,3 +19,2 @@ /** | ||
import { BrowserServer } from './browserServer'; | ||
import { OnProgressCallback } from './browserFetcher'; | ||
export declare type BrowserArgOptions = { | ||
@@ -53,3 +52,2 @@ headless?: boolean; | ||
connect(options: ConnectOptions): Promise<Browser>; | ||
downloadBrowserIfNeeded(progress?: OnProgressCallback): Promise<void>; | ||
} |
@@ -17,3 +17,2 @@ /** | ||
*/ | ||
import { BrowserFetcher, OnProgressCallback, BrowserFetcherOptions } from '../server/browserFetcher'; | ||
import { CRBrowser } from '../chromium/crBrowser'; | ||
@@ -25,6 +24,4 @@ import { LaunchOptions, BrowserType } from './browserType'; | ||
export declare class Chromium implements BrowserType { | ||
private _downloadPath; | ||
private _downloadHost; | ||
readonly _revision: string; | ||
constructor(downloadPath: string, downloadHost: (string | undefined), preferredRevision: string); | ||
private _executablePath; | ||
executablePath(): string; | ||
name(): string; | ||
@@ -40,10 +37,3 @@ launch(options?: LaunchOptions & { | ||
connect(options: ConnectOptions): Promise<CRBrowser>; | ||
executablePath(): string; | ||
private _defaultArgs; | ||
downloadBrowserIfNeeded(onProgress?: OnProgressCallback): Promise<void>; | ||
_createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher; | ||
_resolveExecutablePath(): { | ||
executablePath: string; | ||
missingText: string | null; | ||
}; | ||
} |
@@ -22,4 +22,2 @@ "use strict"; | ||
const path = require("path"); | ||
const util = require("util"); | ||
const browserFetcher_1 = require("../server/browserFetcher"); | ||
const helper_1 = require("../helper"); | ||
@@ -35,6 +33,6 @@ const crBrowser_1 = require("../chromium/crBrowser"); | ||
class Chromium { | ||
constructor(downloadPath, downloadHost, preferredRevision) { | ||
this._downloadPath = downloadPath; | ||
this._downloadHost = downloadHost || 'https://storage.googleapis.com'; | ||
this._revision = preferredRevision; | ||
executablePath() { | ||
if (!this._executablePath) | ||
throw new Error('No executable path!'); | ||
return this._executablePath; | ||
} | ||
@@ -76,9 +74,5 @@ name() { | ||
chromeArguments.push(...args); | ||
let chromeExecutable = executablePath; | ||
if (!executablePath) { | ||
const { missingText, executablePath } = this._resolveExecutablePath(); | ||
if (missingText) | ||
throw new Error(missingText); | ||
chromeExecutable = executablePath; | ||
} | ||
const chromeExecutable = executablePath || this._executablePath; | ||
if (!chromeExecutable) | ||
throw new Error(`No executable path is specified. Pass "executablePath" option directly.`); | ||
let browserServer = undefined; | ||
@@ -113,3 +107,3 @@ const { launchedProcess, gracefullyClose } = await processLauncher_1.launchProcess({ | ||
if (launchType === 'server') { | ||
const timeoutError = new errors_1.TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chromium! The only Chromium revision guaranteed to work is r${this._revision}`); | ||
const timeoutError = new errors_1.TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chromium!`); | ||
const match = await processLauncher_1.waitForLine(launchedProcess, launchedProcess.stderr, /^DevTools listening on (ws:\/\/.*)$/, timeout, timeoutError); | ||
@@ -132,5 +126,2 @@ browserWSEndpoint = match[1]; | ||
} | ||
executablePath() { | ||
return this._resolveExecutablePath().executablePath; | ||
} | ||
_defaultArgs(options = {}, launchType, userDataDir, port) { | ||
@@ -163,64 +154,2 @@ const { devtools = false, headless = !devtools, args = [], } = options; | ||
} | ||
async downloadBrowserIfNeeded(onProgress) { | ||
const fetcher = this._createBrowserFetcher(); | ||
const revisionInfo = fetcher.revisionInfo(); | ||
// Do nothing if the revision is already downloaded. | ||
if (revisionInfo.local) | ||
return; | ||
await fetcher.download(revisionInfo.revision, onProgress); | ||
} | ||
_createBrowserFetcher(options = {}) { | ||
const downloadURLs = { | ||
linux: '%s/chromium-browser-snapshots/Linux_x64/%d/%s.zip', | ||
mac: '%s/chromium-browser-snapshots/Mac/%d/%s.zip', | ||
win32: '%s/chromium-browser-snapshots/Win/%d/%s.zip', | ||
win64: '%s/chromium-browser-snapshots/Win_x64/%d/%s.zip', | ||
}; | ||
const defaultOptions = { | ||
path: path.join(this._downloadPath, '.local-chromium'), | ||
host: this._downloadHost, | ||
platform: (() => { | ||
const platform = os.platform(); | ||
if (platform === 'darwin') | ||
return 'mac'; | ||
if (platform === 'linux') | ||
return 'linux'; | ||
if (platform === 'win32') | ||
return os.arch() === 'x64' ? 'win64' : 'win32'; | ||
return platform; | ||
})() | ||
}; | ||
options = { | ||
...defaultOptions, | ||
...options, | ||
}; | ||
helper_1.assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform); | ||
return new browserFetcher_1.BrowserFetcher(options.path, options.platform, this._revision, (platform, revision) => { | ||
let archiveName = ''; | ||
let executablePath = ''; | ||
if (platform === 'linux') { | ||
archiveName = 'chrome-linux'; | ||
executablePath = path.join(archiveName, 'chrome'); | ||
} | ||
else if (platform === 'mac') { | ||
archiveName = 'chrome-mac'; | ||
executablePath = path.join(archiveName, 'Chromium.app', 'Contents', 'MacOS', 'Chromium'); | ||
} | ||
else if (platform === 'win32' || platform === 'win64') { | ||
// Windows archive name changed at r591479. | ||
archiveName = parseInt(revision, 10) > 591479 ? 'chrome-win' : 'chrome-win32'; | ||
executablePath = path.join(archiveName, 'chrome.exe'); | ||
} | ||
return { | ||
downloadUrl: util.format(downloadURLs[platform], options.host, revision, archiveName), | ||
executablePath | ||
}; | ||
}); | ||
} | ||
_resolveExecutablePath() { | ||
const browserFetcher = this._createBrowserFetcher(); | ||
const revisionInfo = browserFetcher.revisionInfo(); | ||
const missingText = !revisionInfo.local ? `Chromium revision is not downloaded. Run "npm install"` : null; | ||
return { executablePath: revisionInfo.executablePath, missingText }; | ||
} | ||
} | ||
@@ -227,0 +156,0 @@ exports.Chromium = Chromium; |
@@ -20,11 +20,7 @@ /** | ||
import { FFBrowser } from '../firefox/ffBrowser'; | ||
import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback } from './browserFetcher'; | ||
import { BrowserServer } from './browserServer'; | ||
import { BrowserType, LaunchOptions } from './browserType'; | ||
export declare class Firefox implements BrowserType { | ||
private _downloadPath; | ||
private _downloadHost; | ||
readonly _revision: string; | ||
constructor(downloadPath: string, downloadHost: (string | undefined), preferredRevision: string); | ||
downloadBrowserIfNeeded(onProgress?: OnProgressCallback): Promise<void>; | ||
private _executablePath; | ||
executablePath(): string; | ||
name(): string; | ||
@@ -40,9 +36,3 @@ launch(options?: LaunchOptions & { | ||
connect(options: ConnectOptions): Promise<FFBrowser>; | ||
executablePath(): string; | ||
private _defaultArgs; | ||
_createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher; | ||
_resolveExecutablePath(): { | ||
executablePath: string; | ||
missingText: string | null; | ||
}; | ||
} |
@@ -22,3 +22,2 @@ "use strict"; | ||
const path = require("path"); | ||
const util = require("util"); | ||
const errors_1 = require("../errors"); | ||
@@ -30,3 +29,2 @@ const events_1 = require("../events"); | ||
const platform = require("../platform"); | ||
const browserFetcher_1 = require("./browserFetcher"); | ||
const browserServer_1 = require("./browserServer"); | ||
@@ -36,15 +34,7 @@ const processLauncher_1 = require("./processLauncher"); | ||
class Firefox { | ||
constructor(downloadPath, downloadHost, preferredRevision) { | ||
this._downloadPath = downloadPath; | ||
this._downloadHost = downloadHost || 'https://playwright.azureedge.net'; | ||
this._revision = preferredRevision; | ||
executablePath() { | ||
if (!this._executablePath) | ||
throw new Error('No executable path!'); | ||
return this._executablePath; | ||
} | ||
async downloadBrowserIfNeeded(onProgress) { | ||
const fetcher = this._createBrowserFetcher(); | ||
const revisionInfo = fetcher.revisionInfo(); | ||
// Do nothing if the revision is already downloaded. | ||
if (revisionInfo.local) | ||
return; | ||
await fetcher.download(revisionInfo.revision, onProgress); | ||
} | ||
name() { | ||
@@ -94,9 +84,5 @@ return 'firefox'; | ||
firefoxArguments.push(...args); | ||
let firefoxExecutable = executablePath; | ||
if (!firefoxExecutable) { | ||
const { missingText, executablePath } = this._resolveExecutablePath(); | ||
if (missingText) | ||
throw new Error(missingText); | ||
firefoxExecutable = executablePath; | ||
} | ||
const firefoxExecutable = executablePath || this._executablePath; | ||
if (!firefoxExecutable) | ||
throw new Error(`No executable path is specified. Pass "executablePath" option directly.`); | ||
let browserServer = undefined; | ||
@@ -143,5 +129,2 @@ const { launchedProcess, gracefullyClose } = await processLauncher_1.launchProcess({ | ||
} | ||
executablePath() { | ||
return this._resolveExecutablePath().executablePath; | ||
} | ||
_defaultArgs(options = {}, launchType, userDataDir, port) { | ||
@@ -173,50 +156,4 @@ const { devtools = false, headless = !devtools, args = [], } = options; | ||
} | ||
_createBrowserFetcher(options = {}) { | ||
const downloadURLs = { | ||
linux: '%s/builds/firefox/%s/firefox-linux.zip', | ||
mac: '%s/builds/firefox/%s/firefox-mac.zip', | ||
win32: '%s/builds/firefox/%s/firefox-win32.zip', | ||
win64: '%s/builds/firefox/%s/firefox-win64.zip', | ||
}; | ||
const defaultOptions = { | ||
path: path.join(this._downloadPath, '.local-firefox'), | ||
host: this._downloadHost, | ||
platform: (() => { | ||
const platform = os.platform(); | ||
if (platform === 'darwin') | ||
return 'mac'; | ||
if (platform === 'linux') | ||
return 'linux'; | ||
if (platform === 'win32') | ||
return os.arch() === 'x64' ? 'win64' : 'win32'; | ||
return platform; | ||
})() | ||
}; | ||
options = { | ||
...defaultOptions, | ||
...options, | ||
}; | ||
helper_1.assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform); | ||
return new browserFetcher_1.BrowserFetcher(options.path, options.platform, this._revision, (platform, revision) => { | ||
let executablePath = ''; | ||
if (platform === 'linux') | ||
executablePath = path.join('firefox', 'firefox'); | ||
else if (platform === 'mac') | ||
executablePath = path.join('firefox', 'Nightly.app', 'Contents', 'MacOS', 'firefox'); | ||
else if (platform === 'win32' || platform === 'win64') | ||
executablePath = path.join('firefox', 'firefox.exe'); | ||
return { | ||
downloadUrl: util.format(downloadURLs[platform], options.host, revision), | ||
executablePath | ||
}; | ||
}); | ||
} | ||
_resolveExecutablePath() { | ||
const browserFetcher = this._createBrowserFetcher(); | ||
const revisionInfo = browserFetcher.revisionInfo(); | ||
const missingText = !revisionInfo.local ? `Firefox revision is not downloaded. Run "npm install"` : null; | ||
return { executablePath: revisionInfo.executablePath, missingText }; | ||
} | ||
} | ||
exports.Firefox = Firefox; | ||
//# sourceMappingURL=firefox.js.map |
@@ -23,5 +23,3 @@ /** | ||
declare type PlaywrightOptions = { | ||
downloadPath: string; | ||
browsers: Array<('firefox' | 'webkit' | 'chromium')>; | ||
respectEnvironmentVariables: boolean; | ||
}; | ||
@@ -28,0 +26,0 @@ export declare class Playwright { |
@@ -25,3 +25,2 @@ "use strict"; | ||
const firefox_1 = require("./firefox"); | ||
const packageJSON = require('../../package.json'); | ||
for (const className in api) { | ||
@@ -34,21 +33,14 @@ if (typeof api[className] === 'function') | ||
this.selectors = api.Selectors._instance(); | ||
const { downloadPath, browsers, respectEnvironmentVariables, } = options; | ||
const { browsers, } = options; | ||
this.devices = deviceDescriptors_1.DeviceDescriptors; | ||
this.errors = { TimeoutError: errors_1.TimeoutError }; | ||
const downloadHost = respectEnvironmentVariables ? getFromENV('PLAYWRIGHT_DOWNLOAD_HOST') : undefined; | ||
if (browsers.includes('chromium')) | ||
this.chromium = new chromium_1.Chromium(downloadPath, downloadHost, packageJSON.playwright.chromium_revision); | ||
this.chromium = new chromium_1.Chromium(); | ||
if (browsers.includes('webkit')) | ||
this.webkit = new webkit_1.WebKit(downloadPath, downloadHost, packageJSON.playwright.webkit_revision); | ||
this.webkit = new webkit_1.WebKit(); | ||
if (browsers.includes('firefox')) | ||
this.firefox = new firefox_1.Firefox(downloadPath, downloadHost, packageJSON.playwright.firefox_revision); | ||
this.firefox = new firefox_1.Firefox(); | ||
} | ||
} | ||
exports.Playwright = Playwright; | ||
function getFromENV(name) { | ||
let value = process.env[name]; | ||
value = value || process.env[`npm_config_${name.toLowerCase()}`]; | ||
value = value || process.env[`npm_package_config_${name.toLowerCase()}`]; | ||
return value; | ||
} | ||
//# sourceMappingURL=playwright.js.map |
@@ -17,3 +17,2 @@ /** | ||
*/ | ||
import { BrowserFetcher, OnProgressCallback, BrowserFetcherOptions } from './browserFetcher'; | ||
import { WKBrowser } from '../webkit/wkBrowser'; | ||
@@ -25,8 +24,5 @@ import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; | ||
export declare class WebKit implements BrowserType { | ||
private _downloadPath; | ||
private _downloadHost; | ||
readonly _revision: string; | ||
constructor(downloadPath: string, downloadHost: (string | undefined), preferredRevision: string); | ||
private _executablePath; | ||
executablePath(): string; | ||
name(): string; | ||
downloadBrowserIfNeeded(onProgress?: OnProgressCallback): Promise<void>; | ||
launch(options?: LaunchOptions & { | ||
@@ -41,9 +37,3 @@ slowMo?: number; | ||
connect(options: ConnectOptions): Promise<WKBrowser>; | ||
executablePath(): string; | ||
_defaultArgs(options: BrowserArgOptions | undefined, launchType: LaunchType, userDataDir: string, port: number): string[]; | ||
_createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher; | ||
_resolveExecutablePath(): { | ||
executablePath: string; | ||
missingText: string | null; | ||
}; | ||
} |
@@ -19,5 +19,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const browserFetcher_1 = require("./browserFetcher"); | ||
const wkBrowser_1 = require("../webkit/wkBrowser"); | ||
const child_process_1 = require("child_process"); | ||
const pipeTransport_1 = require("./pipeTransport"); | ||
@@ -28,3 +26,2 @@ const processLauncher_1 = require("./processLauncher"); | ||
const platform = require("../platform"); | ||
const util = require("util"); | ||
const os = require("os"); | ||
@@ -37,6 +34,6 @@ const helper_1 = require("../helper"); | ||
class WebKit { | ||
constructor(downloadPath, downloadHost, preferredRevision) { | ||
this._downloadPath = downloadPath; | ||
this._downloadHost = downloadHost || 'https://playwright.azureedge.net'; | ||
this._revision = preferredRevision; | ||
executablePath() { | ||
if (!this._executablePath) | ||
throw new Error('No executable path!'); | ||
return this._executablePath; | ||
} | ||
@@ -46,10 +43,2 @@ name() { | ||
} | ||
async downloadBrowserIfNeeded(onProgress) { | ||
const fetcher = this._createBrowserFetcher(); | ||
const revisionInfo = fetcher.revisionInfo(); | ||
// Do nothing if the revision is already downloaded. | ||
if (revisionInfo.local) | ||
return; | ||
await fetcher.download(revisionInfo.revision, onProgress); | ||
} | ||
async launch(options) { | ||
@@ -87,9 +76,5 @@ if (options && options.userDataDir) | ||
webkitArguments.push(...args); | ||
let webkitExecutable = executablePath; | ||
if (!executablePath) { | ||
const { missingText, executablePath } = this._resolveExecutablePath(); | ||
if (missingText) | ||
throw new Error(missingText); | ||
webkitExecutable = executablePath; | ||
} | ||
const webkitExecutable = executablePath || this._executablePath; | ||
if (!webkitExecutable) | ||
throw new Error(`No executable path is specified.`); | ||
let transport = undefined; | ||
@@ -133,5 +118,2 @@ let browserServer = undefined; | ||
} | ||
executablePath() { | ||
return this._resolveExecutablePath().executablePath; | ||
} | ||
_defaultArgs(options = {}, launchType, userDataDir, port) { | ||
@@ -156,42 +138,2 @@ const { devtools = false, headless = !devtools, args = [], } = options; | ||
} | ||
_createBrowserFetcher(options) { | ||
const downloadURLs = { | ||
linux: '%s/builds/webkit/%s/minibrowser-gtk-wpe.zip', | ||
mac: '%s/builds/webkit/%s/minibrowser-mac-%s.zip', | ||
win64: '%s/builds/webkit/%s/minibrowser-win64.zip', | ||
}; | ||
const defaultOptions = { | ||
path: path.join(this._downloadPath, '.local-webkit'), | ||
host: this._downloadHost, | ||
platform: (() => { | ||
const platform = os.platform(); | ||
if (platform === 'darwin') | ||
return 'mac'; | ||
if (platform === 'linux') | ||
return 'linux'; | ||
if (platform === 'win32') | ||
return 'win64'; | ||
return platform; | ||
})() | ||
}; | ||
options = { | ||
...defaultOptions, | ||
...options, | ||
}; | ||
helper_1.assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform); | ||
return new browserFetcher_1.BrowserFetcher(options.path, options.platform, this._revision, (platform, revision) => { | ||
return { | ||
downloadUrl: (platform === 'mac') ? | ||
util.format(downloadURLs[platform], options.host, revision, getMacVersion()) : | ||
util.format(downloadURLs[platform], options.host, revision), | ||
executablePath: platform.startsWith('win') ? 'MiniBrowser.exe' : 'pw_run.sh', | ||
}; | ||
}); | ||
} | ||
_resolveExecutablePath() { | ||
const browserFetcher = this._createBrowserFetcher(); | ||
const revisionInfo = browserFetcher.revisionInfo(); | ||
const missingText = !revisionInfo.local ? `WebKit revision is not downloaded. Run "npm install"` : null; | ||
return { executablePath: revisionInfo.executablePath, missingText }; | ||
} | ||
} | ||
@@ -201,11 +143,2 @@ exports.WebKit = WebKit; | ||
const WEBKIT_PROFILE_PATH = path.join(os.tmpdir(), 'playwright_dev_profile-'); | ||
let cachedMacVersion = undefined; | ||
function getMacVersion() { | ||
if (!cachedMacVersion) { | ||
const [major, minor] = child_process_1.execSync('sw_vers -productVersion').toString('utf8').trim().split('.'); | ||
helper_1.assert(+major === 10 && +minor >= 14, 'Error: unsupported macOS version, macOS 10.14 and newer are supported'); | ||
cachedMacVersion = major + '.' + minor; | ||
} | ||
return cachedMacVersion; | ||
} | ||
class SequenceNumberMixer { | ||
@@ -212,0 +145,0 @@ constructor() { |
{ | ||
"name": "playwright-core", | ||
"version": "0.11.1-next.1584577781703", | ||
"version": "0.11.1-next.1584644386005", | ||
"description": "A high-level API to automate web browsers", | ||
@@ -5,0 +5,0 @@ "repository": "github:Microsoft/playwright", |
Sorry, the diff of this file is too big to display
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2438284
57088
15