Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(docker): address docker offline comments #17377

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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