Skip to content

javierlopezdeancos/testcontainers-node-playwright

Repository files navigation

testcontainers-node-playwright

Test NPM Version GitHub License

Testcontainers logo playwright logo

Playwright module for Testcontainers

Playwright is an open source Microsoft end-to-end testing tool for modern web apps. You can review the Playwright documentation in the Playwright official site.

Features Implemented

  • Run Playwright tests in a playwright container.
    • Run all Playwright tests in a playwright container.
    • Run specific tests file in a playwright container.
    • Run specific tests in a playwright container.
  • Run Playwright tests in a playwright container and extract the reporter results.
    • Run Playwright tests in a playwright container and read the output line reporter.
    • Run Playwright tests in a playwright container and read the output dot reporter.
    • Run Playwright tests in a playwright container with default configuration and extract the html reporter.
    • Run Playwright tests in a playwright container with default configuration and open the html reporter.
    • Run Playwright tests in a playwright container with default configuration and extract the json reporter.
    • Run Playwright tests in a playwright container with default configuration and extract the junit reporter.
    • Run Playwright tests in a playwright container with default configuration and extract the blob reporter.
    • Run Playwright tests in a playwright container with default configuration and extract a list of different reporters.
  • Run Playwright tests pointed to your app.
    • Run Playwright tests in a playwright container pointed to your app container.
  • Run Playwright test in UI mode.
    • Run Tests in UI Mode that user can follow up outside the container using a browser.
  • Run Playwright and extract trace viewer on failed test.
    • Run Playwright tests in a playwright container with default configuration that should fail and extract the trace viewer zip file.
  • Debugging Playwright tests.
    • Debugging Playwright tests with the Playwright inspector in a Playwright container.

Installation

npm install testcontainers-node-playwright --save-dev

Example hello world app test

We have some helpers that allow us the testing module strategy.

.
└── testcontainers-node-playwright/
    └── src/
        └── example-hello-world-app-tests/

A project generated by playwright cli that allow all infrastructure to run playwright test. This project will be the one that will be mounted into the playwright container to execute its tests pointed to example-hello-world-app

How to use

Start a playwright container

Playwright has official docker images in the Microsoft Artifact Registry that you can review to choose a tag.

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

Execute test in a playwright container

const { output, exitCode } = await startedPlaywrightContainer.exec(["npx", "playwright", "test"]);

Reporter

Review the Playwright reporter documentation in order to know the available reporters and how you can notify playwright which should be run and how. Playwright has two different kind of reporters, the built-in reporters (line, dot) and the external reporters (html, json, junit, blob). You can set which reporter and how to run it, configuring the playwright.config.js file in your project. To extract the reporter generated by our tests, you can use the saveReporter method from the PlaywrightContainer class passing the type of reporter to extract and where do you want to extract it .

Execute tests in a playwright container and read the dot reporter output

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

const { output, exitCode } = await startedPlaywrightContainer.exec([
  "npx",
  "playwright",
  "test",
  "--reporter=dot",
]);

Execute tests in a playwright container and read the line reporter output

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

const { output, exitCode } = await startedPlaywrightContainer.exec([
  "npx",
  "playwright",
  "test",
  "--reporter=line",
]);

Execute tests in a playwright container with default configuration and extract a html reporter with results

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");
const PLAYWRIGHT_SAVE_REPORTS_DIRECTORY = path.resolve(__dirname, "..", "example-reports");

const externalDestinationReporterPath = path.resolve(PLAYWRIGHT_SAVE_REPORTS_DIRECTORY, "index.html");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

const { output, exitCode } = await startedPlaywrightContainer.exec(["npx", "playwright", "test", "--reporter=html"]);

await startedPlaywrightContainer.saveReporter("html", externalDestinationReporterPath);

Execute tests in a playwright container with default configuration and extract a json reporter with results

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");
const PLAYWRIGHT_SAVE_REPORTS_DIRECTORY = path.resolve(__dirname, "..", "example-reports");

const externalDestinationReporterPath = path.resolve(PLAYWRIGHT_SAVE_REPORTS_DIRECTORY, "index.html");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

const { output, exitCode } = await startedPlaywrightContainer.exec(["npx", "playwright", "test", "--reporter=json"]);

await startedPlaywrightContainer.saveReporter("json", externalDestinationReporterPath);

Execute tests in a Playwright container with default configuration and extract a blob reporter with results

This functionality get some changes from v1.44.0 version or Playwright that introduce the blob report filename generation automatically adding a hash.

You can read more about in the official blob-reporter playwright documentation section.

Those changes are reflected in the PR #7, taking a a workaround to get the blob reporter output filename for now, but that obviously will be removed in the future.

Caution

This approach implies that you need to set the PLAYWRIGHT_BLOB_OUTPUT_NAME environment variable in the container with the name of the blob output file that you want to generate to be fixed and recoverable without need to read any report-{hash}.zip filename.

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");
const PLAYWRIGHT_SAVE_REPORTS_DIRECTORY = path.resolve(__dirname, "..", "example-reports");

const externalDestinationReporterPath = path.resolve(PLAYWRIGHT_SAVE_REPORTS_DIRECTORY, "index.html");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER
)
.withEnvironment({ PLAYWRIGHT_BLOB_OUTPUT_NAME: "report.zip" })
.start();

