Uploading from CI
Your tests take the screenshots. We tell you what changed.
Your tests already sign in, fill the basket and open the modal. Let them take a screenshot, send us the folder, and we will tell you what changed.
Works with Playwright, Cypress, Puppeteer, Selenium — anything that writes a PNG.
Three steps
1. Take a screenshot in your tests
await page.screenshot({ path: "shots/home.png", fullPage: true });fullPage: true matters — without it you only check the top of the page.
2. Upload the folder when the suite finishes
- run: npx playwright test
- run: npx @testkase/visual upload ./shots --project "Marketing"
env:
TESTKASE_TOKEN: ${{ secrets.TESTKASE_TOKEN }}3. Read the result
Build 12 of "Marketing" — 40 pictures.
feature/blue-button · a1b2c3d · PR #214
checkout same
home same
pricing CHANGED 2.14% different
40 screens · 1 changed · 0 new · 1 to reviewIt exits 1 when something changed, so your pipeline fails like any other test. Open Visual → Builds to approve it or report a bug.
You need three things
- A Visual subscription
- A project, made in Visual first — see Getting Started
- A token — your API key from profile menu → API Keys, saved as
TESTKASE_TOKEN
One rule: keep your filenames the same
shots/home.png is the screen home. Next week's shots/home.png is compared
against it.
If your filenames change, every picture looks new — so nothing is compared and
the build passes while checking nothing. Run --dry-run first to see the names,
and watch for the warning when a whole run matches nothing.
npx @testkase/visual upload ./shots --project "Marketing" --dry-run3 pictures would be uploaded to project "Marketing":
shots/home.png → home
shots/pricing.png → pricing
shots/checkout.png → checkoutTo name a screen yourself, use --name for one file or --map for a folder.
What the words mean
| Word | Meaning |
|---|---|
new | First time we have seen this screen. It becomes the baseline. |
same | Identical to the approved version. |
CHANGED | Different. Someone needs to look. |
failed | We could not compare it. The picture is saved — run it again. |
| Exit code | Meaning |
|---|---|
0 | Nothing to review |
1 | Something changed, or a picture could not be uploaded |
2 | The command was wrong |
Options
| Option | What it does |
|---|---|
--project <name> | Required. Which project these screens belong to. |
--dry-run | Print the names and upload nothing. |
--name / --map | Name a screen yourself, for one file or a folder. |
--files / --ignore | Which files to include or skip. Default **/*.png. |
--browser / --os | Pass these if you run the same suite twice — each gets its own baselines. |
--concurrency <n> | Uploads at once. Default 5, maximum 10. |
--build / --no-finish | For a suite split across machines. See below. |
--branch / --commit / --pr | Read from your CI automatically. Pass them only if yours is not recognised. |
--exit-zero-on-changes | Always exit 0, for your first pipeline. |
npx @testkase/visual --help has the rest.
A suite split across machines
Make one build and have them all join it, or five machines make five builds and your reviewer gets five lists.
# the first machine starts it and leaves it open — it prints the id
npx @testkase/visual upload ./shots-1 --project "Marketing" --no-finish
# the rest join it; the last one finishes and reports the result
npx @testkase/visual upload ./shots-2 --project "Marketing" --build <id> --no-finish
npx @testkase/visual upload ./shots-3 --project "Marketing" --build <id>FAQ
▶Does this use my Live or Automate minutes?
No. Uploading never starts a session or claims a device. Visual is counted in pictures compared, on its own plan.
▶Do the images go through your servers?
No. They go straight from your CI to our storage — our API only hands out the address. That keeps it fast however many you send.
▶What size should the screenshots be?
Whatever your tests produce. Screens are matched on width, so a desktop shot and a mobile shot are tracked separately. Height is not part of the match, so a page that grows taller is still the same screen.
▶How does it know my branch?
It reads the variables your CI already sets — GitHub Actions, GitLab, CircleCI, Bitbucket, Buildkite and Jenkins. On your own machine it says nothing rather than guessing.
▶What if a picture cannot be compared?
It is reported as failed and the run exits 1. The picture is saved, so running the build again is enough.
