Skip to content

Commit

Permalink
chore(docker): address docker offline comments (#17377)
Browse files Browse the repository at this point in the history
This patch:
- Removes all `process.exit(1)` from `docker.ts` and instead throws
  errors.
- Drops the `npx playwright docker test` command. We agreed to
  engage docker when `PLAYWRIGHT_DOCKER` environment variable
  is set.
- Introduces hidden `npx playwright docker status` command that
  dumps a JSON with docker status:
  ```sh
  aslushnikov:~/prog/playwright$ npx playwright docker status
  {
    "dockerEngineRunning": true,
    "imageName": "playwright:local-1.27.0-next-focal",
    "imageIsPulled": true,
"containerWSEndpoing":
"ws://127.0.0.1:55077/eafeb84c-571b-4d12-ac51-f6a2b43e9155",
"containerVNCEndpoint":
"http://127.0.0.1:55076/?path=fb6d4add-9adf-4c3c-b335-893bdc235cd7&resize=scale&autoconnect=1"
  }
  ```
  • Loading branch information
aslushnikov authored Sep 15, 2022
1 parent 30ff278 commit b09ea69
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 51 deletions.
32 changes: 13 additions & 19 deletions docs/src/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,32 +179,26 @@ Docker integration usage:
npx playwright docker start
```

1. Run tests inside Docker container. Note that this command accepts all the same arguments
as a regular `npx playwright test` command.
1. Run tests inside Docker container using the `PLAYWRIGHT_DOCKER` environment variable.
You can set this environment variable as a part of your config:

```bash js
npx playwright docker test
```ts
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
process.env.PLAYWRIGHT_DOCKER = '1';
const config: PlaywrightTestConfig = {
/* ... configuration ... */
};
export default config;
```
Note that this command will detect running Docker container, and auto-launch it if needed.
NOTE: Playwright will automatically detect a running Docker container or start it if needed.
1. Finally, stop background Docker container when you're done working with tests:
```bash js
npx playwright docker stop
```
Playwright Test sets `PLAYWRIGHT_DOCKER` environment variable when it uses Docker integration.
You can use this variable to customize config or tests behavior, for example:
```ts
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
// Ignore all snapshot expectations when running outside
// of docker integration.
ignoreSnapshots: !process.env.PLAYWRIGHT_DOCKER,
};
export default config;
```
51 changes: 33 additions & 18 deletions packages/playwright-test/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,66 @@ 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)`);
.description(`Manage Docker integration (EXPERIMENTAL)`);

dockerCommand.command('build')
.description('build local docker image')
.action(async function(options) {
await docker.buildPlaywrightImage();
try {
await docker.buildPlaywrightImage();
} catch (e) {
console.error(e.stack ? e : e.message);
}
});

dockerCommand.command('start')
.description('start docker container')
.action(async function(options) {
await docker.startPlaywrightContainer();
try {
await docker.startPlaywrightContainer();
} catch (e) {
console.error(e.stack ? e : e.message);
}
});

dockerCommand.command('stop')
.description('stop docker container')
.action(async function(options) {
await docker.stopAllPlaywrightContainers();
try {
await docker.stopAllPlaywrightContainers();
} catch (e) {
console.error(e.stack ? e : e.message);
}
});

dockerCommand.command('delete-image', { hidden: true })
.description('delete docker image, if any')
.action(async function(options) {
await docker.deletePlaywrightImage();
try {
await docker.deletePlaywrightImage();
} catch (e) {
console.error(e.stack ? e : e.message);
}
});

addTestCommand(dockerCommand, true /* isDocker */);
dockerCommand.command('print-status-json', { hidden: true })
.description('print docker status')
.action(async function(options) {
await docker.printDockerStatus();
});
}

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>', `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`);
Expand Down Expand Up @@ -100,8 +117,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.PLAYWRIGHT_DOCKER = '1';
await runTests(args, opts);
} catch (e) {
console.error(e);
Expand All @@ -113,10 +128,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) {
Expand Down
27 changes: 21 additions & 6 deletions packages/playwright-test/src/docker/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,14 @@ export async function buildPlaywrightImage() {
// 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([
throw createStacklessError(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 {
Expand Down Expand Up @@ -168,6 +167,19 @@ interface ContainerInfo {
vncSession: string;
}

export async function printDockerStatus() {
const isDockerEngine = await dockerApi.checkEngineRunning();
const imageIsPulled = isDockerEngine && !!(await findDockerImage(VRT_IMAGE_NAME));
const info = isDockerEngine ? await containerInfo() : undefined;
console.log(JSON.stringify({
dockerEngineRunning: isDockerEngine,
imageName: VRT_IMAGE_NAME,
imageIsPulled,
containerWSEndpoing: info?.wsEndpoint ?? '',
containerVNCEndpoint: info?.vncSession ?? '',
}, null, 2));
}

async function containerInfo(): Promise<ContainerInfo|undefined> {
const allContainers = await dockerApi.listContainers();
const pwDockerImage = await findDockerImage(VRT_IMAGE_NAME);
Expand Down Expand Up @@ -201,15 +213,14 @@ async function containerInfo(): Promise<ContainerInfo|undefined> {
async function ensurePlaywrightContainerOrDie(): Promise<ContainerInfo> {
const pwImage = await findDockerImage(VRT_IMAGE_NAME);
if (!pwImage) {
console.error('\n' + utils.wrapInASCIIBox([
throw createStacklessError('\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();
Expand Down Expand Up @@ -242,18 +253,22 @@ async function ensurePlaywrightContainerOrDie(): Promise<ContainerInfo> {
async function checkDockerEngineIsRunningOrDie() {
if (await dockerApi.checkEngineRunning())
return;
console.error(utils.wrapInASCIIBox([
throw createStacklessError(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<dockerApi.DockerImage|undefined> {
const images = await dockerApi.listImages();
return images.find(image => image.names.includes(imageName));
}

function createStacklessError(message: string) {
const error = new Error(message);
error.stack = '';
return error;
}
29 changes: 21 additions & 8 deletions tests/installation/docker-integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ test.beforeAll(async ({ exec }) => {

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', {
const result = await exec('npx playwright test docker.spec.js', {
expectToExitWithError: true,
env: { PLAYWRIGHT_DOCKER: '1' },
});
expect(result).toContain('npx playwright docker build');
});
Expand All @@ -52,7 +53,9 @@ test.describe('installed image', () => {
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');
const result = await exec('npx playwright test docker.spec.js --grep platform', {
env: { PLAYWRIGHT_DOCKER: '1' },
});
expect(result).toContain('@chromium Linux');
});

Expand All @@ -71,7 +74,9 @@ test.describe('installed image', () => {

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');
const result = await exec('npx playwright test docker.spec.js --grep platform --browser all', {
env: { PLAYWRIGHT_DOCKER: '1' },
});
expect(result).toContain('@chromium Linux');
expect(result).toContain('@webkit Linux');
expect(result).toContain('@firefox Linux');
Expand All @@ -92,27 +97,34 @@ test.describe('installed image', () => {
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`);
const result = await exec(`npx playwright test docker.spec.js --headed --grep userAgent --browser chromium`, {
env: { PLAYWRIGHT_DOCKER: '1' },
});
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`);
const result = await exec(`npx playwright test docker.spec.js --headed --grep userAgent --browser webkit`, {
env: { PLAYWRIGHT_DOCKER: '1' },
});
expect(result).toContain('@webkit');
expect(result).toContain(' Version/');
}
{
const result = await exec(`npx playwright docker test docker.spec.js --headed --grep userAgent --browser firefox`);
const result = await exec(`npx playwright test docker.spec.js --headed --grep userAgent --browser firefox`, {
env: { PLAYWRIGHT_DOCKER: '1' },
});
expect(result).toContain('@firefox');
expect(result).toContain(' Firefox/');
}
});

test('screenshots should use __screenshots__ folder', async ({ exec, tmpWorkspace }) => {
await exec('npm i --foreground-scripts @playwright/test');
await exec('npx playwright docker test docker.spec.js --grep screenshot --browser all', {
await exec('npx playwright test docker.spec.js --grep screenshot --browser all', {
expectToExitWithError: true,
env: { PLAYWRIGHT_DOCKER: '1' },
});
await expect(path.join(tmpWorkspace, '__screenshots__', 'firefox', 'docker.spec.js', 'img.png')).toExistOnFS();
await expect(path.join(tmpWorkspace, '__screenshots__', 'chromium', 'docker.spec.js', 'img.png')).toExistOnFS();
Expand All @@ -126,8 +138,9 @@ test.describe('installed image', () => {
server.setRoute('/', (request, response) => {
response.end('Hello from host');
});
const result = await exec('npx playwright docker test docker.spec.js --grep localhost --browser all', {
const result = await exec('npx playwright test docker.spec.js --grep localhost --browser all', {
env: {
PLAYWRIGHT_DOCKER: '1',
TEST_PORT: TEST_PORT + '',
},
});
Expand Down

0 comments on commit b09ea69

Please sign in to comment.