From d25cc5cd8ab6f57e8ef18ea2fd6936c1408665d4 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Mon, 19 Sep 2022 14:13:05 -0400 Subject: [PATCH] Revert "feat: introduce docker integration (#16382)" This reverts commit af042beb13d13872b9d4bc64758cb47a6554d1bd. --- docs/src/docker.md | 47 --- packages/playwright-core/package.json | 1 - packages/playwright-core/src/cli/cli.ts | 2 +- packages/playwright-test/src/DEPS.list | 1 - packages/playwright-test/src/cli.ts | 90 +----- .../src/docker/build_docker_image.sh | 74 ----- packages/playwright-test/src/docker/docker.ts | 303 ------------------ packages/playwright-test/src/index.ts | 7 +- tests/installation/docker-integration.spec.ts | 130 -------- .../fixture-scripts/docker.spec.js | 19 -- tests/installation/globalSetup.ts | 63 ++-- utils/build/build.js | 2 +- utils/docker/Dockerfile.focal | 5 +- 13 files changed, 37 insertions(+), 707 deletions(-) delete mode 100644 packages/playwright-test/src/docker/build_docker_image.sh delete mode 100644 packages/playwright-test/src/docker/docker.ts delete mode 100755 tests/installation/docker-integration.spec.ts delete mode 100644 tests/installation/fixture-scripts/docker.spec.js diff --git a/docs/src/docker.md b/docs/src/docker.md index 7d4a4a43b4..dee119bcb4 100644 --- a/docs/src/docker.md +++ b/docs/src/docker.md @@ -145,50 +145,3 @@ The image will be tagged as `playwright:localbuild-focal` and could be run as: ``` docker run --rm -it playwright:localbuild /bin/bash ``` - -## (Experimental) Playwright Test Docker Integration -* langs: js - -Playwright Test now ships an **experimental** Docker integration. -With this integration, **only** browser binaries are running inside a Docker container, -while all the code is still running on the host operating system. - -Docker container provides a consistent environment, eliminating browser rendering -differences across platforms. Playwright Test will automatically proxy host network traffic -into the container, so browsers can access servers running on the host. - -:::note -Docker integration requires Docker installed & running on your computer. -See https://docs.docker.com/get-docker/ -::: - -:::note -If you use [Docker Desktop](https://www.docker.com/products/docker-desktop/), make sure to increase -default CPU and mem limit for better performance. -::: - -Docker integration usage: - -1. Build a local Docker image that will be used to run containers. This step - needs to be done only once. - ```bash js - npx playwright docker build - ``` - -2. Run Docker container in the background. - ```bash js - npx playwright docker start - ``` - -3. Run tests inside Docker container. Note that this command accepts all the same arguments - as a regular `npx playwright test` command. - ```bash js - npx playwright docker test - ``` - - Note that this command will detect running Docker container, and auto-launch it if needed. - -4. Finally, stop Docker container when it is no longer needed. - ```bash js - npx playwright docker stop - ``` diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 425bed4eff..9e1a6f4d90 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -23,7 +23,6 @@ "./lib/grid/gridServer": "./lib/grid/gridServer.js", "./lib/outofprocess": "./lib/outofprocess.js", "./lib/utils": "./lib/utils/index.js", - "./lib/common/userAgent": "./lib/common/userAgent.js", "./lib/utils/comparators": "./lib/utils/comparators.js", "./lib/utils/eventsHelper": "./lib/utils/eventsHelper.js", "./lib/utils/fileUtils": "./lib/utils/fileUtils.js", diff --git a/packages/playwright-core/src/cli/cli.ts b/packages/playwright-core/src/cli/cli.ts index ed315e4506..fe7486bc55 100755 --- a/packages/playwright-core/src/cli/cli.ts +++ b/packages/playwright-core/src/cli/cli.ts @@ -291,7 +291,7 @@ program program .command('show-trace [trace...]') .option('-b, --browser ', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium') - .description('show trace viewer') + .description('Show trace viewer') .action(function(traces, options) { if (options.browser === 'cr') options.browser = 'chromium'; diff --git a/packages/playwright-test/src/DEPS.list b/packages/playwright-test/src/DEPS.list index fa00ac451a..c6fa7ed79c 100644 --- a/packages/playwright-test/src/DEPS.list +++ b/packages/playwright-test/src/DEPS.list @@ -1,6 +1,5 @@ [*] ./utilsBundle.ts -docker/ matchers/ reporters/ third_party/ diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 46bd120d15..ac18ba6e74 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -17,11 +17,9 @@ /* eslint-disable no-console */ import type { Command } from 'playwright-core/lib/utilsBundle'; -import * as docker from './docker/docker'; import fs from 'fs'; import url from 'url'; import path from 'path'; -import { colors } from 'playwright-core/lib/utilsBundle'; import { Runner, builtInReporters, kDefaultConfigFiles } from './runner'; import type { ConfigCLIOverrides } from './runner'; import { stopProfiling, startProfiling } from './profiler'; @@ -31,67 +29,14 @@ import { baseFullConfig, defaultTimeout, fileIsModule } from './loader'; import type { TraceMode } from './types'; export function addTestCommands(program: Command) { - addTestCommand(program, false /* isDocker */); + addTestCommand(program); addShowReportCommand(program); addListFilesCommand(program); - addDockerCommand(program); } -function addDockerCommand(program: Command) { - const dockerCommand = program.command('docker') - .description(`run tests in Docker (EXPERIMENTAL)`); - - dockerCommand.command('build') - .description('build local docker image') - .action(async function(options) { - await docker.ensureDockerEngineIsRunningOrDie(); - await docker.buildImage(); - }); - - dockerCommand.command('start') - .description('start docker container') - .action(async function(options) { - await docker.ensureDockerEngineIsRunningOrDie(); - let info = await docker.containerInfo(); - if (!info) { - process.stdout.write(`Starting docker container... `); - const time = Date.now(); - info = await docker.ensureContainerOrDie(); - const deltaMs = (Date.now() - time); - console.log('Done in ' + (deltaMs / 1000).toFixed(1) + 's'); - } - console.log([ - `- VNC session: ${info.vncSession}`, - `- Run tests with browsers inside container:`, - ` npx playwright docker test`, - `- Stop container *manually* when it is no longer needed:`, - ` npx playwright docker stop`, - ].join('\n')); - }); - - dockerCommand.command('delete-image', { hidden: true }) - .description('delete docker image, if any') - .action(async function(options) { - await docker.ensureDockerEngineIsRunningOrDie(); - await docker.deleteImage(); - }); - - dockerCommand.command('stop') - .description('stop docker container') - .action(async function(options) { - await docker.ensureDockerEngineIsRunningOrDie(); - await docker.stopContainer(); - }); - - addTestCommand(dockerCommand, true /* isDocker */); -} - -function addTestCommand(program: Command, isDocker: boolean) { +function addTestCommand(program: Command) { const command = program.command('test [test-filter...]'); - if (isDocker) - command.description('run tests with Playwright Test and browsers inside docker container'); - else - command.description('run tests with Playwright Test'); + command.description('Run tests with Playwright Test'); command.option('--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`); command.option('--headed', `Run tests in headed browsers (default: headless)`); command.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --maxFailures=1 --headed --workers=1" options`); @@ -119,27 +64,6 @@ function addTestCommand(program: Command, isDocker: boolean) { command.option('-x', `Stop after the first failure`); command.action(async (args, opts) => { try { - if (isDocker && !process.env.PW_TS_ESM_ON) { - console.log(colors.dim('Using docker container to run browsers.')); - await docker.ensureDockerEngineIsRunningOrDie(); - let info = await docker.containerInfo(); - if (!info) { - process.stdout.write(colors.dim(`Starting docker container... `)); - const time = Date.now(); - info = await docker.ensureContainerOrDie(); - const deltaMs = (Date.now() - time); - console.log(colors.dim('Done in ' + (deltaMs / 1000).toFixed(1) + 's')); - console.log(colors.dim('The Docker container will keep running after tests finished.')); - console.log(colors.dim('Stop manually using:')); - console.log(colors.dim(' npx playwright docker stop')); - } - console.log(colors.dim(`View screen: ${info.vncSession}`)); - process.env.PW_TEST_CONNECT_WS_ENDPOINT = info.wsEndpoint; - process.env.PW_TEST_CONNECT_HEADERS = JSON.stringify({ - 'x-playwright-proxy': '*', - }); - process.env.PW_TEST_SNAPSHOT_SUFFIX = 'docker'; - } await runTests(args, opts); } catch (e) { console.error(e); @@ -151,10 +75,10 @@ Arguments [test-filter...]: Pass arguments to filter test files. Each argument is treated as a regular expression. Examples: - $ npx playwright${isDocker ? ' docker ' : ' '}test my.spec.ts - $ npx playwright${isDocker ? ' docker ' : ' '}test some.spec.ts:42 - $ npx playwright${isDocker ? ' docker ' : ' '}test --headed - $ npx playwright${isDocker ? ' docker ' : ' '}test --browser=webkit`); + $ npx playwright test my.spec.ts + $ npx playwright test some.spec.ts:42 + $ npx playwright test --headed + $ npx playwright test --browser=webkit`); } function addListFilesCommand(program: Command) { diff --git a/packages/playwright-test/src/docker/build_docker_image.sh b/packages/playwright-test/src/docker/build_docker_image.sh deleted file mode 100644 index b592675995..0000000000 --- a/packages/playwright-test/src/docker/build_docker_image.sh +++ /dev/null @@ -1,74 +0,0 @@ -export NOVNC_REF='1.3.0' -export WEBSOCKIFY_REF='0.10.0' -export DEBIAN_FRONTEND=noninteractive - -# Install FluxBox, VNC & noVNC -mkdir -p /opt/bin && chmod +x /dev/shm \ - && apt-get update && apt-get install -y unzip fluxbox x11vnc \ - && curl -L -o noVNC.zip "https://github.com/novnc/noVNC/archive/v${NOVNC_REF}.zip" \ - && unzip -x noVNC.zip \ - && rm -rf noVNC-${NOVNC_REF}/{docs,tests} \ - && mv noVNC-${NOVNC_REF} /opt/bin/noVNC \ - && cp /opt/bin/noVNC/vnc.html /opt/bin/noVNC/index.html \ - && rm noVNC.zip \ - && curl -L -o websockify.zip "https://github.com/novnc/websockify/archive/v${WEBSOCKIFY_REF}.zip" \ - && unzip -x websockify.zip \ - && rm websockify.zip \ - && rm -rf websockify-${WEBSOCKIFY_REF}/{docs,tests} \ - && mv websockify-${WEBSOCKIFY_REF} /opt/bin/noVNC/utils/websockify - -# Configure FluxBox menus -mkdir /root/.fluxbox -cd /ms-playwright-agent -cat <<'EOF' | node > /root/.fluxbox/menu - const { chromium, firefox, webkit } = require('playwright-core'); - - console.log(` - [begin] (fluxbox) - [submenu] (Browsers) {} - [exec] (Chromium) { ${chromium.executablePath()} --no-sandbox --test-type= } <> - [exec] (Firefox) { ${firefox.executablePath()} } <> - [exec] (WebKit) { ${webkit.executablePath()} } <> - [end] - [include] (/etc/X11/fluxbox/fluxbox-menu) - [end] - `); -EOF - -# Create entrypoint.sh -cat <<'EOF' > /entrypoint.sh -#!/bin/bash -set -e -SCREEN_WIDTH=1360 -SCREEN_HEIGHT=1020 -SCREEN_DEPTH=24 -SCREEN_DPI=96 -GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH" - -nohup /usr/bin/xvfb-run --server-num=$DISPLAY_NUM \ - --listen-tcp \ - --server-args="-screen 0 "$GEOMETRY" -fbdir /var/tmp -dpi "$SCREEN_DPI" -listen tcp -noreset -ac +extension RANDR" \ - /usr/bin/fluxbox -display "$DISPLAY" >/dev/null 2>&1 & - -for i in $(seq 1 500); do - if xdpyinfo -display $DISPLAY >/dev/null 2>&1; then - break - fi - echo "Waiting for Xvfb..." - sleep 0.2 -done - -nohup x11vnc -forever -shared -rfbport 5900 -rfbportv6 5900 -display "$DISPLAY" >/dev/null 2>&1 & -nohup /opt/bin/noVNC/utils/novnc_proxy --listen 7900 --vnc localhost:5900 >/dev/null 2>&1 & - -cd /ms-playwright-agent - -fbsetbg -c /ms-playwright-agent/node_modules/playwright-core/lib/server/chromium/appIcon.png - -NOVNC_UUID=$(cat /proc/sys/kernel/random/uuid) -echo "novnc is listening on http://127.0.0.1:7900?path=$NOVNC_UUID&resize=scale&autoconnect=1" - -PW_UUID=$(cat /proc/sys/kernel/random/uuid) -npx playwright run-server --port=5400 --path=/$PW_UUID -EOF -chmod 755 /entrypoint.sh diff --git a/packages/playwright-test/src/docker/docker.ts b/packages/playwright-test/src/docker/docker.ts deleted file mode 100644 index 12eda03c2b..0000000000 --- a/packages/playwright-test/src/docker/docker.ts +++ /dev/null @@ -1,303 +0,0 @@ -/** - * 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 no-console */ - -import http from 'http'; -import path from 'path'; -import fs from 'fs'; -import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync'; -import * as utils from 'playwright-core/lib/utils'; -import { getPlaywrightVersion } from 'playwright-core/lib/common/userAgent'; - -interface DockerImage { - Containers: number; - Created: number; - Id: string; - Labels: null | Record; - ParentId: string; - RepoDigests: null | string[]; - RepoTags: null | string[]; - SharedSize: number; - Size: number; - VirtualSize: number; -} - -const VRT_IMAGE_DISTRO = 'focal'; -const VRT_IMAGE_NAME = `playwright:local-${getPlaywrightVersion()}-${VRT_IMAGE_DISTRO}`; -const VRT_CONTAINER_NAME = `playwright-${getPlaywrightVersion()}-${VRT_IMAGE_DISTRO}`; - -export async function deleteImage() { - const dockerImage = await findDockerImage(VRT_IMAGE_NAME); - if (!dockerImage) - return; - - if (await containerInfo()) - await stopContainer(); - await callDockerAPI('delete', `/images/${dockerImage.Id}`); -} - -export async function buildImage() { - const isDevelopmentMode = getPlaywrightVersion().includes('next'); - let baseImageName = `mcr.microsoft.com/playwright:v${getPlaywrightVersion()}-${VRT_IMAGE_DISTRO}`; - // 1. Build or pull base image. - if (isDevelopmentMode) { - // Use our docker build scripts in development mode! - if (!process.env.PWTEST_DOCKER_BASE_IMAGE) { - const arch = process.arch === 'arm64' ? '--arm64' : '--amd64'; - console.error(utils.wrapInASCIIBox([ - `You are in DEVELOPMENT mode!`, - ``, - `1. Build local base image`, - ` ./utils/docker/build.sh ${arch} ${VRT_IMAGE_DISTRO} playwright:localbuild`, - `2. Use the local base to build VRT image:`, - ` PWTEST_DOCKER_BASE_IMAGE=playwright:localbuild npx playwright docker build`, - ].join('\n'), 1)); - process.exit(1); - } - baseImageName = process.env.PWTEST_DOCKER_BASE_IMAGE; - } else { - const { code } = await spawnAsync('docker', ['pull', baseImageName], { stdio: 'inherit' }); - if (code !== 0) - throw new Error('Failed to pull docker image!'); - } - // 2. Find pulled docker image - const dockerImage = await findDockerImage(baseImageName); - if (!dockerImage) - throw new Error(`Failed to pull ${baseImageName}`); - // 3. Launch container and install VNC in it - console.log(`Building ${VRT_IMAGE_NAME}...`); - const buildScriptText = await fs.promises.readFile(path.join(__dirname, 'build_docker_image.sh'), 'utf8'); - const containerId = await launchContainer({ - image: dockerImage, - autoRemove: false, - command: ['/bin/bash', '-c', buildScriptText], - }); - await postJSON(`/containers/${containerId}/wait`); - - // 4. Commit a new image based on the launched container with installed VNC & noVNC. - const [vrtRepo, vrtTag] = VRT_IMAGE_NAME.split(':'); - await postJSON(`/commit?container=${containerId}&repo=${vrtRepo}&tag=${vrtTag}`, { - Entrypoint: ['/entrypoint.sh'], - Env: [ - 'DISPLAY_NUM=99', - 'DISPLAY=:99', - ], - }); - await Promise.all([ - // Make sure to wait for the container to be removed. - postJSON(`/containers/${containerId}/wait?condition=removed`), - callDockerAPI('delete', `/containers/${containerId}`), - ]); - console.log(`Done!`); -} - -interface ContainerInfo { - wsEndpoint: string; - vncSession: string; -} - -export async function containerInfo(): Promise { - const containerId = await findRunningDockerContainerId(); - if (!containerId) - return undefined; - const rawLogs = await callDockerAPI('get', `/containers/${containerId}/logs?stdout=true&stderr=true`).catch(e => ''); - if (!rawLogs) - return undefined; - // Docker might prefix every log line with 8 characters. Stip them out. - // See https://github.com/moby/moby/issues/7375 - // This doesn't happen if the containers is launched manually with attached terminal. - const logLines = rawLogs.split('\n').map(line => { - if ([0, 1, 2].includes(line.charCodeAt(0))) - return line.substring(8); - return line; - }); - const WS_LINE_PREFIX = 'Listening on ws://'; - const webSocketLine = logLines.find(line => line.startsWith(WS_LINE_PREFIX)); - const NOVNC_LINE_PREFIX = 'novnc is listening on '; - const novncLine = logLines.find(line => line.startsWith(NOVNC_LINE_PREFIX)); - return novncLine && webSocketLine ? { - wsEndpoint: 'ws://' + webSocketLine.substring(WS_LINE_PREFIX.length), - vncSession: novncLine.substring(NOVNC_LINE_PREFIX.length), - } : undefined; -} - -export async function ensureContainerOrDie(): Promise { - const pwImage = await findDockerImage(VRT_IMAGE_NAME); - if (!pwImage) { - console.error('\n' + utils.wrapInASCIIBox([ - `Failed to find local docker image.`, - `Please build local docker image with the following command:`, - ``, - ` npx playwright docker build`, - ``, - `<3 Playwright Team`, - ].join('\n'), 1)); - process.exit(1); - } - - let info = await containerInfo(); - if (info) - return info; - - await launchContainer({ - image: pwImage, - name: VRT_CONTAINER_NAME, - autoRemove: true, - ports: [5400, 7900], - }); - - // Wait for the service to become available. - const startTime = Date.now(); - const timeouts = [0, 100, 100, 200, 500, 1000]; - do { - await new Promise(x => setTimeout(x, timeouts.shift() ?? 1000)); - info = await containerInfo(); - } while (!info && Date.now() < startTime + 60000); - - if (!info) - throw new Error('Failed to launch docker container!'); - return info; -} - -export async function stopContainer() { - const containerId = await findRunningDockerContainerId(); - if (!containerId) - return; - await Promise.all([ - // Make sure to wait for the container to be removed. - postJSON(`/containers/${containerId}/wait?condition=removed`), - postJSON(`/containers/${containerId}/kill`), - ]); -} - -export async function ensureDockerEngineIsRunningOrDie() { - try { - await callDockerAPI('get', '/info'); - } catch (e) { - console.error(utils.wrapInASCIIBox([ - `Docker is not running!`, - `Please install and launch docker:`, - ``, - ` https://docs.docker.com/get-docker`, - ``, - ].join('\n'), 1)); - process.exit(1); - } -} - -async function findDockerImage(imageName: string): Promise { - const images: DockerImage[] | null = await getJSON('/images/json'); - return images ? images.find(image => image.RepoTags?.includes(imageName)) : undefined; -} - -interface Container { - ImageID: string; - State: string; - Names: [string]; - Id: string; -} - -async function findRunningDockerContainerId(): Promise { - const containers: (Container[]|undefined) = await getJSON('/containers/json'); - if (!containers) - return undefined; - const dockerImage = await findDockerImage(VRT_IMAGE_NAME); - const container = dockerImage ? containers.find((container: Container) => container.ImageID === dockerImage.Id) : undefined; - return container?.State === 'running' ? container.Id : undefined; -} - -interface ContainerOptions { - image: DockerImage; - autoRemove: boolean; - command?: string[]; - ports?: Number[]; - name?: string; -} - -async function launchContainer(options: ContainerOptions): Promise { - const ExposedPorts: any = {}; - const PortBindings: any = {}; - for (const port of (options.ports ?? [])) { - ExposedPorts[`${port}/tcp`] = {}; - PortBindings[`${port}/tcp`] = [{ HostPort: port + '' }]; - } - const container = await postJSON(`/containers/create` + (options.name ? '?name=' + options.name : ''), { - Cmd: options.command, - AttachStdout: true, - AttachStderr: true, - Image: options.image.Id, - ExposedPorts, - HostConfig: { - Init: true, - AutoRemove: options.autoRemove, - ShmSize: 2 * 1024 * 1024 * 1024, - PortBindings, - }, - }); - await postJSON(`/containers/${container.Id}/start`); - return container.Id; -} - -async function getJSON(url: string): Promise { - const result = await callDockerAPI('get', url); - if (!result) - return result; - return JSON.parse(result); -} - -async function postJSON(url: string, json: any = undefined) { - const result = await callDockerAPI('post', url, json ? JSON.stringify(json) : undefined); - if (!result) - return result; - return JSON.parse(result); -} - -const DOCKER_API_VERSION = '1.41'; - -function callDockerAPI(method: 'post'|'get'|'delete', url: string, body: Buffer|string|undefined = undefined): Promise { - const dockerSocket = process.platform === 'win32' ? '\\\\.\\pipe\\docker_engine' : '/var/run/docker.sock'; - return new Promise((resolve, reject) => { - const request = http.request({ - socketPath: dockerSocket, - path: `/v${DOCKER_API_VERSION}${url}`, - timeout: 30000, - method, - }, (response: http.IncomingMessage) => { - let body = ''; - response.on('data', function(chunk){ - body += chunk; - }); - response.on('end', function(){ - if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 300) - reject(new Error(`${method} ${url} FAILED with statusCode ${response.statusCode} and body\n${body}`)); - else - resolve(body); - }); - }); - request.on('error', function(e){ - reject(e); - }); - if (body) { - request.setHeader('Content-Type', 'application/json'); - request.setHeader('Content-Length', body.length); - request.write(body); - } else { - request.setHeader('Content-Type', 'text/plain'); - } - request.end(); - }); -} - diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index f139aaaf8b..aadc12f9a6 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -72,10 +72,7 @@ export const test = _baseTest.extend({ headless: [({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: 'worker', option: true }], channel: [({ launchOptions }, use) => use(launchOptions.channel), { scope: 'worker', option: true }], launchOptions: [{}, { scope: 'worker', option: true }], - connectOptions: [process.env.PW_TEST_CONNECT_WS_ENDPOINT ? { - wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT, - headers: process.env.PW_TEST_CONNECT_HEADERS ? JSON.parse(process.env.PW_TEST_CONNECT_HEADERS) : undefined, - } : undefined, { scope: 'worker', option: true }], + connectOptions: [process.env.PW_TEST_CONNECT_WS_ENDPOINT ? { wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT } : undefined, { scope: 'worker', option: true }], screenshot: ['off', { scope: 'worker', option: true }], video: ['off', { scope: 'worker', option: true }], trace: ['off', { scope: 'worker', option: true }], @@ -223,7 +220,7 @@ export const test = _baseTest.extend({ }); }, - _snapshotSuffix: [process.env.PW_TEST_SNAPSHOT_SUFFIX ?? process.platform, { scope: 'worker' }], + _snapshotSuffix: [process.platform, { scope: 'worker' }], _setupContextOptionsAndArtifacts: [async ({ playwright, _snapshotSuffix, _combinedContextOptions, _browserOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => { testInfo.snapshotSuffix = _snapshotSuffix; diff --git a/tests/installation/docker-integration.spec.ts b/tests/installation/docker-integration.spec.ts deleted file mode 100755 index 3cffac2ac2..0000000000 --- a/tests/installation/docker-integration.spec.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * 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'; -import * as path from 'path'; -import * as fs from 'fs'; -import { TestServer } from '../../utils/testserver'; - -// Skipping docker tests on CI on non-linux since GHA does not have -// Docker engine installed on macOS and Windows. -test.skip(() => process.env.CI && process.platform !== 'linux'); - -test.beforeAll(async ({ exec }) => { - // Delete any previous docker image to ensure clean run. - await exec('npx playwright docker delete-image', { - cwd: path.join(__dirname, '..', '..'), - }); -}); - -test('make sure it tells to run `npx playwright docker build` when image is not instaleld', async ({ exec }) => { - await exec('npm i --foreground-scripts @playwright/test'); - const result = await exec('npx playwright docker test docker.spec.js', { - expectToExitWithError: true, - }); - expect(result).toContain('npx playwright docker build'); -}); - -test.describe('installed image', () => { - test.beforeAll(async ({ exec }) => { - await exec('npx playwright docker build', { - env: { PWTEST_DOCKER_BASE_IMAGE: 'playwright:installation-tests-focal' }, - cwd: path.join(__dirname, '..', '..'), - }); - }); - test.afterAll(async ({ exec }) => { - await exec('npx playwright docker delete-image', { - cwd: path.join(__dirname, '..', '..'), - }); - }); - - test('make sure it auto-starts container', async ({ exec }) => { - await exec('npm i --foreground-scripts @playwright/test'); - await exec('npx playwright docker stop'); - const result = await exec('npx playwright docker test docker.spec.js --grep platform'); - expect(result).toContain('@chromium Linux'); - }); - - test.describe('running container', () => { - test.beforeAll(async ({ exec }) => { - await exec('npx playwright docker start', { - cwd: path.join(__dirname, '..', '..'), - }); - }); - - test.afterAll(async ({ exec }) => { - await exec('npx playwright docker stop', { - cwd: path.join(__dirname, '..', '..'), - }); - }); - - test('all browsers work headless', async ({ exec }) => { - await exec('npm i --foreground-scripts @playwright/test'); - const result = await exec('npx playwright docker test docker.spec.js --grep platform --browser all'); - expect(result).toContain('@chromium Linux'); - expect(result).toContain('@webkit Linux'); - expect(result).toContain('@firefox Linux'); - }); - - test('all browsers work headed', async ({ exec }) => { - await exec('npm i --foreground-scripts @playwright/test'); - { - const result = await exec(`npx playwright docker test docker.spec.js --headed --grep userAgent --browser chromium`); - expect(result).toContain('@chromium'); - expect(result).not.toContain('Headless'); - expect(result).toContain(' Chrome/'); - } - { - const result = await exec(`npx playwright docker test docker.spec.js --headed --grep userAgent --browser webkit`); - expect(result).toContain('@webkit'); - expect(result).toContain(' Version/'); - } - { - const result = await exec(`npx playwright docker test docker.spec.js --headed --grep userAgent --browser firefox`); - expect(result).toContain('@firefox'); - expect(result).toContain(' Firefox/'); - } - }); - - test('screenshots have docker suffix', async ({ exec, tmpWorkspace }) => { - await exec('npm i --foreground-scripts @playwright/test'); - await exec('npx playwright docker test docker.spec.js --grep screenshot --browser all', { - expectToExitWithError: true, - }); - const files = await fs.promises.readdir(path.join(tmpWorkspace, 'docker.spec.js-snapshots')); - expect(files).toContain('img-chromium-docker.png'); - expect(files).toContain('img-firefox-docker.png'); - expect(files).toContain('img-webkit-docker.png'); - }); - - test('port forwarding works', async ({ exec, tmpWorkspace }) => { - await exec('npm i --foreground-scripts @playwright/test'); - const TEST_PORT = 8425; - const server = await TestServer.create(tmpWorkspace, TEST_PORT); - server.setRoute('/', (request, response) => { - response.end('Hello from host'); - }); - const result = await exec('npx playwright docker test docker.spec.js --grep localhost --browser all', { - env: { - TEST_PORT: TEST_PORT + '', - }, - }); - expect(result).toContain('@chromium Hello from host'); - expect(result).toContain('@webkit Hello from host'); - expect(result).toContain('@firefox Hello from host'); - }); - }); -}); - diff --git a/tests/installation/fixture-scripts/docker.spec.js b/tests/installation/fixture-scripts/docker.spec.js deleted file mode 100644 index edc16f1133..0000000000 --- a/tests/installation/fixture-scripts/docker.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -const { test, expect } = require('@playwright/test'); - -test('platform', async ({ page }) => { - console.log('@' + page.context().browser().browserType().name(), await page.evaluate(() => navigator.platform)); -}); - -test('userAgent', async ({ page }) => { - console.log('@' + page.context().browser().browserType().name(), await page.evaluate(() => navigator.userAgent)); -}); - -test('screenshot', async ({ page }) => { - await expect(page).toHaveScreenshot('img.png'); -}); - -test('localhost', async ({ page }) => { - expect(process.env.TEST_PORT).toBeTruthy(); - await page.goto('http://localhost:' + process.env.TEST_PORT); - console.log('@' + page.context().browser().browserType().name(), await page.textContent('body')); -}); diff --git a/tests/installation/globalSetup.ts b/tests/installation/globalSetup.ts index 0518517e65..936ecb0b13 100644 --- a/tests/installation/globalSetup.ts +++ b/tests/installation/globalSetup.ts @@ -21,55 +21,40 @@ import fs from 'fs'; import { TMP_WORKSPACES } from './npmTest'; const PACKAGE_BUILDER_SCRIPT = path.join(__dirname, '..', '..', 'utils', 'pack_package.js'); -const DOCKER_BUILDER_SCRIPT = path.join(__dirname, '..', '..', 'utils', 'docker', 'build.sh'); async function globalSetup() { await promisify(rimraf)(TMP_WORKSPACES); console.log(`Temporary workspaces will be created in ${TMP_WORKSPACES}. 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_WORKSPACES, { 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.'); - } else { - 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))); + return; } - if (process.env.CI && process.platform !== 'linux') { - console.log('Skipped building docker: docker tests are not supported on Windows and macOS Github Actions.'); - } else if (process.env.PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD) { - console.log('Skipped building docker. Unset PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD to build docker.'); - } else { - console.log('Building docker. Set PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD to skip.'); - const DOCKER_IMAGE_NAME = 'playwright:installation-tests-focal'; - const arch = process.arch === 'arm64' ? '--arm64' : '--amd64'; - const { code, stderr, stdout } = await spawnAsync('bash', [DOCKER_BUILDER_SCRIPT, arch, 'focal', DOCKER_IMAGE_NAME]); + 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 docker:\n${stderr}\n${stdout}`); - console.log('Built: docker image ', DOCKER_IMAGE_NAME); - } + 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/utils/build/build.js b/utils/build/build.js index 0a07a7e696..fa64cc871d 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -308,7 +308,7 @@ copyFiles.push({ }); copyFiles.push({ - files: 'packages/playwright-test/src/**/*.(js|sh)', + files: 'packages/playwright-test/src/**/*.js', from: 'packages/playwright-test/src', to: 'packages/playwright-test/lib', ignored: ['**/.eslintrc.js'] diff --git a/utils/docker/Dockerfile.focal b/utils/docker/Dockerfile.focal index f6db13a34a..81bac7369c 100644 --- a/utils/docker/Dockerfile.focal +++ b/utils/docker/Dockerfile.focal @@ -27,9 +27,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright # The package should be built beforehand from tip-of-tree Playwright. COPY ./playwright-core.tar.gz /tmp/playwright-core.tar.gz -# 2. Bake in Playwright Agent. -# Playwright Agent is used to bake in browsers and browser dependencies, -# and run docker server later on. +# 2. Bake in browsers & deps. # Browsers will be downloaded in `/ms-playwright`. # Note: make sure to set 777 to the registry so that any user can access # registry. @@ -40,4 +38,5 @@ RUN mkdir /ms-playwright && \ npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \ npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \ rm /tmp/playwright-core.tar.gz && \ + rm -rf /ms-playwright-agent && \ chmod -R 777 /ms-playwright