Revert "feat: introduce docker integration (#16382)"
This reverts commit af042beb13.
This commit is contained in:
parent
48e97d8b88
commit
d25cc5cd8a
|
|
@ -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
|
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
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
"./lib/grid/gridServer": "./lib/grid/gridServer.js",
|
"./lib/grid/gridServer": "./lib/grid/gridServer.js",
|
||||||
"./lib/outofprocess": "./lib/outofprocess.js",
|
"./lib/outofprocess": "./lib/outofprocess.js",
|
||||||
"./lib/utils": "./lib/utils/index.js",
|
"./lib/utils": "./lib/utils/index.js",
|
||||||
"./lib/common/userAgent": "./lib/common/userAgent.js",
|
|
||||||
"./lib/utils/comparators": "./lib/utils/comparators.js",
|
"./lib/utils/comparators": "./lib/utils/comparators.js",
|
||||||
"./lib/utils/eventsHelper": "./lib/utils/eventsHelper.js",
|
"./lib/utils/eventsHelper": "./lib/utils/eventsHelper.js",
|
||||||
"./lib/utils/fileUtils": "./lib/utils/fileUtils.js",
|
"./lib/utils/fileUtils": "./lib/utils/fileUtils.js",
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ program
|
||||||
program
|
program
|
||||||
.command('show-trace [trace...]')
|
.command('show-trace [trace...]')
|
||||||
.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium')
|
.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium')
|
||||||
.description('show trace viewer')
|
.description('Show trace viewer')
|
||||||
.action(function(traces, options) {
|
.action(function(traces, options) {
|
||||||
if (options.browser === 'cr')
|
if (options.browser === 'cr')
|
||||||
options.browser = 'chromium';
|
options.browser = 'chromium';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
[*]
|
[*]
|
||||||
./utilsBundle.ts
|
./utilsBundle.ts
|
||||||
docker/
|
|
||||||
matchers/
|
matchers/
|
||||||
reporters/
|
reporters/
|
||||||
third_party/
|
third_party/
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,9 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import type { Command } from 'playwright-core/lib/utilsBundle';
|
import type { Command } from 'playwright-core/lib/utilsBundle';
|
||||||
import * as docker from './docker/docker';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
|
||||||
import { Runner, builtInReporters, kDefaultConfigFiles } from './runner';
|
import { Runner, builtInReporters, kDefaultConfigFiles } from './runner';
|
||||||
import type { ConfigCLIOverrides } from './runner';
|
import type { ConfigCLIOverrides } from './runner';
|
||||||
import { stopProfiling, startProfiling } from './profiler';
|
import { stopProfiling, startProfiling } from './profiler';
|
||||||
|
|
@ -31,67 +29,14 @@ import { baseFullConfig, defaultTimeout, fileIsModule } from './loader';
|
||||||
import type { TraceMode } from './types';
|
import type { TraceMode } from './types';
|
||||||
|
|
||||||
export function addTestCommands(program: Command) {
|
export function addTestCommands(program: Command) {
|
||||||
addTestCommand(program, false /* isDocker */);
|
addTestCommand(program);
|
||||||
addShowReportCommand(program);
|
addShowReportCommand(program);
|
||||||
addListFilesCommand(program);
|
addListFilesCommand(program);
|
||||||
addDockerCommand(program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDockerCommand(program: Command) {
|
function addTestCommand(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) {
|
|
||||||
const command = program.command('test [test-filter...]');
|
const command = program.command('test [test-filter...]');
|
||||||
if (isDocker)
|
command.description('Run tests with Playwright Test');
|
||||||
command.description('run tests with Playwright Test and browsers inside docker container');
|
|
||||||
else
|
|
||||||
command.description('run tests with Playwright Test');
|
|
||||||
command.option('--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`);
|
command.option('--browser <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('--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`);
|
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.option('-x', `Stop after the first failure`);
|
||||||
command.action(async (args, opts) => {
|
command.action(async (args, opts) => {
|
||||||
try {
|
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);
|
await runTests(args, opts);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(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.
|
Pass arguments to filter test files. Each argument is treated as a regular expression.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
$ npx playwright${isDocker ? ' docker ' : ' '}test my.spec.ts
|
$ npx playwright test my.spec.ts
|
||||||
$ npx playwright${isDocker ? ' docker ' : ' '}test some.spec.ts:42
|
$ npx playwright test some.spec.ts:42
|
||||||
$ npx playwright${isDocker ? ' docker ' : ' '}test --headed
|
$ npx playwright test --headed
|
||||||
$ npx playwright${isDocker ? ' docker ' : ' '}test --browser=webkit`);
|
$ npx playwright test --browser=webkit`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addListFilesCommand(program: Command) {
|
function addListFilesCommand(program: Command) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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<string, string>;
|
|
||||||
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<ContainerInfo|undefined> {
|
|
||||||
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<ContainerInfo> {
|
|
||||||
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<DockerImage|undefined> {
|
|
||||||
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<string|undefined> {
|
|
||||||
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<string> {
|
|
||||||
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<any> {
|
|
||||||
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<string> {
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -72,10 +72,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||||
headless: [({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: 'worker', option: true }],
|
headless: [({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: 'worker', option: true }],
|
||||||
channel: [({ launchOptions }, use) => use(launchOptions.channel), { scope: 'worker', option: true }],
|
channel: [({ launchOptions }, use) => use(launchOptions.channel), { scope: 'worker', option: true }],
|
||||||
launchOptions: [{}, { scope: 'worker', option: true }],
|
launchOptions: [{}, { scope: 'worker', option: true }],
|
||||||
connectOptions: [process.env.PW_TEST_CONNECT_WS_ENDPOINT ? {
|
connectOptions: [process.env.PW_TEST_CONNECT_WS_ENDPOINT ? { wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT } : undefined, { scope: 'worker', option: true }],
|
||||||
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 }],
|
|
||||||
screenshot: ['off', { scope: 'worker', option: true }],
|
screenshot: ['off', { scope: 'worker', option: true }],
|
||||||
video: ['off', { scope: 'worker', option: true }],
|
video: ['off', { scope: 'worker', option: true }],
|
||||||
trace: ['off', { scope: 'worker', option: true }],
|
trace: ['off', { scope: 'worker', option: true }],
|
||||||
|
|
@ -223,7 +220,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_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) => {
|
_setupContextOptionsAndArtifacts: [async ({ playwright, _snapshotSuffix, _combinedContextOptions, _browserOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => {
|
||||||
testInfo.snapshotSuffix = _snapshotSuffix;
|
testInfo.snapshotSuffix = _snapshotSuffix;
|
||||||
|
|
|
||||||
|
|
@ -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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
@ -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'));
|
|
||||||
});
|
|
||||||
|
|
@ -21,55 +21,40 @@ import fs from 'fs';
|
||||||
import { TMP_WORKSPACES } from './npmTest';
|
import { TMP_WORKSPACES } from './npmTest';
|
||||||
|
|
||||||
const PACKAGE_BUILDER_SCRIPT = path.join(__dirname, '..', '..', 'utils', 'pack_package.js');
|
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() {
|
async function globalSetup() {
|
||||||
await promisify(rimraf)(TMP_WORKSPACES);
|
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.`);
|
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 });
|
await fs.promises.mkdir(TMP_WORKSPACES, { recursive: true });
|
||||||
|
|
||||||
if (process.env.PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS) {
|
if (process.env.PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS) {
|
||||||
console.log('Skipped building packages. Unset PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS to build packages.');
|
console.log('Skipped building packages. Unset PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS to build packages.');
|
||||||
} else {
|
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.CI && process.platform !== 'linux') {
|
console.log('Building packages. Set PWTEST_INSTALLATION_TEST_SKIP_PACKAGE_BUILDS to skip.');
|
||||||
console.log('Skipped building docker: docker tests are not supported on Windows and macOS Github Actions.');
|
const outputDir = path.join(__dirname, 'output');
|
||||||
} else if (process.env.PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD) {
|
await promisify(rimraf)(outputDir);
|
||||||
console.log('Skipped building docker. Unset PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD to build docker.');
|
await fs.promises.mkdir(outputDir, { recursive: true });
|
||||||
} else {
|
|
||||||
console.log('Building docker. Set PWTEST_INSTALLATION_TEST_SKIP_DOCKER_BUILD to skip.');
|
const build = async (buildTarget: string, pkgNameOverride?: string) => {
|
||||||
const DOCKER_IMAGE_NAME = 'playwright:installation-tests-focal';
|
const outPath = path.resolve(path.join(outputDir, `${buildTarget}.tgz`));
|
||||||
const arch = process.arch === 'arm64' ? '--arm64' : '--amd64';
|
const { code, stderr, stdout } = await spawnAsync('node', [PACKAGE_BUILDER_SCRIPT, buildTarget, outPath]);
|
||||||
const { code, stderr, stdout } = await spawnAsync('bash', [DOCKER_BUILDER_SCRIPT, arch, 'focal', DOCKER_IMAGE_NAME]);
|
|
||||||
if (!!code)
|
if (!!code)
|
||||||
throw new Error(`Failed to build docker:\n${stderr}\n${stdout}`);
|
throw new Error(`Failed to build: ${buildTarget}:\n${stderr}\n${stdout}`);
|
||||||
console.log('Built: docker image ', DOCKER_IMAGE_NAME);
|
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;
|
export default globalSetup;
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ copyFiles.push({
|
||||||
});
|
});
|
||||||
|
|
||||||
copyFiles.push({
|
copyFiles.push({
|
||||||
files: 'packages/playwright-test/src/**/*.(js|sh)',
|
files: 'packages/playwright-test/src/**/*.js',
|
||||||
from: 'packages/playwright-test/src',
|
from: 'packages/playwright-test/src',
|
||||||
to: 'packages/playwright-test/lib',
|
to: 'packages/playwright-test/lib',
|
||||||
ignored: ['**/.eslintrc.js']
|
ignored: ['**/.eslintrc.js']
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
|
||||||
# The package should be built beforehand from tip-of-tree Playwright.
|
# The package should be built beforehand from tip-of-tree Playwright.
|
||||||
COPY ./playwright-core.tar.gz /tmp/playwright-core.tar.gz
|
COPY ./playwright-core.tar.gz /tmp/playwright-core.tar.gz
|
||||||
|
|
||||||
# 2. Bake in Playwright Agent.
|
# 2. Bake in browsers & deps.
|
||||||
# Playwright Agent is used to bake in browsers and browser dependencies,
|
|
||||||
# and run docker server later on.
|
|
||||||
# Browsers will be downloaded in `/ms-playwright`.
|
# Browsers will be downloaded in `/ms-playwright`.
|
||||||
# Note: make sure to set 777 to the registry so that any user can access
|
# Note: make sure to set 777 to the registry so that any user can access
|
||||||
# registry.
|
# registry.
|
||||||
|
|
@ -40,4 +38,5 @@ RUN mkdir /ms-playwright && \
|
||||||
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
|
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
|
||||||
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
|
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
|
||||||
rm /tmp/playwright-core.tar.gz && \
|
rm /tmp/playwright-core.tar.gz && \
|
||||||
|
rm -rf /ms-playwright-agent && \
|
||||||
chmod -R 777 /ms-playwright
|
chmod -R 777 /ms-playwright
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue