diff --git a/.gitignore b/.gitignore index 58a669a876..fc8ae87ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ .local-browsers/ /.dev_profile* .DS_Store -.downloaded-browsers.json *.swp *.pyc .vscode diff --git a/docs/api.md b/docs/api.md index 4d93fa38c8..a71a038e46 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3980,10 +3980,14 @@ Playwright looks for certain [environment variables](https://en.wikipedia.org/wi If Playwright doesn't find them in the environment, a lowercased variant of these variables will be used from the [npm config](https://docs.npmjs.com/cli/config). - `PLAYWRIGHT_DOWNLOAD_HOST` - overwrite URL prefix that is used to download browsers. Note: this includes protocol and might even include path prefix. By default, Playwright uses `https://storage.googleapis.com` to download Chromium and `https://playwright.azureedge.net` to download Webkit & Firefox. -- `PLAYWRIGHT_GLOBAL_INSTALL` - install Playwright browsers in a single global location. Locations are different on different platforms: - * MacOS: `$HOME/Library/Caches/playwright-nodejs` - * Linux: `$HOME/.cache/playwright-nodejs` - * Windows: `$HOME/AppData/Local/playwright-nodejs/Cache` +- `PLAYWRIGHT_BROWSERS_PATH` - specify a shared folder that playwright will use to download browsers and to look for browsers when launching browser instances. + +```sh +# Install browsers to the shared location. +$ PLAYWRIGHT_BROWSERS_PATH=$HOME/playwright-browsers npm install playwright +# Use shared location to find browsers. +$ PLAYWRIGHT_BROWSERS_PATH=$HOME/playwright-browsers node playwright-script.js +``` ### Working with selectors diff --git a/download-browser.js b/download-browser.js index 5c4c79ac7a..1a0679e71e 100644 --- a/download-browser.js +++ b/download-browser.js @@ -18,22 +18,40 @@ const path = require('path'); const browserFetcher = require('./lib/server/browserFetcher.js'); const packageJSON = require('./package.json'); -const FALSY_VALUES = ['0', 'false']; +function localDownloadOptions(browserName) { + const revision = packageJSON.playwright[`${browserName}_revision`]; + const downloadPath = path.join(__dirname, '.local-browsers', `${browserName}-${revision}`); + return { + browser: browserName, + progressBarBrowserName: `${browserName} r${revision}`, + revision, + downloadPath, + executablePath: browserFetcher.executablePath({browser: browserName, downloadPath}), + }; +} -async function downloadBrowserWithProgressBar(downloadPath, browser, respectGlobalInstall = false) { - const PLAYWRIGHT_GLOBAL_INSTALL = respectGlobalInstall ? getFromENV('PLAYWRIGHT_GLOBAL_INSTALL') : false; - if (!!PLAYWRIGHT_GLOBAL_INSTALL && !FALSY_VALUES.includes(PLAYWRIGHT_GLOBAL_INSTALL.toLowerCase().trim())) { - const envPaths = require('env-paths'); - const appPaths = envPaths('playwright'); - downloadPath = path.join(appPaths.cache, `playwright-${packageJSON.version}-${browser}`); - } +function downloadOptionsFromENV(packagePath, browserName) { + const browsersPath = getFromENV('PLAYWRIGHT_BROWSERS_PATH'); + const downloadPath = browsersPath ? + path.join(browsersPath, 'v' + packageJSON.version, browserName) : + path.join(packagePath, '.local-browsers', browserName); + return { + downloadPath, + progressBarBrowserName: `${browserName} for playwright v${packageJSON.version}`, + revision: packageJSON.playwright[`${browserName}_revision`], + browser: browserName, + host: getFromENV('PLAYWRIGHT_DOWNLOAD_HOST'), + executablePath: browserFetcher.executablePath({browser: browserName, downloadPath}), + }; +} + +async function downloadBrowserWithProgressBar(options) { let progressBar = null; let lastDownloadedBytes = 0; - const revision = packageJSON.playwright[`${browser}_revision`]; function progress(downloadedBytes, totalBytes) { if (!progressBar) { const ProgressBar = require('progress'); - progressBar = new ProgressBar(`Downloading ${browser} r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, { + progressBar = new ProgressBar(`Downloading ${options.progressBarBrowserName} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `, { complete: '=', incomplete: ' ', width: 20, @@ -44,15 +62,8 @@ async function downloadBrowserWithProgressBar(downloadPath, browser, respectGlob lastDownloadedBytes = downloadedBytes; progressBar.tick(delta); } - const executablePath = await browserFetcher.downloadBrowser({ - downloadPath, - browser, - revision, - progress, - host: getFromENV('PLAYWRIGHT_DOWNLOAD_HOST'), - }); - logPolitely(`${browser} downloaded to ${downloadPath}`); - return executablePath; + await browserFetcher.downloadBrowser({...options, progress}); + logPolitely(`${options.progressBarBrowserName} downloaded to ${options.downloadPath}`); } function toMegabytes(bytes) { @@ -75,4 +86,4 @@ function getFromENV(name) { return value; } -module.exports = {downloadBrowserWithProgressBar}; +module.exports = {downloadBrowserWithProgressBar, downloadOptionsFromENV, localDownloadOptions}; diff --git a/index.js b/index.js index 22381bfca2..48353433ce 100644 --- a/index.js +++ b/index.js @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const fs = require('fs'); +const path = require('path'); const {Playwright} = require('./lib/server/playwright.js'); +const {localDownloadOptions} = require('./download-browser.js'); const playwright = new Playwright({ browsers: ['webkit', 'chromium', 'firefox'], }); -try { - const downloadedBrowsers = require('./.downloaded-browsers.json'); - playwright.chromium._executablePath = downloadedBrowsers.crExecutablePath; - playwright.firefox._executablePath = downloadedBrowsers.ffExecutablePath; - playwright.webkit._executablePath = downloadedBrowsers.wkExecutablePath; -} catch (e) { +if (fs.existsSync(path.join(__dirname, '.local-browsers'))) { + playwright.chromium._executablePath = localDownloadOptions('chromium').executablePath; + playwright.firefox._executablePath = localDownloadOptions('firefox').executablePath; + playwright.webkit._executablePath = localDownloadOptions('webkit').executablePath; } module.exports = playwright; diff --git a/install-from-github.js b/install-from-github.js index 0dc674faf4..d1b1b44f9e 100644 --- a/install-from-github.js +++ b/install-from-github.js @@ -32,55 +32,31 @@ const fs = require('fs'); const util = require('util'); const rmAsync = util.promisify(require('rimraf')); const existsAsync = path => fs.promises.access(path).then(() => true, e => false); -const {downloadBrowserWithProgressBar} = require('./download-browser'); +const {downloadBrowserWithProgressBar, localDownloadOptions} = require('./download-browser'); const protocolGenerator = require('./utils/protocol-types-generator'); -const packageJSON = require('./package.json'); - -const DOWNLOADED_BROWSERS_JSON_PATH = path.join(__dirname, '.downloaded-browsers.json'); -const DOWNLOAD_PATHS = { - chromium: path.join(__dirname, '.local-browsers', `chromium-${packageJSON.playwright.chromium_revision}`), - firefox: path.join(__dirname, '.local-browsers', `firefox-${packageJSON.playwright.firefox_revision}`), - webkit: path.join(__dirname, '.local-browsers', `webkit-${packageJSON.playwright.webkit_revision}`), -}; (async function() { - const downloadedBrowsersJSON = await fs.promises.readFile(DOWNLOADED_BROWSERS_JSON_PATH, 'utf8').then(json => JSON.parse(json)).catch(() => ({})); - try { - if (!(await existsAsync(DOWNLOAD_PATHS.chromium))) { - const crExecutablePath = await downloadBrowserWithProgressBar(DOWNLOAD_PATHS.chromium, 'chromium', false /* respectGlobalInstall */); - downloadedBrowsersJSON.crExecutablePath = crExecutablePath; - await protocolGenerator.generateChromiumProtocol(crExecutablePath); - await fs.promises.writeFile(DOWNLOADED_BROWSERS_JSON_PATH, JSON.stringify(downloadedBrowsersJSON)); - } - } catch (e) { - console.warn(e.message); + const chromiumOptions = localDownloadOptions('chromium'); + const firefoxOptions = localDownloadOptions('firefox'); + const webkitOptions = localDownloadOptions('webkit'); + if (!(await existsAsync(chromiumOptions.downloadPath))) { + await downloadBrowserWithProgressBar(chromiumOptions); + await protocolGenerator.generateChromiumProtocol(chromiumOptions.executablePath).catch(console.warn); } - try { - if (!(await existsAsync(DOWNLOAD_PATHS.firefox))) { - const ffExecutablePath = await downloadBrowserWithProgressBar(DOWNLOAD_PATHS.firefox, 'firefox', false /* respectGlobalInstall */); - downloadedBrowsersJSON.ffExecutablePath = ffExecutablePath; - await protocolGenerator.generateFirefoxProtocol(ffExecutablePath); - await fs.promises.writeFile(DOWNLOADED_BROWSERS_JSON_PATH, JSON.stringify(downloadedBrowsersJSON)); - } - } catch (e) { - console.warn(e.message); + if (!(await existsAsync(firefoxOptions.downloadPath))) { + await downloadBrowserWithProgressBar(firefoxOptions); + await protocolGenerator.generateFirefoxProtocol(firefoxOptions.executablePath).catch(console.warn); } - try { - if (!(await existsAsync(DOWNLOAD_PATHS.webkit))) { - const wkExecutablePath = await downloadBrowserWithProgressBar(DOWNLOAD_PATHS.webkit, 'webkit', false /* respectGlobalInstall */); - downloadedBrowsersJSON.wkExecutablePath = wkExecutablePath; - await protocolGenerator.generateWebKitProtocol(path.dirname(wkExecutablePath)); - await fs.promises.writeFile(DOWNLOADED_BROWSERS_JSON_PATH, JSON.stringify(downloadedBrowsersJSON)); - } - } catch (e) { - console.warn(e.message); + if (!(await existsAsync(webkitOptions.downloadPath))) { + await downloadBrowserWithProgressBar(webkitOptions); + await protocolGenerator.generateWebKitProtocol(webkitOptions.downloadPath).catch(console.warn); } // Cleanup stale revisions. const directories = new Set(await readdirAsync(path.join(__dirname, '.local-browsers'))); - directories.delete(DOWNLOAD_PATHS.chromium); - directories.delete(DOWNLOAD_PATHS.firefox); - directories.delete(DOWNLOAD_PATHS.webkit); + directories.delete(chromiumOptions.downloadPath); + directories.delete(firefoxOptions.downloadPath); + directories.delete(webkitOptions.downloadPath); // cleanup old browser directories. directories.add(path.join(__dirname, '.local-chromium')); directories.add(path.join(__dirname, '.local-firefox')); diff --git a/package.json b/package.json index 87f9494f8b..5ec9f59ffa 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "license": "Apache-2.0", "dependencies": { "debug": "^4.1.0", - "env-paths": "^2.2.0", "extract-zip": "^1.6.6", "https-proxy-agent": "^3.0.0", "jpeg-js": "^0.3.6", diff --git a/packages/playwright-chromium/index.js b/packages/playwright-chromium/index.js index 1ff40f5174..50776eb99c 100644 --- a/packages/playwright-chromium/index.js +++ b/packages/playwright-chromium/index.js @@ -15,15 +15,13 @@ */ const path = require('path'); const {Playwright} = require('playwright-core/lib/server/playwright.js'); +const {downloadOptionsFromENV} = require('playwright-core/download-browser.js'); const playwright = new Playwright({ browsers: ['chromium'], }); + +playwright.chromium._executablePath = downloadOptionsFromENV(__dirname, 'chromium').executablePath; + module.exports = playwright; -try { - const downloadedBrowsers = require('./.downloaded-browsers.json'); - playwright.chromium._executablePath = downloadedBrowsers.crExecutablePath; -} catch (e) { - throw new Error('playwright-chromium has not downloaded Chromium.'); -} diff --git a/packages/playwright-chromium/install.js b/packages/playwright-chromium/install.js index cdfb0ede5f..1bba82c5c0 100644 --- a/packages/playwright-chromium/install.js +++ b/packages/playwright-chromium/install.js @@ -15,8 +15,8 @@ */ const path = require('path'); const fs = require('fs'); -const {downloadBrowserWithProgressBar} = require('playwright-core/download-browser'); +const {downloadBrowserWithProgressBar, downloadOptionsFromEnv} = require('playwright-core/download-browser'); + (async function() { - const crExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'chromium'), 'chromium', true /* respectGlobalInstall */); - await fs.promises.writeFile(path.join(__dirname, '.downloaded-browsers.json'), JSON.stringify({crExecutablePath})); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'chromium')); })(); diff --git a/packages/playwright-firefox/index.js b/packages/playwright-firefox/index.js index 0236851b65..663ebb0fda 100644 --- a/packages/playwright-firefox/index.js +++ b/packages/playwright-firefox/index.js @@ -15,16 +15,13 @@ */ const path = require('path'); const {Playwright} = require('playwright-core/lib/server/playwright.js'); +const {downloadOptionsFromENV} = require('playwright-core/download-browser.js'); const playwright = new Playwright({ browsers: ['firefox'], }); + +playwright.firefox._executablePath = downloadOptionsFromENV(__dirname, 'firefox').executablePath; + module.exports = playwright; -try { - const downloadedBrowsers = require(path.join(__dirname, '.downloaded-browsers.json')); - playwright.firefox._executablePath = downloadedBrowsers.ffExecutablePath; -} catch (e) { - throw new Error('playwright-firefox has not downloaded Firefox.'); -} - diff --git a/packages/playwright-firefox/install.js b/packages/playwright-firefox/install.js index c409726130..7ccc5c5193 100644 --- a/packages/playwright-firefox/install.js +++ b/packages/playwright-firefox/install.js @@ -15,9 +15,8 @@ */ const path = require('path'); const fs = require('fs'); -const {downloadBrowserWithProgressBar} = require('playwright-core/download-browser'); +const {downloadBrowserWithProgressBar, downloadOptionsFromEnv} = require('playwright-core/download-browser'); (async function() { - const ffExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'firefox'), 'firefox', true /* respectGlobalInstall */); - await fs.promises.writeFile(path.join(__dirname, '.downloaded-browsers.json'), JSON.stringify({ffExecutablePath, })); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'firefox')); })(); diff --git a/packages/playwright-webkit/index.js b/packages/playwright-webkit/index.js index b5a4f5738f..2f55cca7b5 100644 --- a/packages/playwright-webkit/index.js +++ b/packages/playwright-webkit/index.js @@ -15,16 +15,13 @@ */ const path = require('path'); const {Playwright} = require('playwright-core/lib/server/playwright.js'); +const {downloadOptionsFromENV} = require('playwright-core/download-browser.js'); const playwright = new Playwright({ browsers: ['webkit'], }); + +playwright.webkit._executablePath = downloadOptionsFromENV(__dirname, 'webkit').executablePath; + module.exports = playwright; -try { - const downloadedBrowsers = require(path.join(__dirname, '.downloaded-browsers.json')); - playwright.webkit._executablePath = downloadedBrowsers.wkExecutablePath; -} catch (e) { - throw new Error('playwright-webkit has not downloaded WebKit.'); -} - diff --git a/packages/playwright-webkit/install.js b/packages/playwright-webkit/install.js index 0b8c812233..497d5e891f 100644 --- a/packages/playwright-webkit/install.js +++ b/packages/playwright-webkit/install.js @@ -15,9 +15,8 @@ */ const path = require('path'); const fs = require('fs'); -const {downloadBrowserWithProgressBar} = require('playwright-core/download-browser'); +const {downloadBrowserWithProgressBar, downloadOptionsFromEnv} = require('playwright-core/download-browser'); (async function() { - const wkExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'webkit'), 'webkit', true /* respectGlobalInstall */); - await fs.promises.writeFile(path.join(__dirname, '.downloaded-browsers.json'), JSON.stringify({wkExecutablePath, })); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'webkit')); })(); diff --git a/packages/playwright/index.js b/packages/playwright/index.js index 5591bbe62c..ef1fd91094 100644 --- a/packages/playwright/index.js +++ b/packages/playwright/index.js @@ -15,18 +15,15 @@ */ const path = require('path'); const {Playwright} = require('playwright-core/lib/server/playwright.js'); +const {downloadOptionsFromENV} = require('playwright-core/download-browser.js'); const playwright = new Playwright({ browsers: ['webkit', 'chromium', 'firefox'], }); + +playwright.chromium._executablePath = downloadOptionsFromENV(__dirname, 'chromium').executablePath; +playwright.webkit._executablePath = downloadOptionsFromENV(__dirname, 'webkit').executablePath; +playwright.firefox._executablePath = downloadOptionsFromENV(__dirname, 'firefox').executablePath; + module.exports = playwright; -try { - const downloadedBrowsers = require(path.join(__dirname, '.downloaded-browsers.json')); - playwright.chromium._executablePath = downloadedBrowsers.crExecutablePath; - playwright.firefox._executablePath = downloadedBrowsers.ffExecutablePath; - playwright.webkit._executablePath = downloadedBrowsers.wkExecutablePath; -} catch (e) { - throw new Error('ERROR: Playwright did not download browsers'); -} - diff --git a/packages/playwright/install.js b/packages/playwright/install.js index 425202cfef..5445f25bc3 100644 --- a/packages/playwright/install.js +++ b/packages/playwright/install.js @@ -15,11 +15,10 @@ */ const path = require('path'); const fs = require('fs'); -const {downloadBrowserWithProgressBar} = require('playwright-core/download-browser'); +const {downloadBrowserWithProgressBar, downloadOptionsFromEnv} = require('playwright-core/download-browser'); (async function() { - const crExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'chromium'), 'chromium', true /* respectGlobalInstall */); - const ffExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'firefox'), 'firefox', true /* respectGlobalInstall */); - const wkExecutablePath = await downloadBrowserWithProgressBar(path.join(__dirname, '.local-browsers', 'webkit'), 'webkit', true /* respectGlobalInstall */); - await fs.promises.writeFile(path.join(__dirname, '.downloaded-browsers.json'), JSON.stringify({crExecutablePath, ffExecutablePath, wkExecutablePath, })); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'chromium')); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'firefox')); + await downloadBrowserWithProgressBar(downloadOptionsFromEnv(__dirname, 'webkit')); })(); diff --git a/src/server/browserFetcher.ts b/src/server/browserFetcher.ts index 466c813b12..a2609a9043 100644 --- a/src/server/browserFetcher.ts +++ b/src/server/browserFetcher.ts @@ -125,7 +125,7 @@ function revisionURL(options: DownloadOptions): string { return util.format(urlTemplate, host, revision); } -export async function downloadBrowser(options: DownloadOptions): Promise { +export async function downloadBrowser(options: DownloadOptions): Promise { const { browser, revision, @@ -146,9 +146,16 @@ export async function downloadBrowser(options: DownloadOptions): Promise if (await existsAsync(zipPath)) await unlinkAsync(zipPath); } - const executablePath = path.join(downloadPath, ...RELATIVE_EXECUTABLE_PATHS[browser][platform as BrowserPlatform]); - await chmodAsync(executablePath, 0o755); - return executablePath; + await chmodAsync(executablePath(options), 0o755); +} + +export function executablePath(options: DownloadOptions): string { + const { + browser, + downloadPath, + platform = CURRENT_HOST_PLATFORM, + } = options; + return path.join(downloadPath, ...RELATIVE_EXECUTABLE_PATHS[browser][platform as BrowserPlatform]); } export async function canDownload(options: DownloadOptions): Promise {