How to Run Percy Tests in GitLab CI for an Indian Web Team
Add Percy visual snapshots to a GitLab CI pipeline, secure the project token, and make reviews repeatable across your team.
Direct answer: run your existing browser tests in a GitLab CI job under Percy’s CLI, and add Percy snapshot calls through the SDK for your test framework. Put the project token in a masked GitLab CI/CD variable named PERCY_TOKEN, ensure the app is reachable when the tests run, then inspect and review the resulting visual build in Percy. The core workflow is the same for a team in India as elsewhere; the sources cited here establish no India-specific CI instructions, availability, hosting, or pricing terms.
Percy describes its service as integrating with existing CI/CD workflows, and its changelog records support for GitLab.com and GitLab CI. The announcement dates to 2018, so use the current framework-specific integration and CLI instructions for your stack rather than treating that historical announcement as a current recipe. Percy integrations · GitLab CI support announcement
1. Understand the pipeline
GitLab CI checks out your code and runs the job. Your app and browser tests provide the page states to capture. Percy’s framework integration emits snapshots, while the Percy CLI wraps the test command, authenticates the upload, and finalizes the visual build. The team then reviews the visual changes in Percy.
This guide gives a complete example for a Node.js application using Cypress. If your team uses another framework, keep the GitLab secret and job pattern, but install that framework’s current Percy SDK and use its snapshot API and test command. Percy’s integration material describes support for existing suites, including complex suites that run across multiple processes or machines.
2. Add Percy to a Cypress project
- Create or select a web project in Percy and obtain its project token using Percy’s current project instructions.
- Install the Percy CLI and Cypress SDK as development dependencies:
npm install --save-dev @percy/cli @percy/cypress
Keep the versions in the project lockfile so local development and CI install the same dependency tree.
- Load the Cypress integration from your support file. In current Cypress projects that use
cypress/support/e2e.js, add:
import '@percy/cypress';
Use the support-file path configured by your project if it differs. Add a snapshot after the page reaches the state you want to compare:
describe('visual pages', () => {
it('captures the home page', () => {
cy.visit('/');
cy.get('[data-testid="home-ready"]').should('be.visible');
cy.percySnapshot('Home page');
});
});
The readiness assertion is application-specific: replace the selector with a stable signal that means the important content has loaded. A passing test that captures a loading screen is still a bad visual test.
For Cypress SDK installation and usage, see Percy’s documentation and its current Cypress integration guide.
3. Store the token in GitLab
In GitLab, open the project’s Settings → CI/CD → Variables and add the project token with the exact key PERCY_TOKEN. Mark it masked. Mark it protected only if the Percy job runs on protected branches or tags; protected variables are not available to unprotected refs. Do not put the token in .gitlab-ci.yml, a committed .env file, a command-line argument, or job logs. GitLab documents that YAML-defined variables are visible to people with repository access and recommends storing secrets in the UI. See GitLab CI/CD variables.
For merge request pipelines from forks, secret availability depends on your GitLab project settings and trust policy. Do not expose a Percy project token to untrusted code merely to make visual builds run. Run Percy only in trusted pipeline contexts, or use a separate review process for untrusted contributions.
4. Add a GitLab CI job
This example assumes the repository has a lockfile, a working npm run build script, and a Cypress configuration that can serve the built application at http://127.0.0.1:4173. It uses Vite’s preview server; replace that command with your application’s own server command and URL. The server is started in the background, and the job waits for it before running Cypress.
stages:
- visual
visual-percy:
stage: visual
image: cypress/browsers:node-20.11.1-chrome-122.0.6261.69-1-ff-123.0.1-edge-122.0.2365.52-1
variables:
CI: "true"
before_script:
- npm ci
script:
- npm run build
- npm run preview -- --host 127.0.0.1 > /tmp/app.log 2>&1 &
- |
for i in $(seq 1 60); do
if curl --fail --silent http://127.0.0.1:4173/ > /dev/null; then
break
fi
sleep 1
done
curl --fail --silent http://127.0.0.1:4173/ > /dev/null || {
cat /tmp/app.log
exit 1
}
- npx percy exec -- npx cypress run
artifacts:
when: on_failure
paths:
- /tmp/app.log
expire_in: 1 week
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
GitLab Runner shells do not all handle absolute artifact paths the same way, and artifact paths are normally relative to the project directory. If your runner rejects /tmp/app.log, write the log into the repository, for example logs/app.log, create that directory before launching the server, and artifact that relative path instead. You may also omit the artifact stanza; it is only there to retain the startup log on failure.
The image is an example pinned browser environment, not a Percy requirement. Select a maintained image compatible with your project and runner, and pin it to a version your team deliberately updates. If the project already has a Cypress job that starts the app, add the Percy wrapper there rather than creating a duplicate build and test pipeline. In the shown command, Percy wraps the test invocation: npx percy exec -- npx cypress run.
GitLab’s CI/CD YAML reference documents job scripts, stages, variables, artifacts, and rules. The exact application start command and framework behavior must match your repository.
5. Run and review the first build
- Commit the SDK setup, snapshot calls, and CI job.
- Run the pipeline on a trusted branch where
PERCY_TOKENis available. - Check the job log for test failures, snapshot activity, and Percy’s finalized build link. Do not print the token while diagnosing.
- Open the Percy build and compare each proposed change with the intended application change. Approve intentional visual updates through your team’s normal review process; fix regressions before merging.
- Confirm the merge request pipeline actually ran for the event types your team uses, including same-project and fork contributions where applicable.
On the first successful run, establish a baseline by reviewing the captured pages carefully. A baseline created from a broken, incomplete, or incorrectly configured environment makes later comparisons less useful.
6. Adapt the job to your test framework
The important invariant is: install the CLI and the correct SDK, invoke SDK snapshot calls from the tests, and wrap the normal test command with Percy’s CLI. Commands below illustrate the shape; confirm the SDK package and setup file against current Percy instructions for your framework.
| Framework | SDK setup | Wrapped test command |
|---|---|---|
| Cypress | @percy/cypress; load it in Cypress support |
npx percy exec -- npx cypress run |
| WebdriverIO | Current @percy/webdriverio integration |
npx percy exec -- npx wdio run wdio.conf.js |
| Protractor | Current compatible Protractor integration | npx percy exec -- npx protractor conf.js |
| Static site | Use the Percy CLI snapshot command with a built directory or snapshot list | npx percy snapshot ./public |
Do not add a second Percy wrapper inside a command that already launches Percy. For static snapshots, the input can be a static directory, a snapshot list, or a sitemap URL; consult the CLI’s current help and documentation for supported options. The published CLI package describes percy snapshot and its input types at @percy/cli-snapshot.
7. Make snapshots stable and useful
- Wait for meaningful readiness. Wait for a page-specific selector or app state before snapshotting. Prefer a deterministic readiness condition over a long fixed sleep.
- Control dynamic content. Seed predictable test data and avoid capturing rotating ads, current timestamps, random identifiers, or live personalized content where possible. Use the framework’s supported snapshot options or application test mode for unstable regions.
- Use descriptive names. Name snapshots by page and state, such as “Checkout — payment details,” so reviewers can find the relevant view.
- Cover important states. Include representative routes, responsive layouts, empty and populated states, and meaningful interaction states. A large snapshot list increases runtime and review work, so prioritize risk and user journeys.
- Ensure consistent assets. Make fonts, stylesheets, and test data available in CI. A snapshot taken before fonts or images load can differ from the intended design.
- Keep the environment repeatable. Pin dependencies with a lockfile, keep the browser environment deliberate, and use the same build configuration across baseline and candidate runs.
8. India team considerations
The technical steps do not change based on the team’s country in the sources reviewed. For teams spread across Indian time zones or working with colleagues elsewhere, agree on who reviews visual changes and when; CI can attach the Percy build link to the merge request so review does not depend on the pipeline owner being online.
Keep time-dependent page content out of snapshots or freeze it in test data. If a staging service is region-restricted or only reachable from a particular network, verify reachability from the GitLab Runner itself; a developer’s laptop connection does not prove the runner can reach it. No India-specific Percy availability, hosting, or commercial terms are established by the cited research, so check current vendor terms directly for those questions.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Build cannot authenticate or Percy reports a missing token | PERCY_TOKEN is unset, misspelled, unavailable on this ref, or stored as the wrong variable. |
Check the exact variable key and project token in GitLab settings. Check whether the job runs on a protected ref if the variable is protected. Avoid echoing the secret. |
| Tests pass but no visual build appears | The command was run without Percy’s wrapper, no Percy snapshot calls executed, or the job did not have a valid token. | Wrap the test command with npx percy exec --, confirm the SDK is loaded, and confirm the test path containing snapshot calls ran. |
| App connection refused or tests show a blank page | The app server did not start, the URL or port is wrong, or the job proceeds before the server is ready. | Inspect the server log, verify the configured port and host, and add a bounded readiness check before Cypress starts. |
| Local tests work but CI cannot reach the app | The test uses a developer-only hostname, an incorrect bind address, or a service name that is not resolvable in the runner’s network. | Use a URL reachable from the job container. Bind the app to the appropriate interface and check the runner’s networking model. |
| Snapshots show spinners, missing fonts, or incomplete content | Capture starts before the app or its assets are ready. | Wait for a stable app-specific selector and ensure asset requests succeed before calling the snapshot API. |
| Large diffs appear on every run | Content or rendering varies because of dates, randomized data, animation, fonts, browser changes, or inconsistent test state. | Stabilize test data and time, disable or settle animation in the test environment, check font loading, and keep the browser image consistent. |
| Fork merge request job has no token | GitLab withholds protected or sensitive variables from untrusted fork pipelines. | Keep the secret protected. Run Percy in trusted pipelines or use a separate controlled review path; do not expose credentials to fork code. |
| Pipeline fails with an unknown Percy command | The CLI dependency was not installed, the package script is using an outdated binary, or the invocation differs from the installed CLI version. | Install and lock @percy/cli, use npx percy --help to inspect the installed version, and follow its current command syntax. |
| Pipeline gets slower as pages are added | More routes, viewports, application startup, and test setup increase work. | Remove duplicate snapshots, prioritize critical routes, reuse a built app where practical, and keep unrelated test suites from repeating the same setup. |
10. Performance, reliability, and cost
Performance: visual jobs add dependency installation, app startup, browser tests, snapshot processing, and upload time. Cache package downloads according to your repository’s existing GitLab policy, but do not let a stale build or test dataset undermine repeatability. Split independent suites only when your team can keep their Percy build and baseline workflow understandable.
Reliability: use an explicit readiness check, preserve useful failure logs, pin dependencies, and make the snapshot input deterministic. Retries can help with transient infrastructure failures, but repeated blind retries may hide a broken test or unavailable app. Keep the Percy token scoped to trusted CI contexts and avoid printing it.
Cost: this research does not establish current Percy plan limits or pricing, nor any India-specific billing terms. Check Percy’s current commercial documentation for your account before estimating spend. Control workload by selecting snapshots that provide useful coverage rather than capturing every page and state indiscriminately.
11. Or skip the browser setup
If your immediate task is to capture pages as image files or PDFs rather than run framework-based visual comparisons, ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. Its clean-shot flow accepts cookie consent and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf to AI agents and MCP clients.
For a CI job, store the API key as a GitLab masked variable such as SCREENSHOTNEO_API_KEY, then make the call from the job:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key="$SCREENSHOTNEO_API_KEY" \
--data-urlencode url=https://stripe.com \
-o shot.webp
See the ScreenshotNeo API documentation for request options. This captures pages; it does not replace Percy’s visual baseline and review workflow. ScreenshotNeo includes 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Sign up free and get 1,000 screenshots a month with no card.
12. FAQ
Does an Indian team need a special GitLab CI configuration?
No India-specific configuration is established by the cited sources. Use the same framework and GitLab setup, and verify runner access to your application and any region-dependent services.
Can Percy run only on merge requests?
Yes. Configure GitLab job rules for the pipeline events your team wants. The example runs on merge requests and the default branch; adjust those rules to match your review and baseline policy.
Can I use Percy without Cypress?
Yes. Use Percy’s current integration for your existing framework or the CLI snapshot workflow for supported static inputs. The framework package and capture command vary.
Should every route get a snapshot?
Not necessarily. Cover important user paths and risky layouts first, then add snapshots where they catch meaningful visual regressions without overwhelming review.
Does the India location change Percy pricing?
The research available for this article does not establish regional pricing or billing terms. Consult Percy’s current account and commercial information.


