Tag: docker

  • Visual testing with Playwright and Docker

    1. Introduction
    2. The challenge: different machines, different results. 
    3. How we did it
      1. Dockerfile
      2. Docker Compose
      3. Standardized Screenshots
      4. CI Integration
      5. Exclude visual tests from regular execution
    4. Why It Matters

    Introduction

    As part of an open-source project by the Women Coding Community, we’re building a Playwright test suite to ensure our frontend works reliably. While functional tests cover most interactions, we noticed that for some static pages, like FAQs or other content-heavy sections, visual testing could add real value. Even minor CSS changes or layout shifts can break the page in ways users notice, but automated functional tests often miss these subtle regressions.

    This is where visual testing comes in. It lets us capture screenshots of key pages and automatically compare them against a reference, so we can catch unintended visual changes before they reach our users.

    Playwright makes it easy to implement visual testing in just a few lines of code:

    test('Verify FAQ Page Outline', async ({ page }) => {
      await page.goto('/mentorship/faqs');
      await expect(page).toHaveScreenshot('faq-page.png', { fullPage: true });
    });

    The challenge: different machines, different results. 

    While this code works on a single machine, it may fail on another due to subtle rendering differences. Fonts, spacing, and other visual details vary between operating systems: Windows renders fonts differently than macOS, which renders differently than Linux. For users, this is expected and harmless. But for visual tests, it creates false positives when running on different developer machines or in our CI pipeline.

    See the difference in the screenshots shared in this article:

    Different rendering in Chromium on Ubuntu and Safari on macOS

    For example, a screenshot taken on a macOS laptop may fail if the same test runs on a Linux-based CI environment. 

    The solution? Docker. It gives us a consistent environment so tests pass reliably everywhere.

    How we did it

    Reference: check PR here.

    Dockerfile

      We built DockerFile on the official Playwright image, which includes browsers and system dependencies. Then we installed our project dependencies and copied the code.

      FROM mcr.microsoft.com/playwright:v1.57.0-noble
      WORKDIR /app
      ENV CI=true
      RUN npm install -g pnpm
      COPY package.json pnpm-lock.yaml ./
      RUN pnpm install --frozen-lockfile
      COPY src ./src
      COPY public ./public
      COPY playwright-tests ./playwright-tests
      COPY next.config.mjs tsconfig.json jest.config.ts jest.setup.js ./
      USER pwuser

      Docker Compose

      Docker Compose was introduced to make running visual tests easy and consistent. It lets developers start and run everything with a single command, using the same setup locally and in CI. This avoids environment differences and reduces “works on my machine” issues.

      Our Compose file:

      • Defines a service playwright responsible for running the tests.
      • Builds the Docker image using our Dockerfile.
      • Sets /app as the default working directory.
      • Loads environment variables from .env.local for consistency with local development.
      • Mounts volumes for:
        • Project directory – so the container can access source code without copying it every time.
        • Screenshots – so new screenshots persist and visual diffs remain across container runs.
        • Playwright reports – so test reports are available locally and in CI artifacts.

      Standardized Screenshots

      We use a consistent viewport:

      use: {
        ...devices['Desktop Chrome'],
        viewport: { width: 1280, height: 720 },
      },

      And a predictable snapshot path:

      snapshotPathTemplate: 'playwright-tests/screenshots/{arg}{ext}',

      This eliminates layout differences caused by varying screen sizes and makes visual diffs easy to review.

      After test execution, screenshots are saved in the screenshot folder located inside the playwright-tests root directory.

      CI Integration

      To run visual tests inside Docker in CI, we configured environment variables:

      API_BASE_URL: https://wcc-backend.fly.dev/api
      API_KEY: ${{ secrets.API_KEY }}

      We updated CI commands.

      Run tests without updating screenshots:
      pnpm test:e2e:docker
      Run tests and update screenshots for intentional UI changes:
      pnpm test:e2e:docker:update

      These commands are defined in package.json:

      "test:e2e:docker": "docker compose run --rm playwright pnpm playwright test",
      "test:e2e:docker:update": "docker compose run --rm playwright pnpm playwright test --update-snapshots"

      This setup ensures tests run consistently in CI and locally, while keeping secrets secure and allowing controlled screenshot updates when needed.

      Exclude visual tests from regular execution

      Visual tests can be slower and more prone to false positives compared to regular tests. 

      To manage this:

      • Introduced an @visual tag for all visual tests.
      • Excluded these tests from standard runs using –grep-invert.
      • Only run them in the controlled Docker environment.

      Example test:

      test('Verify FAQ Page Outline', { tag: '@visual' }, async ({ page }) => {
        await page.goto('/mentorship/faqs');
        await expect(page).toHaveScreenshot('faq-page.png', { fullPage: true });
      });

      Standard test command in package.json:

      "test:e2e": "playwright test --grep-invert @visual"

      This prevents false positives during regular development, while maintaining reliable visual tests in Docker.

      Why It Matters

      With this setup, we can catch real visual regressions while ignoring harmless OS-level differences. Docker guarantees a consistent testing environment, and Playwright makes capturing and comparing screenshots simple.

      Our users may see slightly different fonts depending on their OS—but our tests are now reliable, reproducible, and actionable, keeping our UI looking great everywhere.

      PR: https://github.com/Women-Coding-Community/wcc-frontend/pull/186