const { output, exitCode } = await startedPlaywrightContainer.exec(["npx", "playwright", "test", "--reporter=blob"]);

await startedPlaywrightContainer.saveReporter("blob", externalDestinationReporterPath);

Execute tests in a Playwright container with default configuration and extract a junit reporter with results

import path from "path";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.44.0-jammy";
const PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER = path.resolve(__dirname, "..", "example-project");
const PLAYWRIGHT_SAVE_REPORTS_DIRECTORY = path.resolve(__dirname, "..", "example-reports");

const externalDestinationReporterPath = path.resolve(PLAYWRIGHT_SAVE_REPORTS_DIRECTORY, "index.html");

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  PLAYWRIGHT_PROJECT_TESTS_TO_RUN_INTO_THE_CONTAINER,
).start();

const { output, exitCode } = await startedPlaywrightContainer.exec(["npx", "playwright", "test", "--reporter=junit"]);

await startedPlaywrightContainer.saveReporter("junit", externalDestinationReporterPath);

Trace viewer

Review the Playwright trace viewer documentation in order to know the available reporters and how you can notify playwright which should be run and how.

Execute tests in a playwright container with default configuration that should fail and extract the trace viewer zip file

import { PlaywrightContainer } from "testcontainers-node-playwright";

const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  EXTERNAL_PLAYWRIGHT_PROJECT_DIRECTORY,
).start();

const { exitCode } = await startedPlaywrightContainer.exec([
  "npx",
  "playwright",
  "test",
  "tests/example-fail.spec.ts",
  "--trace",
  "on",
]);

const TEST_FAILED_NAME_GET_STARTED_LINK_IN_KEBAB_CASE = "example-fail-get-started-link";

const browsers = ["chromium", "firefox", "webkit"];

await startedPlaywrightContainer.saveTraceViewer(
  browsers,
  TEST_FAILED_NAME_GET_STARTED_LINK_IN_KEBAB_CASE,
  EXTERNAL_PLAYWRIGHT_SAVE_TRACES_DIRECTORY,
);

await startedPlaywrightContainer.stop();

Run playwright tests pointed to your app

We have seen how to run a playwright container, mount and execute some tests against an application already deployed on the web but, the interesting use case is how we can do the same to execute the same tests pointed to another container that is running the web application that we want to test in our same environment.

Run playwright tests in a playwright container pointed to your app container

Pointed to our hello world app example

To testing purposes we are using an image that allow us run a container with a simple web server that serve a simple html with some vanilla javascript behavior.

This app has the source code and the docker image pushed to use it here as container application to check if the playwright container can pass its tests.

You can identify how we create and start this container in playwright-container.test.ts file:

import { Network, GenericContainer } from "testcontainers";
import { PlaywrightContainer } from "testcontainers-node-playwright";

const PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright:v1.43.0-jammy";
const HELLO_WORLD_APP_IMAGE = "javierland/example-hello-world-app:latest";

const EXTERNAL_HELLO_WORLD_APP_PORT_TO_BE_TESTED = "3000";
const EXTERNAL_HELLO_WORLD_APP_ALIAS_TO_BE_TESTED = "hello-world-app";
const EXTERNAL_HELLO_WORLD_APP_TESTS_DIRECTORY = path.resolve(__dirname, "example-hello-world-app-tests");

const network: Network = new Network();
const startedNetwork = await network.start();

// Start the hello world app container to pass the playwright tests
const helloWorldAppStartedContainer = await new GenericContainer(HELLO_WORLD_APP_IMAGE)
  .withNetwork(startedNetwork)
  .withExposedPorts(EXTERNAL_HELLO_WORLD_APP_PORT_TO_BE_TESTED)
  .start();

const helloWorldAppStartedContainerIpAddress = helloWorldAppStartedContainer.getIpAddress(startedNetwork.getName());

// Start the playwright container to execute the tests pointing to the hello world app container
const startedPlaywrightContainer = await new PlaywrightContainer(
  PLAYWRIGHT_IMAGE,
  EXTERNAL_HELLO_WORLD_APP_TESTS_DIRECTORY,
)
  .withNetwork(startedNetwork)
  .withEnvironment({
    APP_CONTAINER_URL_TO_GO_TO: `http://${helloWorldAppStartedContainerIpAddress}:${EXTERNAL_HELLO_WORLD_APP_PORT_TO_BE_TESTED}`,
  })
  .start();

const { output, exitCode } = await startedPlaywrightContainer.exec([
  "npx",
  "playwright",
  "test",
  "tests/example-success.spec.ts",
  "--reporter=dot",
]);

await startedPlaywrightContainer.stop();
await helloWorldAppStartedContainer.stop();
await startedNetwork.stop();

Note

Take into account that we should start the helloWorldAppStartedContainer with a started network that we should add too into the playwright container initialization to be able the containers communication. In addition to the two containers being on the same network, the easiest way for testcontainers to help in containers communication is to set up an alias for each container that we want to reference.

Note

Take into account that you should reference into APP_CONTAINER_URL_TO_GO_TO env var in your playwright test to be executed pointed to your app container.

So, in your playwright test file:

// ...

const { APP_CONTAINER_URL_TO_GO_TO = "http://localhost:3000" } = process.env;

test("should blablabla", async ({ page }) => {
  await page.goto(APP_CONTAINER_URL_TO_GO_TO);

  // ...
});