From e69e836c40dd55c08a884c8523bf2da893101034 Mon Sep 17 00:00:00 2001 From: Ross Wollman Date: Mon, 25 Apr 2022 09:30:14 -0700 Subject: [PATCH] chore: port installation tests to @playwright/test (#13644) --- .github/workflows/tests_secondary.yml | 2 +- .gitignore | 2 + installation-tests/.gitignore | 1 - installation-tests/README.md | 24 -- .../bin/local-playwright-registry | 274 ------------------ installation-tests/bin/npm_i | 57 ---- installation-tests/bin/npx_playwright | 3 - installation-tests/initialize_test.sh | 130 --------- installation-tests/run_all_tests.sh | 60 ---- installation-tests/test_android_types.sh | 11 - .../test_connect_to_selenium.sh | 8 - installation-tests/test_electron_types.sh | 12 - .../test_npm_i_installs_local_package.sh | 20 -- 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 | 28 -- ...test_playwright_cli_codegen_should_work.sh | 38 --- ...test_playwright_cli_install_should_work.sh | 50 ---- ...t_playwright_cli_screenshot_should_work.sh | 19 -- .../test_playwright_driver_should_work.sh | 12 - .../test_playwright_electron_should_work.sh | 9 - .../test_playwright_firefox_should_work.sh | 28 -- .../test_playwright_global_installation.sh | 14 - ...right_global_installation_cross_package.sh | 20 -- ...global_installation_subsequent_installs.sh | 16 - .../test_playwright_should_work.sh | 31 -- ...should_work_with_relative_browsers_path.sh | 13 - ...ght_should_work_with_relative_home_path.sh | 8 - .../test_playwright_validate_dependencies.sh | 12 - ...idate_dependencies_skip_executable_path.sh | 12 - .../test_playwright_webkit_should_work.sh | 28 -- installation-tests/test_screencast.sh | 16 - .../test_skip_browser_download.sh | 15 - ...download_inspect_with_custom_executable.sh | 23 -- installation-tests/test_typescript_types.sh | 29 -- package.json | 1 + tests/installation/android-types.spec.ts | 25 ++ .../installation/connect-to-selenium.spec.ts | 23 ++ tests/installation/driver-should-work.spec.ts | 22 ++ tests/installation/electron-types.spec.ts | 25 ++ tests/installation/expect.d.ts | 25 ++ .../fixture-scripts/download-chromedriver.js | 0 .../fixture-scripts/driver-client.js | 0 .../fixture-scripts/electron-app.js | 0 .../esm-playwright-chromium.mjs | 0 .../esm-playwright-firefox.mjs | 0 .../fixture-scripts/esm-playwright-test.mjs | 0 .../fixture-scripts/esm-playwright-webkit.mjs | 0 .../fixture-scripts/esm-playwright.mjs | 0 .../installation}/fixture-scripts/esm.mjs | 0 .../fixture-scripts/failing.spec.js | 0 .../inspector-custom-executable.js | 0 .../fixture-scripts/playwright-test-types.ts | 0 .../fixture-scripts/read-json-report.js | 0 .../fixture-scripts/sample.spec.js | 0 .../fixture-scripts/sanity-electron.js | 0 .../installation}/fixture-scripts/sanity.js | 0 .../fixture-scripts/screencast.js | 0 ...idate-dependencies-skip-executable-path.js | 0 .../fixture-scripts/validate-dependencies.js | 0 tests/installation/globalSetup.ts | 60 ++++ .../npm-installs-local-packages.spec.ts | 36 +++ tests/installation/npmTest.ts | 172 +++++++++++ tests/installation/npx-global-help.spec.ts | 23 ++ tests/installation/npx-global-install.spec.ts | 24 ++ .../npx-global-spec-codegen.spec.ts | 23 ++ .../playwright-cli-codegen.spec.ts | 38 +++ ...playwright-cli-install-should-work.spec.ts | 35 +++ ...ywright-cli-screenshot-should-work.spec.ts | 27 ++ .../playwright-electron-should-work.spec.ts | 21 ++ ...-global-installation-cross-package.spec.ts | 28 ++ .../playwright-global-installation.spec.ts | 24 ++ ...ywright-global-subsequent-installs.spec.ts | 28 ++ ...d-work-with-relative-browsers-path.spec.ts | 25 ++ ...hould-work-with-relative-home-path.spec.ts | 23 ++ .../playwright-should-work.spec.ts | 27 ++ .../playwright-test-should-work.spec.ts | 28 ++ ...playwright-test-stacks-should-work.spec.ts | 24 ++ .../playwright-xyz-should-work.spec.ts | 33 +++ tests/installation/playwright.config.ts | 32 ++ tests/installation/registry.ts | 184 ++++++++++++ tests/installation/screencast.spec.ts | 23 ++ ...oad-inspect-with-custom-executable.spec.ts | 25 ++ .../skip-browser-download.spec.ts | 23 ++ tests/installation/spawnAsync.ts | 48 +++ tests/installation/typescript-types.spec.ts | 37 +++ .../validate-dependencies.spec.ts | 30 ++ 88 files changed, 1225 insertions(+), 1092 deletions(-) delete mode 100644 installation-tests/.gitignore delete mode 100644 installation-tests/README.md delete mode 100755 installation-tests/bin/local-playwright-registry delete mode 100755 installation-tests/bin/npm_i delete mode 100755 installation-tests/bin/npx_playwright delete mode 100755 installation-tests/initialize_test.sh delete mode 100755 installation-tests/run_all_tests.sh delete mode 100755 installation-tests/test_android_types.sh delete mode 100755 installation-tests/test_connect_to_selenium.sh delete mode 100755 installation-tests/test_electron_types.sh delete mode 100755 installation-tests/test_npm_i_installs_local_package.sh delete mode 100755 installation-tests/test_npx_global_codegen.sh delete mode 100755 installation-tests/test_npx_global_help.sh delete mode 100755 installation-tests/test_npx_global_install.sh delete mode 100755 installation-tests/test_playwright_chromium_should_work.sh delete mode 100755 installation-tests/test_playwright_cli_codegen_should_work.sh delete mode 100755 installation-tests/test_playwright_cli_install_should_work.sh delete mode 100755 installation-tests/test_playwright_cli_screenshot_should_work.sh delete mode 100755 installation-tests/test_playwright_driver_should_work.sh delete mode 100755 installation-tests/test_playwright_electron_should_work.sh delete mode 100755 installation-tests/test_playwright_firefox_should_work.sh delete mode 100755 installation-tests/test_playwright_global_installation.sh delete mode 100755 installation-tests/test_playwright_global_installation_cross_package.sh delete mode 100755 installation-tests/test_playwright_global_installation_subsequent_installs.sh delete mode 100755 installation-tests/test_playwright_should_work.sh delete mode 100755 installation-tests/test_playwright_should_work_with_relative_browsers_path.sh delete mode 100755 installation-tests/test_playwright_should_work_with_relative_home_path.sh delete mode 100755 installation-tests/test_playwright_validate_dependencies.sh delete mode 100755 installation-tests/test_playwright_validate_dependencies_skip_executable_path.sh delete mode 100755 installation-tests/test_playwright_webkit_should_work.sh delete mode 100755 installation-tests/test_screencast.sh delete mode 100755 installation-tests/test_skip_browser_download.sh delete mode 100755 installation-tests/test_skip_browser_download_inspect_with_custom_executable.sh delete mode 100755 installation-tests/test_typescript_types.sh create mode 100755 tests/installation/android-types.spec.ts create mode 100755 tests/installation/connect-to-selenium.spec.ts create mode 100755 tests/installation/driver-should-work.spec.ts create mode 100755 tests/installation/electron-types.spec.ts create mode 100644 tests/installation/expect.d.ts rename {installation-tests => tests/installation}/fixture-scripts/download-chromedriver.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/driver-client.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/electron-app.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm-playwright-chromium.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm-playwright-firefox.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm-playwright-test.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm-playwright-webkit.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm-playwright.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/esm.mjs (100%) rename {installation-tests => tests/installation}/fixture-scripts/failing.spec.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/inspector-custom-executable.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/playwright-test-types.ts (100%) rename {installation-tests => tests/installation}/fixture-scripts/read-json-report.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/sample.spec.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/sanity-electron.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/sanity.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/screencast.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/validate-dependencies-skip-executable-path.js (100%) rename {installation-tests => tests/installation}/fixture-scripts/validate-dependencies.js (100%) create mode 100644 tests/installation/globalSetup.ts create mode 100755 tests/installation/npm-installs-local-packages.spec.ts create mode 100644 tests/installation/npmTest.ts create mode 100755 tests/installation/npx-global-help.spec.ts create mode 100755 tests/installation/npx-global-install.spec.ts create mode 100755 tests/installation/npx-global-spec-codegen.spec.ts create mode 100755 tests/installation/playwright-cli-codegen.spec.ts create mode 100755 tests/installation/playwright-cli-install-should-work.spec.ts create mode 100755 tests/installation/playwright-cli-screenshot-should-work.spec.ts create mode 100755 tests/installation/playwright-electron-should-work.spec.ts create mode 100755 tests/installation/playwright-global-installation-cross-package.spec.ts create mode 100755 tests/installation/playwright-global-installation.spec.ts create mode 100755 tests/installation/playwright-global-subsequent-installs.spec.ts create mode 100755 tests/installation/playwright-should-work-with-relative-browsers-path.spec.ts create mode 100755 tests/installation/playwright-should-work-with-relative-home-path.spec.ts create mode 100755 tests/installation/playwright-should-work.spec.ts create mode 100755 tests/installation/playwright-test-should-work.spec.ts create mode 100755 tests/installation/playwright-test-stacks-should-work.spec.ts create mode 100755 tests/installation/playwright-xyz-should-work.spec.ts create mode 100644 tests/installation/playwright.config.ts create mode 100644 tests/installation/registry.ts create mode 100755 tests/installation/screencast.spec.ts create mode 100755 tests/installation/skip-browser-download-inspect-with-custom-executable.spec.ts create mode 100755 tests/installation/skip-browser-download.spec.ts create mode 100644 tests/installation/spawnAsync.ts create mode 100755 tests/installation/typescript-types.spec.ts create mode 100644 tests/installation/validate-dependencies.spec.ts diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index ca8626bb19..afd07669a9 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -134,7 +134,7 @@ jobs: - run: npm run build - run: npx playwright install-deps - name: INSTALLATION TESTS - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash installation-tests/run_all_tests.sh + run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run itest headful_linux: name: "Headful Linux" diff --git a/.gitignore b/.gitignore index e0ca114a0c..fcd59fde3c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ playwright-report /packages/playwright/README.md /packages/playwright-core/api.json .env +/tests/installation/output/ +/tests/installation/.registry.json diff --git a/installation-tests/.gitignore b/installation-tests/.gitignore deleted file mode 100644 index 53752db253..0000000000 --- a/installation-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output diff --git a/installation-tests/README.md b/installation-tests/README.md deleted file mode 100644 index 2f02b2cdbf..0000000000 --- a/installation-tests/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Installation Tests - -These tests check end-to-end installation and operation of Playwright. -Each test is set with a separate folder that contains all scripts from -`fixture-scripts` folder and dummy package.json. - -To create a new test, create a new file that starts with `test_*.sh` -with the following header: - - ```bash - #!/bin/bash - source ./initialize_test.sh && initialize_test "$@" # initialize test - ``` - -To run all tests: - -```bash -./run_all_tests.sh -``` - -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 deleted file mode 100755 index bae4f38aa9..0000000000 --- a/installation-tests/bin/local-playwright-registry +++ /dev/null @@ -1,274 +0,0 @@ -#!/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 deleted file mode 100755 index 1ea3554e85..0000000000 --- a/installation-tests/bin/npm_i +++ /dev/null @@ -1,57 +0,0 @@ -#!/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 - echo "ERROR: cannot install package $i using npm_i. Use regular npm instead" - fi -done -npm install $args 2>&1 - -SCRIPT=$(cat <&1 diff --git a/installation-tests/initialize_test.sh b/installation-tests/initialize_test.sh deleted file mode 100755 index 8d71d0192a..0000000000 --- a/installation-tests/initialize_test.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash - -# break script execution if some command returns non-zero exit code -set -e - -TEST_FRAMEWORK_RUN_ROOT="/tmp/playwright-installation-tests" - -function build_packages() { - local PACKAGE_BUILDER="../../utils/pack_package.js" - rm -rf ./output - mkdir ./output - pushd ./output >/dev/null - - node ${PACKAGE_BUILDER} playwright-core "${PLAYWRIGHT_CORE_TGZ}" 2>&1 1>/dev/null - node ${PACKAGE_BUILDER} playwright-test "${PLAYWRIGHT_TEST_TGZ}" 2>&1 1>/dev/null - node ${PACKAGE_BUILDER} playwright "${PLAYWRIGHT_TGZ}" 2>&1 1>/dev/null - node ${PACKAGE_BUILDER} playwright-chromium "${PLAYWRIGHT_CHROMIUM_TGZ}" 2>&1 1>/dev/null - node ${PACKAGE_BUILDER} playwright-webkit "${PLAYWRIGHT_WEBKIT_TGZ}" 2>&1 1>/dev/null - node ${PACKAGE_BUILDER} playwright-firefox "${PLAYWRIGHT_FIREFOX_TGZ}" 2>&1 1>/dev/null - popd >/dev/null -} - -function cecho(){ - local RED="\033[0;31m" - local GREEN="\033[0;32m" - local YELLOW="\033[1;33m" - local NC="\033[0m" # No Color - printf "${!1}${2} ${NC}\n" -} - -function report_test_result { - RV=$? - set +x - if [[ $RV == 0 ]]; then - echo - cecho "GREEN" "<<<<<<<<<<<<" - cecho "GREEN" " Test '${TEST_FILE}' PASSED" - cecho "GREEN" "<<<<<<<<<<<<" - else - cecho "RED" "<<<<<<<<<<<<" - cecho "RED" " Test '${TEST_FILE}' FAILED" - cecho "RED" " To debug locally, run:" - cecho "RED" " bash ${TEST_FILE}" - cecho "RED" "<<<<<<<<<<<<" - echo - fi - echo -} - -function setup_env_variables() { - # Package paths. - NODE_VERSION=$(node -e "console.log(process.version.slice(1).split('.')[0])") - - export PLAYWRIGHT_CORE_TGZ="${PWD}/output/playwright-core.tgz" - export PLAYWRIGHT_TGZ="${PWD}/output/playwright.tgz" - export PLAYWRIGHT_CHROMIUM_TGZ="${PWD}/output/playwright-chromium.tgz" - export PLAYWRIGHT_WEBKIT_TGZ="${PWD}/output/playwright-webkit.tgz" - 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() { - rm -rf "${TEST_FRAMEWORK_RUN_ROOT}" - mkdir -p "${TEST_FRAMEWORK_RUN_ROOT}" -} - -function initialize_test { - trap "report_test_result; kill %1; cd $(pwd -P);" EXIT - cd "$(dirname $0)" - - # cleanup environment - unset PLAYWRIGHT_DOWNLOAD_HOST - unset PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD - export PLAYWRIGHT_BROWSERS_PATH=0 - - local SCRIPTS_PATH="$(pwd -P)" - setup_env_variables - TEST_FILE=$(basename $0) - TEST_NAME=$(basename ${0%%.sh}) - - # 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: 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" - exit 1 - fi - - if [[ "$1" != "--no-build" && "$2" != "--no-build" ]]; then - echo 'Building packages... NOTE: run with `--no-build` to reuse previous builds' - build_packages - else - if [[ ! -f "${PLAYWRIGHT_TGZ}" || \ - ! -f "${PLAYWRIGHT_CORE_TGZ}" || \ - ! -f "${PLAYWRIGHT_CHROMIUM_TGZ}" || \ - ! -f "${PLAYWRIGHT_WEBKIT_TGZ}" || \ - ! -f "${PLAYWRIGHT_FIREFOX_TGZ}" || \ - ! -f "${PLAYWRIGHT_TEST_TGZ}" ]]; then - echo 'ERROR: cannot run test with `--no-build` flag! One of the packages is missing!' - exit 1 - fi - fi - if [[ "$1" != "--do-not-clean-test-root" && "$2" != "--do-not-clean-test-root" ]]; then - clean_test_root - fi - cd ${TEST_FRAMEWORK_RUN_ROOT} - - cecho "YELLOW" ">>>>>>>>>>>>" - cecho "YELLOW" " Running test - '${TEST_FILE}'" - cecho "YELLOW" " Workdir - ${PWD}/${TEST_NAME}" - cecho "YELLOW" ">>>>>>>>>>>>" - mkdir ${TEST_NAME} && cd ${TEST_NAME} && npm init -y 1>/dev/null 2>/dev/null - - 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 & - TEST_TMP_NPM_SCRATCH_SPACE="${TEST_FRAMEWORK_RUN_ROOT}/${TEST_NAME}--npm-scratch-space" - 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/run_all_tests.sh b/installation-tests/run_all_tests.sh deleted file mode 100755 index 4efa5ef070..0000000000 --- a/installation-tests/run_all_tests.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -e -set +x - -function report_results() { - echo - if [[ -n "${FAILED_TESTS}" ]]; then - cecho "RED" "SOME TESTS FAILED! To debug:" - cecho "RED" "${FAILED_TESTS}" - exit 1 - else - cecho "GREEN" "All tests passed!" - exit 0 - fi -} - -trap "report_results; cd $(pwd -P)" EXIT -cd "$(dirname $0)" - -source ./initialize_test.sh - -setup_env_variables -echo "Building packages..." -build_packages -clean_test_root - -function gh_echo { - if [[ -z "${GITHUB_ACTIONS}" ]]; then - return - fi - echo "$@" -} - -FAILED_TESTS="" - -TOTAL=$(ls -1 test_*.sh | wc -l | tr -d ' ') -COUNTER=1 -for i in test_*.sh -do - set +e - cecho "YELLOW" "Running ${COUNTER}/${TOTAL} - $i..." - COUNTER=$(( COUNTER + 1 )) - OUTPUT=$(bash $i --multitest --no-build 2>&1) - RV=$? - set -e - if [[ "${RV}" != 0 ]]; then - FAILED_TESTS="${FAILED_TESTS}- ${i}\n" - - gh_echo "::group::FAILED - $i" - cecho "RED" "FAILED - $i" - echo "${OUTPUT}" - gh_echo "::endgroup::" - else - gh_echo "::group::PASSED - $i" - cecho "GREEN" "PASSED - $i" - gh_echo "${OUTPUT}" - gh_echo "::endgroup::" - fi -done - diff --git a/installation-tests/test_android_types.sh b/installation-tests/test_android_types.sh deleted file mode 100755 index 8194d2dc00..0000000000 --- a/installation-tests/test_android_types.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -source ./initialize_test.sh && initialize_test "$@" - -npm_i playwright-core -PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm_i playwright -npm i -D typescript@3.8 -npm i -D @types/node@14 -echo "import { AndroidDevice, _android, AndroidWebView, Page } from 'playwright';" > "test.ts" - -echo "Running tsc" -npx --yes -p typescript@3.7.5 tsc "test.ts" diff --git a/installation-tests/test_connect_to_selenium.sh b/installation-tests/test_connect_to_selenium.sh deleted file mode 100755 index 943050bc54..0000000000 --- a/installation-tests/test_connect_to_selenium.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -source ./initialize_test.sh && initialize_test "$@" - -npm_i playwright-core -node "./download-chromedriver.js" "${PWD}" -export PWTEST_CHROMEDRIVER="${PWD}/chromedriver" -cd "${PLAYWRIGHT_CHECKOUT}" -npm run test -- --reporter=list selenium.spec diff --git a/installation-tests/test_electron_types.sh b/installation-tests/test_electron_types.sh deleted file mode 100755 index 20b57cab03..0000000000 --- a/installation-tests/test_electron_types.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -source ./initialize_test.sh && initialize_test "$@" - -npm_i playwright-core -PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm_i playwright -npm i electron@12 -npm i -D typescript@3.8 -npm i -D @types/node@14 -echo "import { Page, _electron, ElectronApplication, Electron } from 'playwright';" > "test.ts" - -echo "Running tsc" -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 deleted file mode 100755 index a868a88b45..0000000000 --- a/installation-tests/test_npm_i_installs_local_package.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -source ./initialize_test.sh && initialize_test "$@" - - -PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm_i playwright{,-core,-webkit,-firefox,-chromium} -# test subshell installation -OUTPUT=$(npm_i --foreground-script @playwright/test) - -SCRIPT=$(cat < "${PKG_NAME}.ts" && npx --yes -p typescript@3.7.5 tsc "${PKG_NAME}.ts" -done; - -echo "Checking types of @playwright/test" -echo npx --yes -p typescript@3.7.5 tsc "playwright-test-types.ts" diff --git a/package.json b/package.json index 3459d27afc..077bfba52a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "wtest": "playwright test --config=tests/library/playwright.config.ts --project=webkit", "atest": "playwright test --config=tests/android/playwright.config.ts", "etest": "playwright test --config=tests/electron/playwright.config.ts", + "itest": "playwright test --config=tests/installation/playwright.config.ts", "test-html-reporter": "playwright test --config=packages/html-reporter", "test-web": "playwright test --config=packages/web", "ttest": "node ./tests/playwright-test/stable-test-runner/node_modules/@playwright/test/cli test --config=tests/playwright-test/playwright.config.ts", diff --git a/tests/installation/android-types.spec.ts b/tests/installation/android-types.spec.ts new file mode 100755 index 0000000000..e8e84004d1 --- /dev/null +++ b/tests/installation/android-types.spec.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('android types', async ({ exec, tsc, writeFiles }) => { + await exec('npm i --foreground-scripts playwright', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + await writeFiles({ + 'test.ts': + `import { AndroidDevice, _android, AndroidWebView, Page } from 'playwright';`, + }); + await tsc('test.ts'); +}); diff --git a/tests/installation/connect-to-selenium.spec.ts b/tests/installation/connect-to-selenium.spec.ts new file mode 100755 index 0000000000..df4866822e --- /dev/null +++ b/tests/installation/connect-to-selenium.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import path from 'path'; +import { test } from './npmTest'; + +test('connect to selenium', async ({ exec, tmpWorkspace }, testInfo) => { + await exec('npm i --foreground-scripts playwright-core'); + await exec(`node ./download-chromedriver.js ${path.join(tmpWorkspace)}`); + await exec(`npm run test -- --reporter=list selenium.spec --output=${testInfo.outputPath('tmp-test-results')}`, { cwd: path.join(__dirname, '..', '..'), env: { PWTEST_CHROMEDRIVER: path.join(tmpWorkspace, 'chromedriver') } }); +}); diff --git a/tests/installation/driver-should-work.spec.ts b/tests/installation/driver-should-work.spec.ts new file mode 100755 index 0000000000..d0024adb4b --- /dev/null +++ b/tests/installation/driver-should-work.spec.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('driver should work', async ({ exec }) => { + await exec('npm i --foreground-scripts playwright', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + await exec('npx playwright install'); + await exec('node driver-client.js'); +}); diff --git a/tests/installation/electron-types.spec.ts b/tests/installation/electron-types.spec.ts new file mode 100755 index 0000000000..a2257f9459 --- /dev/null +++ b/tests/installation/electron-types.spec.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('electron types', async ({ exec, tsc, writeFiles }) => { + await exec('npm i --foreground-scripts playwright electron@12', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + await writeFiles({ + 'test.ts': + `import { Page, _electron, ElectronApplication, Electron } from 'playwright';` + }); + await tsc('test.ts'); +}); diff --git a/tests/installation/expect.d.ts b/tests/installation/expect.d.ts new file mode 100644 index 0000000000..e8ccb161f7 --- /dev/null +++ b/tests/installation/expect.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ + +export {} + +declare global { + namespace PlaywrightTest { + interface Matchers { + toHaveLoggedSoftwareDownload(browsers: ("chromium" | "firefox" | "webkit" | "ffmpeg")[]): R; + } + } +} diff --git a/installation-tests/fixture-scripts/download-chromedriver.js b/tests/installation/fixture-scripts/download-chromedriver.js similarity index 100% rename from installation-tests/fixture-scripts/download-chromedriver.js rename to tests/installation/fixture-scripts/download-chromedriver.js diff --git a/installation-tests/fixture-scripts/driver-client.js b/tests/installation/fixture-scripts/driver-client.js similarity index 100% rename from installation-tests/fixture-scripts/driver-client.js rename to tests/installation/fixture-scripts/driver-client.js diff --git a/installation-tests/fixture-scripts/electron-app.js b/tests/installation/fixture-scripts/electron-app.js similarity index 100% rename from installation-tests/fixture-scripts/electron-app.js rename to tests/installation/fixture-scripts/electron-app.js diff --git a/installation-tests/fixture-scripts/esm-playwright-chromium.mjs b/tests/installation/fixture-scripts/esm-playwright-chromium.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm-playwright-chromium.mjs rename to tests/installation/fixture-scripts/esm-playwright-chromium.mjs diff --git a/installation-tests/fixture-scripts/esm-playwright-firefox.mjs b/tests/installation/fixture-scripts/esm-playwright-firefox.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm-playwright-firefox.mjs rename to tests/installation/fixture-scripts/esm-playwright-firefox.mjs diff --git a/installation-tests/fixture-scripts/esm-playwright-test.mjs b/tests/installation/fixture-scripts/esm-playwright-test.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm-playwright-test.mjs rename to tests/installation/fixture-scripts/esm-playwright-test.mjs diff --git a/installation-tests/fixture-scripts/esm-playwright-webkit.mjs b/tests/installation/fixture-scripts/esm-playwright-webkit.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm-playwright-webkit.mjs rename to tests/installation/fixture-scripts/esm-playwright-webkit.mjs diff --git a/installation-tests/fixture-scripts/esm-playwright.mjs b/tests/installation/fixture-scripts/esm-playwright.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm-playwright.mjs rename to tests/installation/fixture-scripts/esm-playwright.mjs diff --git a/installation-tests/fixture-scripts/esm.mjs b/tests/installation/fixture-scripts/esm.mjs similarity index 100% rename from installation-tests/fixture-scripts/esm.mjs rename to tests/installation/fixture-scripts/esm.mjs diff --git a/installation-tests/fixture-scripts/failing.spec.js b/tests/installation/fixture-scripts/failing.spec.js similarity index 100% rename from installation-tests/fixture-scripts/failing.spec.js rename to tests/installation/fixture-scripts/failing.spec.js diff --git a/installation-tests/fixture-scripts/inspector-custom-executable.js b/tests/installation/fixture-scripts/inspector-custom-executable.js similarity index 100% rename from installation-tests/fixture-scripts/inspector-custom-executable.js rename to tests/installation/fixture-scripts/inspector-custom-executable.js diff --git a/installation-tests/fixture-scripts/playwright-test-types.ts b/tests/installation/fixture-scripts/playwright-test-types.ts similarity index 100% rename from installation-tests/fixture-scripts/playwright-test-types.ts rename to tests/installation/fixture-scripts/playwright-test-types.ts diff --git a/installation-tests/fixture-scripts/read-json-report.js b/tests/installation/fixture-scripts/read-json-report.js similarity index 100% rename from installation-tests/fixture-scripts/read-json-report.js rename to tests/installation/fixture-scripts/read-json-report.js diff --git a/installation-tests/fixture-scripts/sample.spec.js b/tests/installation/fixture-scripts/sample.spec.js similarity index 100% rename from installation-tests/fixture-scripts/sample.spec.js rename to tests/installation/fixture-scripts/sample.spec.js diff --git a/installation-tests/fixture-scripts/sanity-electron.js b/tests/installation/fixture-scripts/sanity-electron.js similarity index 100% rename from installation-tests/fixture-scripts/sanity-electron.js rename to tests/installation/fixture-scripts/sanity-electron.js diff --git a/installation-tests/fixture-scripts/sanity.js b/tests/installation/fixture-scripts/sanity.js similarity index 100% rename from installation-tests/fixture-scripts/sanity.js rename to tests/installation/fixture-scripts/sanity.js diff --git a/installation-tests/fixture-scripts/screencast.js b/tests/installation/fixture-scripts/screencast.js similarity index 100% rename from installation-tests/fixture-scripts/screencast.js rename to tests/installation/fixture-scripts/screencast.js diff --git a/installation-tests/fixture-scripts/validate-dependencies-skip-executable-path.js b/tests/installation/fixture-scripts/validate-dependencies-skip-executable-path.js similarity index 100% rename from installation-tests/fixture-scripts/validate-dependencies-skip-executable-path.js rename to tests/installation/fixture-scripts/validate-dependencies-skip-executable-path.js diff --git a/installation-tests/fixture-scripts/validate-dependencies.js b/tests/installation/fixture-scripts/validate-dependencies.js similarity index 100% rename from installation-tests/fixture-scripts/validate-dependencies.js rename to tests/installation/fixture-scripts/validate-dependencies.js diff --git a/tests/installation/globalSetup.ts b/tests/installation/globalSetup.ts new file mode 100644 index 0000000000..3c84815980 --- /dev/null +++ b/tests/installation/globalSetup.ts @@ -0,0 +1,60 @@ +/** + * 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. + */ +import path from 'path'; +import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync'; +import { rimraf } from 'playwright-core/lib/utilsBundle'; +import { promisify } from 'util'; +import fs from 'fs'; + +const PACKAGE_BUILDER_SCRIPT = path.join(__dirname, '..', '..', 'utils', 'pack_package.js'); +const TMP_WORKSPACE = '/tmp/pwt/workspaces'; + +async function globalSetup() { + await promisify(rimraf)(TMP_WORKSPACE); + console.log(`Temporary workspaces will be created in ${TMP_WORKSPACE}. They will not be removed at the end. Set DEBUG=itest to determine which sub-dir a specific test is using.`); + await fs.promises.mkdir(TMP_WORKSPACE, { recursive: true }); + if (process.env.PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS) { + console.log('Skipped building packages. Unset PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS to build packages.'); + return; + } + + console.log('Building packages. Set PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS to skip.'); + const outputDir = path.join(__dirname, 'output'); + await promisify(rimraf)(outputDir); + await fs.promises.mkdir(outputDir, { recursive: true }); + + const build = async (buildTarget: string, pkgNameOverride?: string) => { + const outPath = path.resolve(path.join(outputDir, `${buildTarget}.tgz`)); + const { code, stderr, stdout } = await spawnAsync('node', [PACKAGE_BUILDER_SCRIPT, buildTarget, outPath]); + if (!!code) + throw new Error(`Failed to build: ${buildTarget}:\n${stderr}\n${stdout}`); + console.log('Built:', pkgNameOverride || buildTarget); + return [pkgNameOverride || buildTarget, outPath]; + }; + + const builds = await Promise.all([ + build('playwright-core'), + build('playwright-test', '@playwright/test'), + build('playwright'), + build('playwright-chromium'), + build('playwright-firefox'), + build('playwright-webkit'), + ]); + + await fs.promises.writeFile(path.join(__dirname, './.registry.json'), JSON.stringify(Object.fromEntries(builds))); +} + +export default globalSetup; diff --git a/tests/installation/npm-installs-local-packages.spec.ts b/tests/installation/npm-installs-local-packages.spec.ts new file mode 100755 index 0000000000..f40793b5e8 --- /dev/null +++ b/tests/installation/npm-installs-local-packages.spec.ts @@ -0,0 +1,36 @@ +/** + * 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. + */ +import path from 'path'; +import { test, expect } from './npmTest'; +import fs from 'fs'; + +test('installs local packages', async ({ registry, exec, tmpWorkspace }) => { + const packages = ['playwright', 'playwright-core', 'playwright-chromium', 'playwright-firefox', 'playwright-webkit', '@playwright/test']; + await exec('npm i --foreground-scripts', ...packages, { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + + const output = await exec('node', path.join(__dirname, '..', '..', 'utils', 'workspace.js'), '--get-version'); + const expectedPlaywrightVersion = output.trim(); + for (const pkg of packages) { + await test.step(`check version and installation location of ${pkg}`, async () => { + registry.assertLocalPackage(pkg); + const result = await exec('node', `--eval='console.log(JSON.stringify(require.resolve("${pkg}")))'`); + const pkgJsonPath = path.join(path.dirname(JSON.parse(result)), 'package.json'); + expect(pkgJsonPath.startsWith(path.join(tmpWorkspace, 'node_modules'))).toBeTruthy(); + const installedVersion = JSON.parse(await fs.promises.readFile(pkgJsonPath, 'utf8')).version; + expect(installedVersion).toBe(expectedPlaywrightVersion); + }); + } +}); diff --git a/tests/installation/npmTest.ts b/tests/installation/npmTest.ts new file mode 100644 index 0000000000..8970459640 --- /dev/null +++ b/tests/installation/npmTest.ts @@ -0,0 +1,172 @@ +/** + * 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. + */ + +// eslint-disable-next-line spaced-comment +/// + +import { test as _test, expect as _expect } from '@playwright/test'; +import fs from 'fs'; +import path from 'path'; +import debugLogger from 'debug'; +import { Registry } from './registry'; +import { spawnAsync } from './spawnAsync'; + +const debug = debugLogger('itest'); + +/** + * A minimal NPM Registry Server that can serve local packages, or proxy to the upstream registry. + * This is useful in test installation behavior of packages that aren't yet published. It's particularly helpful + * when your installation requires transitive dependencies that are also not yet published. + * + * See https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md for information on the offical APIs. + */ + +_expect.extend({ + toHaveLoggedSoftwareDownload(received: any, browsers: ('chromium' | 'firefox' | 'webkit' | 'ffmpeg')[]) { + if (typeof received !== 'string') + throw new Error(`Expected argument to be a string.`); + + const downloaded = new Set(); + for (const [, browser] of received.matchAll(/^.*(chromium|firefox|webkit|ffmpeg) v\d+ downloaded.*$/img)) + downloaded.add(browser); + + const expected = browsers; + if (expected.length === downloaded.size && expected.every(browser => downloaded.has(browser))) + return { pass: true }; + return { + pass: false, + message: () => [ + `Browser download expectation failed!`, + ` expected: ${[...expected].sort().join(', ')}`, + ` actual: ${[...downloaded].sort().join(', ')}`, + ].join('\n'), + }; + } +}); + +const expect = _expect; + +export type ExecOptions = { cwd?: string, env?: Record, message?: string, expectToExitWithError?: boolean }; +export type ArgsOrOptions = [] | [...string[]] | [...string[], ExecOptions] | [ExecOptions]; +export const test = _test.extend<{ + _autoCopyScripts: void, + tmpWorkspace: string, + nodeMajorVersion: number, + installedSoftwareOnDisk: (registryPath?: string) => Promise; + writeFiles: (nameToContents: Record) => Promise, + exec: (cmd: string, ...argsAndOrOptions: ArgsOrOptions) => Promise + tsc: (...argsAndOrOptions: ArgsOrOptions) => Promise, + registry: Registry, + }>({ + _autoCopyScripts: [async ({ tmpWorkspace }, use) => { + const dstDir = path.join(tmpWorkspace); + const sourceDir = path.join(__dirname, 'fixture-scripts'); + const contents = await fs.promises.readdir(sourceDir); + await Promise.all(contents.map(f => fs.promises.copyFile(path.join(sourceDir, f), path.join(dstDir, f)))); + await use(); + }, { + auto: true, + }], + nodeMajorVersion: async ({}, use) => { + await use(+process.versions.node.split('.')[0]); + }, + writeFiles: async ({ tmpWorkspace }, use) => { + await use(async (nameToContents: Record) => { + for (const [name, contents] of Object.entries(nameToContents)) + await fs.promises.writeFile(path.join(tmpWorkspace, name), contents); + }); + }, + tmpWorkspace: async ({}, use) => { + // We want a location that won't have a node_modules dir anywhere along its path + const tmpWorkspace = path.join('/tmp/pwt/workspaces', path.basename(test.info().outputDir)); + await fs.promises.mkdir(tmpWorkspace); + debug(`Workspace Folder: ${tmpWorkspace}`); + await spawnAsync('npm', ['init', '-y'], { + cwd: tmpWorkspace, + }); + + await use(tmpWorkspace); + }, + registry: async ({}, use, testInfo) => { + const port = testInfo.workerIndex + 16123; + const url = `http://127.0.0.1:${port}`; + const registry = new Registry(testInfo.outputPath('registry'), url); + await registry.start(JSON.parse((await fs.promises.readFile(path.join(__dirname, './.registry.json'), 'utf8')))); + await use(registry); + await registry.shutdown(); + }, + installedSoftwareOnDisk: async ({ tmpWorkspace }, use) => { + await use(async (registryPath?: string) => fs.promises.readdir(registryPath || path.join(tmpWorkspace, 'browsers')).catch(() => []).then(files => files.map(f => f.split('-')[0]).filter(f => !f.startsWith('.')))); + }, + exec: async ({ registry, tmpWorkspace }, use, testInfo) => { + await use(async (cmd: string, ...argsAndOrOptions: [] | [...string[]] | [...string[], ExecOptions] | [ExecOptions]) => { + let args: string[] = []; + let options: ExecOptions = {}; + if (typeof argsAndOrOptions[argsAndOrOptions.length - 1] === 'object') + options = argsAndOrOptions.pop() as ExecOptions; + + args = argsAndOrOptions as string[]; + + let result!: Awaited>; + await test.step(`exec: ${[cmd, ...args].join(' ')}`, async () => { + result = await spawnAsync(cmd, args, { + shell: true, + cwd: options.cwd ?? tmpWorkspace, + // NB: We end up running npm-in-npm, so it's important that we do NOT forward process.env and instead cherry-pick environment variables. + env: { + 'PATH': process.env.PATH, + 'DISPLAY': process.env.DISPLAY, + 'XAUTHORITY': process.env.XAUTHORITY, + 'PLAYWRIGHT_BROWSERS_PATH': path.join(tmpWorkspace, 'browsers'), + 'npm_config_cache': testInfo.outputPath('npm_cache'), + 'npm_config_registry': registry.url(), + 'npm_config_prefix': testInfo.outputPath('npm_global'), + ...options.env, + } + }); + }); + + const stdio = `${result.stdout}\n${result.stderr}`; + + await testInfo.attach(`${[cmd, ...args].join(' ')}`, { body: `COMMAND: ${[cmd, ...args].join(' ')}\n\nEXIT CODE: ${result.code}\n\n====== STDIO + STDERR ======\n\n${stdio}` }); + + // This means something is really off with spawn + if (result.error) + throw result.error; + + // User expects command to fail + if (options.expectToExitWithError) { + if (result.code === 0) { + const message = options.message ? ` Message: ${options.message}` : ''; + throw new Error(`Expected the command to exit with an error, but exited cleanly.${message}`); + } + } else if (result.code !== 0) { + const message = options.message ? ` Message: ${options.message}` : ''; + throw new Error(`Expected the command to exit cleanly (0 status code), but exited with ${result.code}.${message}`); + } + + return stdio; + + }); + }, + tsc: async ({ exec }, use) => { + await exec('npm i --foreground-scripts typescript@3.8 @types/node@14'); + await use((...args: ArgsOrOptions) => exec('npx', '-p', 'typescript@3.8', 'tsc', ...args)); + }, + }); + + +export { expect }; diff --git a/tests/installation/npx-global-help.spec.ts b/tests/installation/npx-global-help.spec.ts new file mode 100755 index 0000000000..154104d420 --- /dev/null +++ b/tests/installation/npx-global-help.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('npx playwright --help should not download browsers', async ({ exec, installedSoftwareOnDisk }) => { + const result = await exec('npx playwright --help'); + expect(result).toHaveLoggedSoftwareDownload([]); + expect(await installedSoftwareOnDisk()).toEqual([]); + expect(result).not.toContain(`To avoid unexpected behavior, please install your dependencies first`); +}); diff --git a/tests/installation/npx-global-install.spec.ts b/tests/installation/npx-global-install.spec.ts new file mode 100755 index 0000000000..1a44b41f07 --- /dev/null +++ b/tests/installation/npx-global-install.spec.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('npx playwright install global', async ({ exec, installedSoftwareOnDisk }) => { + const result = await exec('npx playwright install'); + expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', 'firefox', 'webkit']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']); + expect(result).not.toContain(`Please run the following command to download new browsers`); + expect(result).toContain(`To avoid unexpected behavior, please install your dependencies first`); +}); diff --git a/tests/installation/npx-global-spec-codegen.spec.ts b/tests/installation/npx-global-spec-codegen.spec.ts new file mode 100755 index 0000000000..0bf1ccc151 --- /dev/null +++ b/tests/installation/npx-global-spec-codegen.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('npx playwright codegen', async ({ exec, installedSoftwareOnDisk }) => { + const stdio = await exec('npx playwright codegen', { expectToExitWithError: true }); + expect(stdio).toHaveLoggedSoftwareDownload([]); + expect(await installedSoftwareOnDisk()).toEqual([]); + expect(stdio).toContain(`Please run the following command to download new browsers`); +}); diff --git a/tests/installation/playwright-cli-codegen.spec.ts b/tests/installation/playwright-cli-codegen.spec.ts new file mode 100755 index 0000000000..7562f81809 --- /dev/null +++ b/tests/installation/playwright-cli-codegen.spec.ts @@ -0,0 +1,38 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('codegen should work', async ({ exec }) => { + await exec('npm i --foreground-scripts playwright'); + + await test.step('codegen without arguments', async () => { + const result = await exec('npx playwright codegen', { env: { PWTEST_CLI_EXIT: '1' } }); + expect(result).toContain(`@playwright/test`); + expect(result).toContain(`{ page }`); + }); + + await test.step('codegen --target=javascript', async () => { + const result = await exec('npx playwright codegen --target=javascript', { env: { PWTEST_CLI_EXIT: '1' } }); + expect(result).toContain(`playwright`); + expect(result).toContain(`page.close`); + }); + + await test.step('codegen --target=python', async () => { + const result = await exec('npx playwright codegen --target=python', { env: { PWTEST_CLI_EXIT: '1' } }); + expect(result).toContain(`chromium.launch`); + expect(result).toContain(`browser.close`); + }); +}); diff --git a/tests/installation/playwright-cli-install-should-work.spec.ts b/tests/installation/playwright-cli-install-should-work.spec.ts new file mode 100755 index 0000000000..1a348e193f --- /dev/null +++ b/tests/installation/playwright-cli-install-should-work.spec.ts @@ -0,0 +1,35 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('codegen should work', async ({ exec, installedSoftwareOnDisk }) => { + await exec('npm i --foreground-scripts playwright', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + + await test.step('playwright install chromium', async () => { + const result = await exec('npx playwright install chromium'); + expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg']); + }); + + await test.step('playwright install', async () => { + const result = await exec('npx playwright install'); + expect(result).toHaveLoggedSoftwareDownload(['firefox', 'webkit']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']); + }); + + await exec('node sanity.js playwright none', { env: { PLAYWRIGHT_BROWSERS_PATH: undefined } }); + await exec('node sanity.js playwright'); +}); diff --git a/tests/installation/playwright-cli-screenshot-should-work.spec.ts b/tests/installation/playwright-cli-screenshot-should-work.spec.ts new file mode 100755 index 0000000000..7a448a101a --- /dev/null +++ b/tests/installation/playwright-cli-screenshot-should-work.spec.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import { test } from './npmTest'; +import fs from 'fs'; +import path from 'path'; + +test('playwright cli screenshot should work', async ({ exec, tmpWorkspace }) => { + await exec('npm i --foreground-scripts playwright'); + await exec('./node_modules/.bin/playwright screenshot about:blank one.png'); + await fs.promises.stat(path.join(tmpWorkspace, 'one.png')); + + await exec('npx playwright screenshot about:blank two.png'); + await fs.promises.stat(path.join(tmpWorkspace, 'two.png')); +}); diff --git a/tests/installation/playwright-electron-should-work.spec.ts b/tests/installation/playwright-electron-should-work.spec.ts new file mode 100755 index 0000000000..085389ccb5 --- /dev/null +++ b/tests/installation/playwright-electron-should-work.spec.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('electron should work', async ({ exec }) => { + await exec('npm i --foreground-scripts playwright electron@9.0', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + await exec('node sanity-electron.js'); +}); diff --git a/tests/installation/playwright-global-installation-cross-package.spec.ts b/tests/installation/playwright-global-installation-cross-package.spec.ts new file mode 100755 index 0000000000..723a11132d --- /dev/null +++ b/tests/installation/playwright-global-installation-cross-package.spec.ts @@ -0,0 +1,28 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('global installation cross package', async ({ exec, installedSoftwareOnDisk }) => { + const packages = ['playwright-chromium', 'playwright-firefox', 'playwright-webkit']; + for (const pkg of packages) + await exec('npm i --foreground-scripts', pkg, { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + const result = await exec('npm i --foreground-scripts playwright'); + expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', 'firefox', 'webkit']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']); + + for (const pkg of packages) + await test.step(pkg, () => exec('node ./sanity.js', pkg, 'all')); +}); diff --git a/tests/installation/playwright-global-installation.spec.ts b/tests/installation/playwright-global-installation.spec.ts new file mode 100755 index 0000000000..b8805e8128 --- /dev/null +++ b/tests/installation/playwright-global-installation.spec.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('global installation', async ({ exec, installedSoftwareOnDisk }) => { + const result = await exec('npm i --foreground-scripts playwright'); + expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', 'firefox', 'webkit']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']); + await exec('node sanity.js playwright none', { env: { PLAYWRIGHT_BROWSERS_PATH: undefined } }); + await exec('node ./sanity.js playwright'); +}); diff --git a/tests/installation/playwright-global-subsequent-installs.spec.ts b/tests/installation/playwright-global-subsequent-installs.spec.ts new file mode 100755 index 0000000000..82354a595c --- /dev/null +++ b/tests/installation/playwright-global-subsequent-installs.spec.ts @@ -0,0 +1,28 @@ +/** + * 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. + */ +import { test } from './npmTest'; +import path from 'path'; + +test('subsequent installs works', async ({ exec }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/1651' }); + await exec('npm i --foreground-scripts playwright'); + // Note: the `npm install` would not actually crash, the error + // is merely logged to the console. To reproduce the error, we should make + // sure that script's install.js can be run subsequently without unhandled promise rejections. + // Note: the flag `--unhandled-rejections=strict` will force node to terminate in case + // of UnhandledPromiseRejection. + await exec('node --unhandled-rejections=strict', path.join('./node_modules', 'playwright', 'install.js')); +}); diff --git a/tests/installation/playwright-should-work-with-relative-browsers-path.spec.ts b/tests/installation/playwright-should-work-with-relative-browsers-path.spec.ts new file mode 100755 index 0000000000..7d63f88c1d --- /dev/null +++ b/tests/installation/playwright-should-work-with-relative-browsers-path.spec.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import { test } from './npmTest'; +import fs from 'fs'; +import path from 'path'; + +test('playwright should work with relative home path', async ({ exec, tmpWorkspace }) => { + await fs.promises.mkdir(path.join(tmpWorkspace, 'foo')); + // Make sure that browsers path is resolved relative to the `npm install` call location. + await exec('npm i --foreground-scripts playwright', { cwd: path.join(tmpWorkspace, 'foo'), env: { PLAYWRIGHT_BROWSERS_PATH: '../relative' } }); + await exec('node sanity.js playwright', { env: { PLAYWRIGHT_BROWSERS_PATH: './relative' } }); +}); diff --git a/tests/installation/playwright-should-work-with-relative-home-path.spec.ts b/tests/installation/playwright-should-work-with-relative-home-path.spec.ts new file mode 100755 index 0000000000..a19b51b4b2 --- /dev/null +++ b/tests/installation/playwright-should-work-with-relative-home-path.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('playwright should work with relative home path', async ({ exec }) => { + const env = { PLAYWRIGHT_BROWSERS_PATH: '0', HOME: '.' }; + await exec('npm i --foreground-scripts playwright', { env }); + // Firefox does not work with relative HOME. + await exec('node sanity.js playwright chromium webkit', { env }); +}); diff --git a/tests/installation/playwright-should-work.spec.ts b/tests/installation/playwright-should-work.spec.ts new file mode 100755 index 0000000000..eb3ff4fb3c --- /dev/null +++ b/tests/installation/playwright-should-work.spec.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test(`playwright should work`, async ({ exec, nodeMajorVersion, installedSoftwareOnDisk }) => { + const result = await exec('npm i --foreground-scripts playwright'); + expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', 'firefox', 'webkit']); + expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']); + await exec('node ./sanity.js playwright'); + if (nodeMajorVersion >= 14) + await exec('node esm-playwright.mjs'); + const stdio = await exec('npx playwright', 'test', '-c', '.', { expectToExitWithError: true }); + expect(stdio).toContain(`Please install @playwright/test package to use Playwright Test.`); +}); diff --git a/tests/installation/playwright-test-should-work.spec.ts b/tests/installation/playwright-test-should-work.spec.ts new file mode 100755 index 0000000000..1e64d4a241 --- /dev/null +++ b/tests/installation/playwright-test-should-work.spec.ts @@ -0,0 +1,28 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('@playwright/test should work', async ({ exec, nodeMajorVersion }) => { + await exec('npm i --foreground-scripts @playwright/test'); + await exec('npx playwright test -c .', { expectToExitWithError: true, message: 'should not be able to run tests without installing browsers' }); + + await exec('npx playwright install'); + await exec('npx playwright test -c . --browser=all --reporter=list,json sample.spec.js', { env: { PLAYWRIGHT_JSON_OUTPUT_NAME: 'report.json' } }); + await exec('node ./read-json-report.js ./report.json'); + await exec('node sanity.js @playwright/test'); + if (nodeMajorVersion >= 14) + await exec('node', 'esm-playwright-test.mjs'); +}); diff --git a/tests/installation/playwright-test-stacks-should-work.spec.ts b/tests/installation/playwright-test-stacks-should-work.spec.ts new file mode 100755 index 0000000000..50b001ca96 --- /dev/null +++ b/tests/installation/playwright-test-stacks-should-work.spec.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('@playwright/test stacks should work', async ({ exec }) => { + await exec('npm i --foreground-scripts @playwright/test'); + await exec('npx playwright install chromium'); + const output = await exec('npx playwright test -c . failing.spec.js', { expectToExitWithError: true, env: { DEBUG: 'pw:api' } }); + expect(output).toContain('expect.toHaveText started'); + expect(output).toContain('failing.spec.js:5:38'); +}); diff --git a/tests/installation/playwright-xyz-should-work.spec.ts b/tests/installation/playwright-xyz-should-work.spec.ts new file mode 100755 index 0000000000..945413f577 --- /dev/null +++ b/tests/installation/playwright-xyz-should-work.spec.ts @@ -0,0 +1,33 @@ + +/** + * 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. + */ +import { test, expect } from './npmTest'; + +for (const pkg of ['playwright-chromium', 'playwright-firefox', 'playwright-webkit']) { + test(`${pkg} should work`, async ({ exec, nodeMajorVersion, installedSoftwareOnDisk }) => { + const result = await exec('npm i --foreground-scripts', pkg); + const browserName = pkg.split('-')[1]; + const expectedSoftware = [browserName]; + if (browserName === 'chromium') + expectedSoftware.push('ffmpeg'); + expect(result).toHaveLoggedSoftwareDownload(expectedSoftware as any); + expect(await installedSoftwareOnDisk()).toEqual(expectedSoftware); + expect(result).not.toContain(`To avoid unexpected behavior, please install your dependencies first`); + await exec('node ./sanity.js', pkg); + if (nodeMajorVersion >= 14) + await exec('node', `esm-${pkg}.mjs`); + }); +} diff --git a/tests/installation/playwright.config.ts b/tests/installation/playwright.config.ts new file mode 100644 index 0000000000..78217d407a --- /dev/null +++ b/tests/installation/playwright.config.ts @@ -0,0 +1,32 @@ +/** + * 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. + */ + +import path from 'path'; +import type { PlaywrightTestConfig } from '@playwright/test'; +import { config as loadEnv } from 'dotenv'; +loadEnv({ path: path.join(__dirname, '..', '..', '.env') }); + +const config: PlaywrightTestConfig = { + testIgnore: '**\/fixture-scripts/**', + globalSetup: path.join(__dirname, 'globalSetup'), + timeout: 5 * 60 * 1000, + retries: 0, + reporter: process.env.CI ? 'dot' : [['list'], ['html', { open: 'on-failure' }]], + forbidOnly: !!process.env.CI, + workers: 1, +}; + +export default config; diff --git a/tests/installation/registry.ts b/tests/installation/registry.ts new file mode 100644 index 0000000000..363f671171 --- /dev/null +++ b/tests/installation/registry.ts @@ -0,0 +1,184 @@ +/** + * 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. + */ +import crypto from 'crypto'; +import fs from 'fs'; +import type { Server } from 'http'; +import http from 'http'; +import https from 'https'; +import path from 'path'; +import { spawnAsync } from './spawnAsync'; + +const kPublicNpmRegistry = 'https://registry.npmjs.org'; +const kContentTypeAbbreviatedMetadata = 'application/vnd.npm.install-v1+json'; + +export class Registry { + private _workDir: string; + private _url: string; + private _objectsDir: string; + private _packageMeta: Map = new Map(); + private _log: { pkg: string, status: 'PROXIED' | 'LOCAL', type?: 'tar' | 'metadata' }[] = []; + private _server: Server; + + constructor(workDir: string, url: string) { + this._workDir = workDir; + this._objectsDir = path.join(this._workDir); + this._url = url; + } + + url() { return this._url; } + + async shutdown() { + return new Promise((res, rej) => this._server.close(err => err ? rej(err) : res())); + } + + async start(packages: { [pkg: string]: string }) { + await fs.promises.mkdir(this._workDir, { recursive: true }); + await fs.promises.mkdir(this._objectsDir, { recursive: true }); + + await Promise.all(Object.entries(packages).map(([pkg, tar]) => this._addPackage(pkg, tar))); + + this._server = http.createServer(async (req, res) => { + // 1. Only support GET requests + if (req.method !== 'GET') + return res.writeHead(405).end(); + + // 2. Determine what package is being asked for. + // The paths we can handle look like: + // - //*/ + // - //* + // - / + const url = new URL(req.url, kPublicNpmRegistry); + let [/* empty */, userSuppliedPackageName, /* empty */, 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) { // Tarball request. + if (path.basename(objectPath) !== userSuppliedTarName) { + res.writeHead(404).end(); + return; + } + this._logAccess({ status: 'LOCAL', type: 'tar', pkg: userSuppliedPackageName }); + const fileStream = fs.createReadStream(objectPath); + fileStream.pipe(res, { end: true }); + fileStream.on('error', console.log); + res.on('error', console.log); + return; + } else { // Metadata request. + this._logAccess({ status: 'LOCAL', type: 'metadata', pkg: userSuppliedPackageName }); + res.setHeader('content-type', kContentTypeAbbreviatedMetadata); + res.write(JSON.stringify(metadata, null, ' ')); + res.end(); + } + } else { // Fall through to official registry + this._logAccess({ status: 'PROXIED', pkg: 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', err => console.log(`error: `, err)); + fromNpm.pipe(client.res, { end: true }); + }); + client.req.pipe(toNpm); + client.req.on('error', err => console.log(`error: `, err)); + } + }); + + this._server.listen(Number.parseInt(new URL(this._url).port, 10), 'localhost'); + await new Promise((res, rej) => { + this._server.on('listening', () => res()); + this._server.on('error', rej); + }); + } + + public assertLocalPackage(pkg) { + const summary = this._log.reduce((acc, f) => { + if (f.pkg === pkg) { + acc.local = f.status === 'LOCAL' || acc.local; + acc.proxied = f.status === 'PROXIED' || acc.proxied; + } + + return acc; + }, { local: false, proxied: false }); + + if (summary.local && !summary.proxied) + return; + + throw new Error(`${pkg} was not accessed strictly locally: local: ${summary.local}, proxied: ${summary.proxied}`); + } + + private async _addPackage(pkg: string, tarPath: string) { + const tmpDir = await fs.promises.mkdtemp(path.join(this._workDir, '.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 now = new Date().toISOString(); + const shasum = crypto.createHash('sha1').update(await fs.promises.readFile(tarPath)).digest().toString('hex'); + const tarball = new URL(this._url); + tarball.pathname = `${tarball.pathname}${tarball.pathname.endsWith('/') ? '' : '/'}${encodeURIComponent(pkg)}/-/${shasum}.tgz`; + const metadata = { + 'dist-tags': { + latest: packageJson.version, + [packageJson.version]: packageJson.version, + }, + 'modified': now, + '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 || [], + scripts: packageJson.scripts || {}, + dist: { + tarball: tarball.toString(), + shasum, + }, + engines: packageJson.engines || {}, + }, + }, + }; + + const object = path.join(this._objectsDir, `${shasum}.tgz`); + await fs.promises.copyFile(tarPath, object); + this._packageMeta.set(pkg, [metadata, object]); + } + + private _logAccess(info: {status: 'PROXIED' | 'LOCAL', pkg: string, type?: 'tar' | 'metadata'}) { + this._log.push(info); + } +} diff --git a/tests/installation/screencast.spec.ts b/tests/installation/screencast.spec.ts new file mode 100755 index 0000000000..84060ed535 --- /dev/null +++ b/tests/installation/screencast.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('screencast works', async ({ exec }) => { + const packages = ['playwright', 'playwright-chromium', 'playwright-firefox', 'playwright-webkit']; + await exec('npm i --foreground-scripts', ...packages); + for (const pkg of packages) + await test.step(pkg, () => exec('node ./screencast.js', pkg)); +}); diff --git a/tests/installation/skip-browser-download-inspect-with-custom-executable.spec.ts b/tests/installation/skip-browser-download-inspect-with-custom-executable.spec.ts new file mode 100755 index 0000000000..9d08416698 --- /dev/null +++ b/tests/installation/skip-browser-download-inspect-with-custom-executable.spec.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('should skip download', async ({ exec }) => { + const installOutput = await exec('npm i --foreground-scripts playwright', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + expect(installOutput).toContain('Skipping browsers download because'); + if (process.platform === 'linux') { + const output = await exec('node inspector-custom-executable.js', { env: { PWDEBUG: '1' } }); + expect(output).toContain('SUCCESS'); + } +}); diff --git a/tests/installation/skip-browser-download.spec.ts b/tests/installation/skip-browser-download.spec.ts new file mode 100755 index 0000000000..715e5fd5d9 --- /dev/null +++ b/tests/installation/skip-browser-download.spec.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD should skip browser installs', async ({ exec, installedSoftwareOnDisk }) => { + const result = await exec('npm i --foreground-scripts playwright', { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + expect(result).toHaveLoggedSoftwareDownload([]); + expect(await installedSoftwareOnDisk()).toEqual([]); + expect(result).toContain(`Skipping browsers download because`); +}); diff --git a/tests/installation/spawnAsync.ts b/tests/installation/spawnAsync.ts new file mode 100644 index 0000000000..f1009c0f28 --- /dev/null +++ b/tests/installation/spawnAsync.ts @@ -0,0 +1,48 @@ +/** + * 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. + */ +import type { SpawnOptions } from 'child_process'; +import { spawn } from 'child_process'; +import debugLogger from 'debug'; + +const debugExec = debugLogger('itest:exec'); +const debugExecStdout = debugLogger('itest:exec:stdout'); +const debugExecStderr = debugLogger('itest:exec:stderr'); + +export function spawnAsync(cmd: string, args: string[], options: SpawnOptions = {}): Promise<{stdout: string, stderr: string, code: number | null, error?: Error}> { + // debugExec(`CWD: ${options.cwd || process.cwd()}`); + // debugExec(`ENV: ${Object.entries(options.env || {}).map(([key, value]) => `${key}=${value}`).join(' ')}`); + debugExec([cmd, ...args].join(' ')); + const p = spawn(cmd, args, Object.assign({ windowsHide: true }, options)); + + return new Promise(resolve => { + let stdout = ''; + let stderr = ''; + if (p.stdout) { + p.stdout.on('data', data => { + debugExecStdout(data.toString()); + stdout += data; + }); + } + if (p.stderr) { + p.stderr.on('data', data => { + debugExecStderr(data.toString()); + stderr += data; + }); + } + p.on('close', code => resolve({ stdout, stderr, code })); + p.on('error', error => resolve({ stdout, stderr, code: 0, error })); + }); +} diff --git a/tests/installation/typescript-types.spec.ts b/tests/installation/typescript-types.spec.ts new file mode 100755 index 0000000000..5db62d2f80 --- /dev/null +++ b/tests/installation/typescript-types.spec.ts @@ -0,0 +1,37 @@ +/** + * 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. + */ +import { test } from './npmTest'; + +test('typescript types should work', async ({ exec, tsc, writeFiles }) => { + const libraryPackages = [ + 'playwright', + 'playwright-core', + 'playwright-firefox', + 'playwright-webkit', + 'playwright-chromium', + ]; + await exec('npm i @playwright/test', ...libraryPackages, { env: { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' } }); + + for (const libraryPackage of libraryPackages) { + const filename = libraryPackage + '.ts'; + await writeFiles({ + [filename]: `import { Page } from '${libraryPackage}';`, + }); + await tsc(filename); + } + + await tsc('playwright-test-types.ts'); +}); diff --git a/tests/installation/validate-dependencies.spec.ts b/tests/installation/validate-dependencies.spec.ts new file mode 100644 index 0000000000..9ca7e71af1 --- /dev/null +++ b/tests/installation/validate-dependencies.spec.ts @@ -0,0 +1,30 @@ +/** + * 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. + */ +import { test, expect } from './npmTest'; + +test.describe('validate dependencies', () => { + test('default (on)', async ({ exec }) => { + await exec('npm i --foreground-scripts playwright'); + const result = await exec('node ./validate-dependencies.js'); + expect(result).toContain(`PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS`); + }); + + test('disabled (off)', async ({ exec }) => { + await exec('npm i --foreground-scripts playwright'); + const result = await exec('node ./validate-dependencies-skip-executable-path.js'); + expect(result).not.toContain(`PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS`); + }); +});