Bitbucket Pipelines for component snapshot tests
This page is part of the Bitbucket Pipelines section of the CI/CD test gating guide, which covers running component and visual tests inside a pipeline so a snapshot mismatch stops a bad merge.
The specific problem: your jest-image-snapshot or Playwright snapshot suite runs green on your laptop, but in bitbucket-pipelines.yml it either cannot launch a browser at all, fails on every snapshot because the fonts differ, or passes without ever producing a downloadable diff when something breaks.
Problem statement
Component snapshot tests capture a rendered component to a PNG and compare future runs against that baseline. Getting them to run under Bitbucket Pipelines has three distinct failure modes. First, the default Node image has no browser libraries, so Playwright’s Chromium exits immediately with a missing-library error. Second, even when the browser launches, CI renders text with different fonts and antialiasing than your machine, so every snapshot fails on subpixel noise. Third, when a snapshot legitimately fails, the pipeline reports red but the diff PNG stays inside the container and is never surfaced, so nobody can see what changed.
The result is a pipeline that is either permanently broken, permanently flaky, or opaque when it matters — none of which can gate a merge.
Root cause
Bitbucket Pipelines runs each step in a fresh Docker container defined by the step’s image, and that container is discarded when the step ends. A plain node image lacks the system libraries headless browsers link against, and its font stack differs from a developer’s OS, so both the launch failures and the antialiasing diffs trace back to the image. The diffs go missing because Bitbucket only preserves paths you explicitly list under artifacts. Choosing a browser-capable image and pinning it — the same discipline behind handling font rendering differences across browsers — makes the rendering environment reproducible, and declaring artifacts makes the diff reviewable.
Minimal reproduction
A bitbucket-pipelines.yml that uses a plain Node image and runs Playwright snapshots. It fails before a single test asserts, because Chromium has no libraries to load.
# bitbucket-pipelines.yml — BROKEN: node image can't launch a browser
image: node:20
pipelines:
pull-requests:
'**':
- step:
name: Snapshot tests
script:
- npm ci
- npx playwright install chromium # binaries only, no system deps
- npx playwright test --grep @snapshot
The step dies with an error like:
browserType.launch: Host system is missing dependencies to run browsers.
Missing libraries:
libnss3.so
libatk-1.0.so.0
libgbm.so.1
npx playwright install chromium fetched the browser binary, but the node:20 image has none of the shared libraries Chromium needs, so it cannot start. No test runs, and the failure has nothing to do with your components.
Step-by-step fix
1. Use a browser-capable image pinned to the Playwright version
Swap the base image for Microsoft’s official Playwright image, which ships the browsers and every system library they need, with a version tag that matches your @playwright/test.
# bitbucket-pipelines.yml
image: mcr.microsoft.com/playwright:v1.48.2-noble
What this does: the Playwright image includes Chromium, Firefox, WebKit, and their apt dependencies, so browserType.launch succeeds without any install --with-deps step. Pinning the tag to v1.48.2 — the same version as your dependency — keeps the browser build and rendering identical from run to run, which is what stops baselines drifting under you.
2. Cache node_modules and the browser directory
Bitbucket discards the container after each step, so declare caches to avoid reinstalling on every run. node is a built-in cache; add a custom one for the browser directory.
definitions:
caches:
playwright: ~/.cache/ms-playwright
pipelines:
pull-requests:
'**':
- step:
name: Snapshot tests
caches:
- node # built-in: caches node_modules
- playwright # custom: caches browser binaries
script:
- npm ci
What this does: the node cache restores node_modules so npm ci is fast, and the playwright cache restores ~/.cache/ms-playwright. With the Playwright image the browsers are already present, but the cache still matters for any project that installs extra browser channels or additional locales.
3. Run the snapshot suite so a mismatch fails the step
Run the tests as the step’s script. Both jest-image-snapshot and Playwright exit non-zero on a mismatch, and in Bitbucket a non-zero script command fails the step — which is exactly the gate you want.
script:
- npm ci
- npx playwright test --grep @snapshot
For a Jest + jest-image-snapshot suite the script is analogous:
script:
- npm ci
- npx jest --ci src/components # toMatchImageSnapshot() fails on diff
What this does: a failing toHaveScreenshot or toMatchImageSnapshot assertion exits the process with code 1; Bitbucket marks the step red and, if the step is on a required branch or part of a merge check, blocks the merge. Choosing the comparison tolerance is covered under tolerance thresholds.
4. Configure jest-image-snapshot for a reproducible comparison
If you use jest-image-snapshot, set an explicit small threshold and antialiasing handling so the comparison is stable inside the container.
// src/test/setupSnapshots.ts
import { toMatchImageSnapshot } from 'jest-image-snapshot';
expect.extend({
toMatchImageSnapshot: (received, options) =>
toMatchImageSnapshot.call(
{ ...arguments[0] },
received,
{
failureThreshold: 0.01, // allow ≤1% differing pixels
failureThresholdType: 'percent',
comparisonMethod: 'ssim', // structural similarity, tolerant of AA noise
customDiffDir: '__image_snapshots__/__diff_output__',
...options,
}
),
});
What this does: failureThresholdType: 'percent' with a 0.01 threshold absorbs a handful of antialiased edge pixels while still catching real changes, and customDiffDir writes the diff PNG to a known path so the next step can publish it as an artifact.
5. Publish diff artifacts for review
Declare artifacts globs so Bitbucket preserves the diff output from a failed run. Without this, the PNGs are discarded with the container.
script:
- npm ci
- npx playwright test --grep @snapshot
artifacts:
- test-results/** # Playwright: actual/expected/diff
- __image_snapshots__/__diff_output__/** # jest-image-snapshot diffs
- playwright-report/**
What this does: any file matching these globs is attached to the pipeline result under the step’s Artifacts tab, downloadable as individual files. Reviewers open test-results/**/diff.png (or the jest diff) to see exactly which pixels moved before deciding whether to update the baseline or fix the component.
Verification
Push a branch with a deliberate one-pixel change and confirm the step fails with a downloadable diff. The pipeline log shows the mismatch:
Running 1 test using 1 worker
1) [chromium] › card.snapshot.spec.ts:6:3 › Card @snapshot ──────────────
Error: Screenshot comparison failed:
1877 pixels (ratio 0.02 of all image pixels) are different.
Expected: card-default-expected.png
Received: card-default-actual.png
Diff: card-default-diff.png
1 failed
Confirm the pipeline result is red and the artifact is attached:
# Bitbucket REST API — latest pipeline state for the branch
curl -s -u "$BB_USER:$BB_APP_PASSWORD" \
"https://api.bitbucket.org/2.0/repositories/acme/design-system/pipelines/?sort=-created_on&pagelen=1" \
| python3 -c "import sys,json; p=json.load(sys.stdin)['values'][0]; print(p['state']['result']['name'])"
Expected:
FAILED
In the Bitbucket UI, open the failed pipeline, select the Snapshot tests step, and switch to the Artifacts tab — card-default-diff.png is listed and downloadable. A red step plus a retrievable diff is the signal the gate works: the mismatch stops the pipeline, and the evidence is one click away.
Edge cases and caveats
- Baselines must be generated in the pipeline image. A snapshot committed from macOS will not match one rendered in the Linux Playwright container, because fonts and antialiasing differ. Generate and commit baselines by running the suite with
--update-snapshotsinside the samemcr.microsoft.com/playwrightimage (locally via Docker, or by committing what a first pipeline run produces), so the CI comparison runs against a matching environment. - Bitbucket’s default 1 GB memory can starve Chromium. Snapshot steps that launch several browser contexts can hit the container memory limit and crash with a non-obvious
Target closederror. Raise the step’s allocation withsize: 2x(which doubles memory to 8 GB on a standard runner) when the suite renders many components in parallel. - Artifacts expire after 14 days. Bitbucket keeps step artifacts for 14 days by default, so a diff from an old failed run may be gone when you revisit it. Download the diff while triaging, or push the images to external storage if you need a longer audit trail for design-review sign-off.
FAQ
Why do Playwright snapshots fail to launch a browser in Bitbucket Pipelines?
The default node Docker image has no browser system libraries, so Chromium exits with a missing shared-library error. Use the mcr.microsoft.com/playwright image, which ships the browsers and their dependencies, or run npx playwright install --with-deps on a plain node image to install the apt libraries first.
How do I download the diff image from a failed Bitbucket Pipelines snapshot run?
Declare an artifacts section in the step listing the diff paths — __image_snapshots__/__diff_output__/** for jest-image-snapshot or test-results/** for Playwright. Bitbucket attaches matching files to the pipeline result, where they appear under the step’s Artifacts tab as downloads.
Why do my component snapshots pass locally but fail only in Bitbucket Pipelines?
Local and CI environments render text with different fonts and antialiasing, so pixels shift even when the component is unchanged. Commit baselines generated inside the same Docker image the pipeline uses, and allow a small failureThreshold, so the CI comparison runs against a matching rendering environment.
Related
- Bitbucket Pipelines — the parent section covering component and visual test steps on Bitbucket.
- Handling Font Rendering Differences Across Browsers — why baselines must be generated in the same image and how to stabilize text rendering.
- Configuring Chromatic Threshold Settings for Pixel-Perfect Diffs — pick the failure threshold that separates real changes from antialiasing noise.
- Gating Visual Regression Tests in GitHub Actions — the same gating pattern for teams on GitHub.
- CI/CD Test Gating & Pipeline Integration — the top-level guide to gating component tests across CI providers.