From db7bd8ebd2666e23c40002d0f0f5a677e9424fce Mon Sep 17 00:00:00 2001 From: Ross Wollman Date: Fri, 8 Apr 2022 10:46:24 -0700 Subject: [PATCH] chore: don't auto-install browsers if global install (#13299) --- installation-tests/README.md | 3 + .../bin/local-playwright-registry | 274 ++++++++++++++++++ installation-tests/bin/npm_i | 33 +++ installation-tests/bin/npx_playwright | 3 + installation-tests/initialize_test.sh | 18 +- installation-tests/test_android_types.sh | 2 +- installation-tests/test_electron_types.sh | 3 +- .../test_npm_i_installs_local_package.sh | 1 - installation-tests/test_npx_global_codegen.sh | 22 ++ installation-tests/test_npx_global_help.sh | 22 ++ installation-tests/test_npx_global_install.sh | 26 ++ .../test_playwright_chromium_should_work.sh | 4 + .../test_playwright_firefox_should_work.sh | 4 + ...global_installation_subsequent_installs.sh | 1 - .../test_playwright_webkit_should_work.sh | 4 + installation-tests/test_typescript_types.sh | 4 +- packages/playwright-chromium/install.js | 3 +- packages/playwright-core/src/cli/cli.ts | 5 +- packages/playwright-core/src/utils/index.ts | 2 + packages/playwright-firefox/install.js | 3 +- packages/playwright-webkit/install.js | 3 +- packages/playwright/install.js | 3 +- 22 files changed, 423 insertions(+), 20 deletions(-) create mode 100755 installation-tests/bin/local-playwright-registry create mode 100755 installation-tests/bin/npx_playwright create mode 100644 installation-tests/test_npx_global_codegen.sh create mode 100644 installation-tests/test_npx_global_help.sh create mode 100644 installation-tests/test_npx_global_install.sh diff --git a/installation-tests/README.md b/installation-tests/README.md index 49bcf46608..2f02b2cdbf 100644 --- a/installation-tests/README.md +++ b/installation-tests/README.md @@ -19,3 +19,6 @@ To run all tests: ``` To install local builds of `playwright` packages in tests, do `npm_i playwright`. + +Each test run will get its own npm state. You can use `local-playwright-registry ` to +ensure it was installed as part of the test run, and that it was a local copy. diff --git a/installation-tests/bin/local-playwright-registry b/installation-tests/bin/local-playwright-registry new file mode 100755 index 0000000000..bae4f38aa9 --- /dev/null +++ b/installation-tests/bin/local-playwright-registry @@ -0,0 +1,274 @@ +#!/usr/bin/env node + +/* + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// ============================================ +// See `./local-playwright-registry help` for +// usage and help. +// ============================================ + +const crypto = require('crypto'); +const { spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const http = require('http'); +const https = require('https'); + +// WORK_DIR should be a local directory the Registry can work in through its lifetime. It will not be removed automatically. +const WORK_DIR = path.join(process.cwd(), '.playwright-registry'); +// ACCESS_LOGS records which packages have been served locally vs. proxied to the official npm registry. +const ACCESS_LOGS = path.join(WORK_DIR, 'access.log'); +// REGISTRY_URL_FILE is the URL to us to connect to the registy. It is allocated dynamically and shows up on disk only once the packages are actually available for download. +const REGISTRY_URL_FILE = path.join(WORK_DIR, 'registry.url.txt'); + +const kPublicNpmRegistry = 'https://registry.npmjs.org'; +const kContentTypeAbbreviatedMetadata = 'application/vnd.npm.install-v1+json'; + +class Registry { + constructor() { + this._objectsDir = path.join(WORK_DIR, 'objects'); + this._packageMeta = new Map(); + this._address = ''; + this._log = () => { }; + } + + async init() { + await fs.promises.mkdir(this._objectsDir, { recursive: true }); + await fs.promises.writeFile(ACCESS_LOGS, ''); + this._log = msg => fs.appendFileSync(ACCESS_LOGS, `${msg}\n`); + + await Promise.all([ + this._addPackage('playwright', getEnvOrDie('PLAYWRIGHT_TGZ')), + this._addPackage('playwright-core', getEnvOrDie('PLAYWRIGHT_CORE_TGZ')), + this._addPackage('playwright-chromium', getEnvOrDie('PLAYWRIGHT_CHROMIUM_TGZ')), + this._addPackage('playwright-firefox',getEnvOrDie('PLAYWRIGHT_FIREFOX_TGZ')), + this._addPackage('playwright-webkit',getEnvOrDie('PLAYWRIGHT_WEBKIT_TGZ')), + this._addPackage('@playwright/test',getEnvOrDie('PLAYWRIGHT_TEST_TGZ')), + ]); + + // Minimal Server that implements essential endpoints from the NPM Registry API. + // See https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md. + this._server = http.createServer(async (req, res) => { + this._log(`REQUEST: ${req.method} ${req.url}`); + // 1. Only support GET requests + if (req.method !== 'GET') { + res.writeHead(405).end(); + return; + } + + // 2. Determine what package is being asked for. + // The paths we can handle look like: + // - //*/ + // - //* + // - / + const url = new URL(req.url, kPublicNpmRegistry); + let [, userSuppliedPackageName, , userSuppliedTarName] = url.pathname.split('/'); + if (userSuppliedPackageName) + userSuppliedPackageName = decodeURIComponent(userSuppliedPackageName); + if (userSuppliedTarName) + userSuppliedTarName = decodeURIComponent(userSuppliedTarName); + + // 3. If we have local metadata, serve directly (otherwise, proxy to upstream). + if (this._packageMeta.has(userSuppliedPackageName)) { + const [metadata, objectPath] = this._packageMeta.get(userSuppliedPackageName); + if (userSuppliedTarName) { // Tar ball request. + if (path.basename(objectPath) !== userSuppliedTarName) { + res.writeHead(404).end(); + return; + } + this._log(`LOCAL ${userSuppliedPackageName} tar`); + const fileStream = fs.createReadStream(objectPath); + fileStream.pipe(res, { end: true }); + fileStream.on('error', console.error); + res.on('error', console.error); + return; + } else { // Metadata request. + this._log(`LOCAL ${userSuppliedPackageName} metadata`); + res.setHeader('content-type', kContentTypeAbbreviatedMetadata); + res.write(JSON.stringify(metadata)); + res.end(); + return; + } + } else { // Fall through to official registry. + this._log(`PROXIED ${userSuppliedPackageName}`); + const client = { req, res }; + const toNpm = https.request({ + host: url.host, + headers: { ...req.headers, 'host': url.host }, + method: req.method, + path: url.pathname, + searchParams: url.searchParams, + protocol: 'https:', + }, fromNpm => { + client.res.writeHead(fromNpm.statusCode, fromNpm.statusMessage, fromNpm.headers); + fromNpm.on('error', console.error); + fromNpm.pipe(client.res, { end: true }); + }); + client.req.pipe(toNpm); + client.req.on('error', console.error); + return; + } + }); + + this._server.listen(undefined, 'localhost', () => { + this._address = new URL(`http://localhost:${this._server.address().port}`).toString(); + // We now have an address to make tarball paths fully qualified. + for (const [,[metadata]] of this._packageMeta.entries()) { + for (const [,version] of Object.entries(metadata.versions)) + version.dist.tarball = this._address + version.dist.tarball; + } + fs.writeFileSync(REGISTRY_URL_FILE, this._address.toString()); + }); + process.on('exit', () => { + console.log('closing server'); + this._server.close(console.error); + }); + } + + async _addPackage(pkg, tarPath) { + const tmpDir = await fs.promises.mkdtemp(path.join(WORK_DIR, '.staging-package-')); + const { stderr, code } = await spawnAsync('tar', ['-xvzf', tarPath, '-C', tmpDir]); + if (!!code) + throw new Error(`Failed to untar ${pkg}: ${stderr}`); + + const packageJson = JSON.parse((await fs.promises.readFile(path.join(tmpDir, 'package', 'package.json'), 'utf8'))); + if (pkg !== packageJson.name) + throw new Error(`Package name mismatch: ${pkg} is called ${packageJson.name} in its package.json`); + + const shasum = crypto.createHash('sha1').update(await fs.promises.readFile(tarPath)).digest().toString('hex'); + const metadata = { + 'dist-tags': { + latest: packageJson.version, + [packageJson.version]: packageJson.version, + }, + 'modified': new Date().toISOString(), + 'name': pkg, + 'versions': { + [packageJson.version]: { + _hasShrinkwrap: false, + name: pkg, + version: packageJson.version, + dependencies: packageJson.dependencies || {}, + optionalDependencies: packageJson.optionalDependencies || {}, + devDependencies: packageJson.devDependencies || {}, + bundleDependencies: packageJson.bundleDependencies || {}, + peerDependencies: packageJson.peerDependencies || {}, + bin: packageJson.bin || {}, + directories: packageJson.directories || [], + dist: { + // This needs to be updated later with a full address. + tarball: `${encodeURIComponent(pkg)}/-/${shasum}.tgz`, + shasum, + }, + engines: packageJson.engines || {}, + }, + }, + }; + + const object = path.join(this._objectsDir, `${shasum}.tgz`); + await fs.promises.copyFile(tarPath, object); + + this._packageMeta.set(pkg, [metadata, object]); + } + + static async registryFactory() { + const registry = new Registry(); + await registry.init(); + return registry; + } + + static async waitForReady() { + const OVERALL_TIMEOUT_MS = 60000; + const registryUrl = await new Promise(async (res, rej) => { + setTimeout(rej.bind(null, new Error('Timeout: Registry failed to become ready.')), OVERALL_TIMEOUT_MS); + while (true) { + const registryUrl = await fs.promises.readFile(REGISTRY_URL_FILE, 'utf8').catch(() => null); + if (registryUrl) + return res(registryUrl); + await new Promise(r => setTimeout(r, 500)); + } + }); + console.log(registryUrl); + process.exit(0); + } + + static async assertLocalPkg(pkg) { + const logs = await fs.promises.readFile(ACCESS_LOGS, 'utf8'); + const lines = logs.split(`\n`); + if (lines.includes(`LOCAL ${pkg} metadata`) && lines.includes(`LOCAL ${pkg} tar`) && !lines.includes(`PROXIED ${pkg} metadata`)) + return; + console.log('Expected LOCAL metadata and tar, and no PROXIED logs for:', pkg); + console.log('Logs:'); + console.log(lines.join(`\n`)); + process.exit(1); + } +} + +const getEnvOrDie = varName => { + const v = process.env[varName]; + if (!v) + throw new Error(`${varName} environment variable MUST be set.`); + + return v; +}; + +const spawnAsync = (cmd, args, options) => { + const process = spawn(cmd, args, Object.assign({ windowsHide: true }, options)); + + return new Promise(resolve => { + let stdout = ''; + let stderr = ''; + if (process.stdout) + process.stdout.on('data', data => stdout += data); + if (process.stderr) + process.stderr.on('data', data => stderr += data); + process.on('close', code => resolve({ stdout, stderr, code })); + process.on('error', error => resolve({ stdout, stderr, code: 0, error })); + }); +}; + +const commands = { + 'help': async () => { + console.log(` +A minimal, inefficent npm registry to serve local npm packages, or fall through +to the offical npm registry. This is useful for testing npm and npx utilities, +but should NOT, be used for anything more. + +Commands: + - help.......................: prints this help message + - start......................: starts the registry server + - wait-for-ready.............: blocks waiting for the server to print + that it's actually ready and serving the + packages you published! + - assert-downloaded : confirms that was served locally, + AND never proxied to the official registry.`); + }, + 'start': Registry.registryFactory, + 'wait-for-ready': Registry.waitForReady, + 'assert-served-from-local-tgz': ([pkg]) => Registry.assertLocalPkg(pkg), +}; + +(async () => { + const command = commands[process.argv[2]]; + if (!command) { + console.log(`${process.argv[2]} not a valid command:`); + await commands['help'](); + process.exit(1); + } + + await command(process.argv.slice(3)); +})(); diff --git a/installation-tests/bin/npm_i b/installation-tests/bin/npm_i index e32bd25a5a..1ea3554e85 100755 --- a/installation-tests/bin/npm_i +++ b/installation-tests/bin/npm_i @@ -1,19 +1,26 @@ #!/bin/bash args="" +pkgs="" for i in "$@"; do if [[ "$i" == "playwright" ]]; then args="${args} ${PLAYWRIGHT_TGZ}" + pkgs="${pkgs} playwright" elif [[ $i == "playwright-core" ]]; then args="${args} ${PLAYWRIGHT_CORE_TGZ}" + pkgs="${pkgs} playwright-core" elif [[ $i == "playwright-firefox" ]]; then args="${args} ${PLAYWRIGHT_FIREFOX_TGZ}" + pkgs="${pkgs} playwright-firefox" elif [[ $i == "playwright-chromium" ]]; then args="${args} ${PLAYWRIGHT_CHROMIUM_TGZ}" + pkgs="${pkgs} playwright-chromium" elif [[ $i == "playwright-webkit" ]]; then args="${args} ${PLAYWRIGHT_WEBKIT_TGZ}" + pkgs="${pkgs} playwright-webkit" elif [[ $i == "@playwright/test" ]]; then args="${args} ${PLAYWRIGHT_TEST_TGZ}" + pkgs="${pkgs} @playwright/test" elif [[ $i == "-"* ]]; then args="${args} $i" else @@ -22,3 +29,29 @@ for i in "$@"; do done npm install $args 2>&1 +SCRIPT=$(cat <&1 diff --git a/installation-tests/initialize_test.sh b/installation-tests/initialize_test.sh index afcad62e47..c44749a97e 100755 --- a/installation-tests/initialize_test.sh +++ b/installation-tests/initialize_test.sh @@ -58,6 +58,7 @@ function setup_env_variables() { export PLAYWRIGHT_FIREFOX_TGZ="${PWD}/output/playwright-firefox.tgz" export PLAYWRIGHT_TEST_TGZ="${PWD}/output/playwright-test.tgz" PLAYWRIGHT_CHECKOUT="${PWD}/.." + export PLAYWRIGHT_VERSION_UNDER_TEST="$(node ${PLAYWRIGHT_CHECKOUT}/utils/workspace.js --get-version)" } function clean_test_root() { @@ -66,7 +67,8 @@ function clean_test_root() { } function initialize_test { - trap "report_test_result;cd $(pwd -P)" EXIT + TEST_TMP_NPM_SCRATCH_SPACE="$(mktemp -d)"; + trap "report_test_result; kill %1; rm -rf $TEST_TMP_NPM_SCRATCH_SPACE; cd $(pwd -P);" EXIT cd "$(dirname $0)" # cleanup environment @@ -79,11 +81,9 @@ function initialize_test { TEST_FILE=$(basename $0) TEST_NAME=$(basename ${0%%.sh}) - # Check if test tries to install some playwright-family package - # fron NPM registry. + # Check if test tries to install using npm directly if grep 'npm i.*playwright' "$0" 2>&1 >/dev/null; then - # If it does, this is an error: we should always install local packages using - # the `npm_i` script. + # If it does, this is an error: you will miss output cecho "RED" "ERROR: test tries to install playwright-family package from NPM registry!" cecho "RED" " Do not use NPM to install playwright packages!" cecho "RED" " Instead, use 'npm_i' command to install local package" @@ -117,7 +117,13 @@ function initialize_test { cp "${SCRIPTS_PATH}/fixture-scripts/"* . export PATH="${SCRIPTS_PATH}/bin:${PATH}" + # Start up our local registry and configure npm to use it + local-playwright-registry start & + export npm_config_prefix="$TEST_TMP_NPM_SCRATCH_SPACE/npm_prefix" + export npm_config_cache="$TEST_TMP_NPM_SCRATCH_SPACE/npm_cache" + export npm_config_registry="$(local-playwright-registry wait-for-ready)" + export EXPECTED_NODE_MODULES_PARENT="$(pwd -P)" + # Enable bash lines logging. set -x } - diff --git a/installation-tests/test_android_types.sh b/installation-tests/test_android_types.sh index 7443fcb2f8..8194d2dc00 100755 --- a/installation-tests/test_android_types.sh +++ b/installation-tests/test_android_types.sh @@ -8,4 +8,4 @@ npm i -D @types/node@14 echo "import { AndroidDevice, _android, AndroidWebView, Page } from 'playwright';" > "test.ts" echo "Running tsc" -npx -p typescript@3.7.5 tsc "test.ts" +npx --yes -p typescript@3.7.5 tsc "test.ts" diff --git a/installation-tests/test_electron_types.sh b/installation-tests/test_electron_types.sh index 9224f73bdb..20b57cab03 100755 --- a/installation-tests/test_electron_types.sh +++ b/installation-tests/test_electron_types.sh @@ -9,5 +9,4 @@ npm i -D @types/node@14 echo "import { Page, _electron, ElectronApplication, Electron } from 'playwright';" > "test.ts" echo "Running tsc" -npx -p typescript@3.7.5 tsc "test.ts" - +npx --yes -p typescript@3.7.5 tsc "test.ts" diff --git a/installation-tests/test_npm_i_installs_local_package.sh b/installation-tests/test_npm_i_installs_local_package.sh index d4b00f6bd2..a868a88b45 100755 --- a/installation-tests/test_npm_i_installs_local_package.sh +++ b/installation-tests/test_npm_i_installs_local_package.sh @@ -18,4 +18,3 @@ EOF ) # make sure all dependencies are locally installed. node -e "${SCRIPT}" - diff --git a/installation-tests/test_npx_global_codegen.sh b/installation-tests/test_npx_global_codegen.sh new file mode 100644 index 0000000000..886f2c1aa2 --- /dev/null +++ b/installation-tests/test_npx_global_codegen.sh @@ -0,0 +1,22 @@ +#!/bin/bash +source ./initialize_test.sh && initialize_test "$@" + +echo "Running codegen command without installing" +OUTPUT="$(npx_playwright codegen || true)" +local-playwright-registry assert-served-from-local-tgz playwright +if [[ "${OUTPUT}" != *'Please run the following command to download new browsers'* ]]; then + echo "ERROR: should instruct user to download browsers" + exit 1 +fi +if [[ "${OUTPUT}" == *"chromium"*"downloaded"* ]]; then + echo "ERROR: should not download chromium" + exit 1 +fi +if [[ "${OUTPUT}" == *"webkit"*"downloaded"* ]]; then + echo "ERROR: should not download webkit" + exit 1 +fi +if [[ "${OUTPUT}" == *"firefox"*"downloaded"* ]]; then + echo "ERROR: should not download firefox" + exit 1 +fi diff --git a/installation-tests/test_npx_global_help.sh b/installation-tests/test_npx_global_help.sh new file mode 100644 index 0000000000..9851120a9e --- /dev/null +++ b/installation-tests/test_npx_global_help.sh @@ -0,0 +1,22 @@ +#!/bin/bash +source ./initialize_test.sh && initialize_test "$@" + +echo "Running global help command without first installing project" +OUTPUT="$(npx_playwright --help)" +local-playwright-registry assert-served-from-local-tgz playwright +if [[ "${OUTPUT}" == *'To avoid unexpected behavior, please install your dependencies first'* ]]; then + echo "ERROR: should not warn user about global installation" + exit 1 +fi +if [[ "${OUTPUT}" == *"chromium"*"downloaded"* ]]; then + echo "ERROR: should not download chromium" + exit 1 +fi +if [[ "${OUTPUT}" == *"webkit"*"downloaded"* ]]; then + echo "ERROR: should not download webkit" + exit 1 +fi +if [[ "${OUTPUT}" == *"firefox"*"downloaded"* ]]; then + echo "ERROR: should not download firefox" + exit 1 +fi diff --git a/installation-tests/test_npx_global_install.sh b/installation-tests/test_npx_global_install.sh new file mode 100644 index 0000000000..a7ef97acdc --- /dev/null +++ b/installation-tests/test_npx_global_install.sh @@ -0,0 +1,26 @@ +#!/bin/bash +source ./initialize_test.sh && initialize_test "$@" + +echo "Running install explcitly" +OUTPUT="$(npx_playwright install || true)" +local-playwright-registry assert-served-from-local-tgz playwright +if [[ "${OUTPUT}" == *'Please run the following command to download new browsers'* ]]; then + echo "ERROR: should not tell the user to run install" + exit 1 +fi +if [[ "${OUTPUT}" != *'To avoid unexpected behavior, please install your dependencies first'* ]]; then + echo "ERROR: should warn user about global installation" + exit 1 +fi +if [[ "${OUTPUT}" != *"chromium"*"downloaded"* ]]; then + echo "ERROR: should download chromium" + exit 1 +fi +if [[ "${OUTPUT}" != *"firefox"*"downloaded"* ]]; then + echo "ERROR: should download firefox" + exit 1 +fi +if [[ "${OUTPUT}" != *"webkit"*"downloaded"* ]]; then + echo "ERROR: should download webkit" + exit 1 +fi diff --git a/installation-tests/test_playwright_chromium_should_work.sh b/installation-tests/test_playwright_chromium_should_work.sh index 2197127850..0b0b22cd91 100755 --- a/installation-tests/test_playwright_chromium_should_work.sh +++ b/installation-tests/test_playwright_chromium_should_work.sh @@ -3,6 +3,10 @@ source ./initialize_test.sh && initialize_test "$@" npm_i playwright-core OUTPUT=$(npm_i --foreground-script playwright-chromium) +if [[ "${OUTPUT}" == *'To avoid unexpected behavior, please install your dependencies first'* ]]; then + echo "ERROR: should not warn user about global installation" + exit 1 +fi if [[ "${OUTPUT}" != *"chromium"* ]]; then echo "ERROR: should download chromium" exit 1 diff --git a/installation-tests/test_playwright_firefox_should_work.sh b/installation-tests/test_playwright_firefox_should_work.sh index ca51fe2fcd..3dc442d992 100755 --- a/installation-tests/test_playwright_firefox_should_work.sh +++ b/installation-tests/test_playwright_firefox_should_work.sh @@ -3,6 +3,10 @@ source ./initialize_test.sh && initialize_test "$@" npm_i playwright-core OUTPUT=$(npm_i --foreground-script playwright-firefox) +if [[ "${OUTPUT}" == *'To avoid unexpected behavior, please install your dependencies first'* ]]; then + echo "ERROR: should not warn user about global installation" + exit 1 +fi if [[ "${OUTPUT}" == *"chromium"* ]]; then echo "ERROR: should not download chromium" exit 1 diff --git a/installation-tests/test_playwright_global_installation_subsequent_installs.sh b/installation-tests/test_playwright_global_installation_subsequent_installs.sh index f04b122ce7..75ce11a127 100755 --- a/installation-tests/test_playwright_global_installation_subsequent_installs.sh +++ b/installation-tests/test_playwright_global_installation_subsequent_installs.sh @@ -14,4 +14,3 @@ PLAYWRIGHT_BROWSERS_PATH="${BROWSERS}" npm_i playwright # Note: the flag `--unahdnled-rejections=strict` will force node to terminate in case # of UnhandledPromiseRejection. PLAYWRIGHT_BROWSERS_PATH="${BROWSERS}" node --unhandled-rejections=strict node_modules/playwright/install.js - diff --git a/installation-tests/test_playwright_webkit_should_work.sh b/installation-tests/test_playwright_webkit_should_work.sh index a9ca5ee47c..f636929d65 100755 --- a/installation-tests/test_playwright_webkit_should_work.sh +++ b/installation-tests/test_playwright_webkit_should_work.sh @@ -3,6 +3,10 @@ source ./initialize_test.sh && initialize_test "$@" npm_i playwright-core OUTPUT=$(npm_i --foreground-script playwright-webkit) +if [[ "${OUTPUT}" == *'To avoid unexpected behavior, please install your dependencies first'* ]]; then + echo "ERROR: should not warn user about global installation" + exit 1 +fi if [[ "${OUTPUT}" == *"chromium"* ]]; then echo "ERROR: should not download chromium" exit 1 diff --git a/installation-tests/test_typescript_types.sh b/installation-tests/test_typescript_types.sh index 56e3fa76de..594d5fcfdf 100755 --- a/installation-tests/test_typescript_types.sh +++ b/installation-tests/test_typescript_types.sh @@ -22,8 +22,8 @@ for PKG_NAME in "playwright" \ "playwright-webkit" do echo "Checking types of ${PKG_NAME}" - echo "import { Page } from '${PKG_NAME}';" > "${PKG_NAME}.ts" && npx -p typescript@3.7.5 tsc "${PKG_NAME}.ts" + echo "import { Page } from '${PKG_NAME}';" > "${PKG_NAME}.ts" && npx --yes -p typescript@3.7.5 tsc "${PKG_NAME}.ts" done; echo "Checking types of @playwright/test" -echo npx -p typescript@3.7.5 tsc "playwright-test-types.ts" +echo npx --yes -p typescript@3.7.5 tsc "playwright-test-types.ts" diff --git a/packages/playwright-chromium/install.js b/packages/playwright-chromium/install.js index 24127ec408..8ceef68b95 100644 --- a/packages/playwright-chromium/install.js +++ b/packages/playwright-chromium/install.js @@ -17,7 +17,8 @@ let install; try { - install = require('playwright-core/lib/server').installBrowsersForNpmInstall; + if (!require('playwright-core/lib/utils').isLikelyNpxGlobal()) + install = require('playwright-core/lib/server').installBrowsersForNpmInstall; } catch (e) { // Dev build, don't install browsers by default. } diff --git a/packages/playwright-core/src/cli/cli.ts b/packages/playwright-core/src/cli/cli.ts index 5847d9dc19..7b99cf49dc 100755 --- a/packages/playwright-core/src/cli/cli.ts +++ b/packages/playwright-core/src/cli/cli.ts @@ -33,7 +33,7 @@ import type { BrowserType } from '../client/browserType'; import type { BrowserContextOptions, LaunchOptions } from '../client/types'; import { spawn } from 'child_process'; import { getPlaywrightVersion } from '../common/userAgent'; -import { wrapInASCIIBox } from '../utils'; +import { wrapInASCIIBox, isLikelyNpxGlobal } from '../utils'; import { spawnAsync } from '../utils/spawnAsync'; import { launchGridAgent } from '../grid/gridAgent'; import type { GridFactory } from '../grid/gridServer'; @@ -119,8 +119,7 @@ program .option('--with-deps', 'install system dependencies for browsers') .option('--force', 'force reinstall of stable browser channels') .action(async function(args: string[], options: { withDeps?: boolean, force?: boolean }) { - const isLikelyNpxGlobal = process.argv.length >= 2 && process.argv[1].includes('_npx'); - if (isLikelyNpxGlobal) { + if (isLikelyNpxGlobal()) { console.error(wrapInASCIIBox([ `WARNING: It looks like you are running 'npx playwright install' without first`, `installing your project's dependencies.`, diff --git a/packages/playwright-core/src/utils/index.ts b/packages/playwright-core/src/utils/index.ts index 30adeb4eb1..0b841dd2b6 100644 --- a/packages/playwright-core/src/utils/index.ts +++ b/packages/playwright-core/src/utils/index.ts @@ -212,3 +212,5 @@ export function streamToString(stream: stream.Readable): Promise { stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); }); } + +export const isLikelyNpxGlobal = () => process.argv.length >= 2 && process.argv[1].includes('_npx'); diff --git a/packages/playwright-firefox/install.js b/packages/playwright-firefox/install.js index 61b8420cb4..a917664fc0 100644 --- a/packages/playwright-firefox/install.js +++ b/packages/playwright-firefox/install.js @@ -17,7 +17,8 @@ let install; try { - install = require('playwright-core/lib/server').installBrowsersForNpmInstall; + if (!require('playwright-core/lib/utils').isLikelyNpxGlobal()) + install = require('playwright-core/lib/server').installBrowsersForNpmInstall; } catch (e) { // Dev build, don't install browsers by default. } diff --git a/packages/playwright-webkit/install.js b/packages/playwright-webkit/install.js index 1e43075a08..237ee38d63 100644 --- a/packages/playwright-webkit/install.js +++ b/packages/playwright-webkit/install.js @@ -17,7 +17,8 @@ let install; try { - install = require('playwright-core/lib/server').installBrowsersForNpmInstall; + if (!require('playwright-core/lib/utils').isLikelyNpxGlobal()) + install = require('playwright-core/lib/server').installBrowsersForNpmInstall; } catch (e) { // Dev build, don't install browsers by default. } diff --git a/packages/playwright/install.js b/packages/playwright/install.js index 77dff0dc92..d325c22096 100644 --- a/packages/playwright/install.js +++ b/packages/playwright/install.js @@ -17,7 +17,8 @@ let install; try { - install = require('playwright-core/lib/server').installDefaultBrowsersForNpmInstall; + if (!require('playwright-core/lib/utils').isLikelyNpxGlobal()) + install = require('playwright-core/lib/server').installDefaultBrowsersForNpmInstall; } catch (e) { // Dev build, don't install browsers by default. }