Cypress Screenshot Testing for Indian Ecommerce Websites
Build stable Cypress screenshot tests for product, cart, and checkout states, including the UPI handoffs your site supports.
Cypress can save screenshots during a test and automatically capture failures during cypress run. Its built-in cy.screenshot() command does not compare images. To detect visual regressions, pair Cypress with a visual comparison plugin or service that captures a current image, compares it with an approved baseline, and provides a review workflow. Cypress visual testing documentation explains the capture, compare, and review cycle.
For an Indian ecommerce site, build snapshots around the checkout states your site actually supports: product details, cart, address, payment selection, payment handoff or return, and order confirmation. UPI may involve QR, intent, application-based, or collect flows. A screenshot can record what the browser displayed; it cannot prove that a payment settled. NPCI’s UPI FAQ describes merchant integration modes and the customer flow for online merchant payments.
1. Decide what to capture
Begin with a short inventory of important journeys and states. Focus on pages where a layout regression could prevent a shopper from understanding a product, entering delivery details, selecting a payment method, or confirming an order.
| Area | Useful states to cover | What to assert before capture |
|---|---|---|
| Product | Default product, selected variant, unavailable item, long product title | Correct product and variant are visible; price and availability match the fixture |
| Cart | One item, multiple items, quantity update, empty cart, discount or shipping message | Expected items, quantity, subtotal, and next-step action are shown |
| Address | Address form, validation errors, saved address, delivery estimate | Correct form state and validation feedback are visible |
| Payment selection | Each payment option the site offers, selected and unselected states | The intended option is selected and its instructions or controls appear |
| UPI flow | QR display, app or intent handoff, return to browser, collect-request instructions if supported | The website shows the expected handoff and return status |
| Confirmation | Success, pending, declined, or retry state as applicable | The expected status and next action are visible |
Do not assume every Indian retailer offers every UPI mode. Model the integration your merchant and payment provider actually expose. NPCI lists QR, intent, application-based, and collect modes; the appropriate browser snapshots differ by flow. Keep test payments in a provider sandbox or use controlled fixtures. Do not enter real payment credentials or authorize real transactions in a visual test.
For each supported locale, add representative states where translated labels or product text are long. Check wrapping, clipping, button labels, and layout shifts. NPCI’s BHIM page lists multiple available languages, but that does not mean every ecommerce site supports the same set; test only the locales your application offers.
2. Install Cypress and configure the app
This example uses JavaScript and the Cypress end-to-end runner. In an existing app, install Cypress and open the setup wizard once to create the configuration and example folders:
npm install --save-dev cypress
npx cypress open
Set the application base URL and screenshot output folder in cypress.config.js. Replace the example URL with your local or test deployment URL:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1365,
viewportHeight: 900,
screenshotOnRunFailure: true,
screenshotsFolder: 'cypress/screenshots',
video: false
}
})
Run a spec interactively with npx cypress open, or headlessly with npx cypress run. Cypress automatically takes failure screenshots during cypress run, including CI runs; it does not automatically take failure screenshots in the interactive runner. These diagnostic screenshots are not visual-regression comparisons.
3. Write a checkout test that asserts state before taking a screenshot
Use stable selectors such as data-cy attributes for test setup and interaction. Assert the intended application state before capture so a screenshot is not taken while the page is still transitioning. The selectors and routes below are illustrative: align them with your app’s markup and test fixtures.
// cypress/e2e/checkout.cy.js
describe('checkout visual states', () => {
beforeEach(() => {
cy.intercept('GET', '/api/cart', { fixture: 'cart-one-item.json' }).as('cart')
cy.intercept('GET', '/api/payment-options', {
body: ['card', 'upi']
}).as('paymentOptions')
})
it('captures the UPI selection state', () => {
cy.visit('/checkout')
cy.wait('@cart')
cy.wait('@paymentOptions')
cy.findByRole('radio', { name: /upi/i }).check()
cy.get('[data-cy="upi-instructions"]').should('be.visible')
cy.get('[data-cy="checkout-total"]').should('contain', '₹')
// Cypress saves this file; a visual plugin/service adds comparison.
cy.screenshot('checkout/upi-selected', {
capture: 'viewport',
blackout: ['[data-cy="customer-email"]']
})
})
it('captures the order confirmation state from a controlled fixture', () => {
cy.intercept('POST', '/api/orders', {
statusCode: 201,
body: { id: 'TEST-ORDER-001', status: 'confirmed' }
}).as('createOrder')
cy.visit('/checkout')
cy.get('[data-cy="place-order"]').click()
cy.wait('@createOrder')
cy.get('[data-cy="order-status"]').should('contain', 'Order confirmed')
cy.screenshot('checkout/order-confirmed', { capture: 'viewport' })
})
})
findByRole comes from a Testing Library Cypress integration; if it is not installed, use your app’s accessible selector or a stable test ID, for example cy.get('[data-cy="upi-radio"]').check(). A test ID helps keep selectors stable, but by itself does not establish that the control is accessible.
What Cypress screenshots can capture
cy.screenshot() can capture the current viewport, the full page, the Cypress runner, a cropped region, or a selected element. Use an element screenshot when the component is the unit you own and review; use full-page capture when the overall page layout matters. Full-page capture scrolls and stitches sections, so fixed or sticky elements can appear more than once.
// Current viewport
cy.screenshot('cart/viewport', { capture: 'viewport' })
// Whole application page
cy.screenshot('product/full-page', { capture: 'fullPage' })
// A focused component, with padding around its bounds
cy.get('[data-cy="cart-summary"]').screenshot('cart/summary', { padding: 12 })
// Crop a viewport capture to a specific rectangle
cy.screenshot('checkout/payment-panel', {
capture: 'viewport',
clip: { x: 500, y: 180, width: 600, height: 620 }
})
Important screenshot options include:
| Option | Use |
|---|---|
capture |
viewport for the visible app area, fullPage for the app from top to bottom, or runner to include the Cypress command log. Element screenshots ignore this option. |
blackout |
An array of selectors whose matched content should be blacked out. Use narrowly for uncontrolled or sensitive content; it does not apply to runner captures. |
clip |
Crop using pixel coordinates: { x, y, width, height }. |
padding |
Add space around an element screenshot. It does not affect page captures. |
disableTimersAndAnimations |
Defaults to true for screenshot capture; useful for reducing moving content during the capture. |
overwrite |
Overwrite a duplicate filename when set to true. Otherwise Cypress numbers duplicate files. |
timeout |
Maximum wait for screenshot completion; defaults to Cypress’s response timeout. |
onBeforeScreenshot, onAfterScreenshot |
Callbacks for work immediately before capture and to read capture details afterward, such as path and dimensions. |
log |
Set false to hide the screenshot command from the Cypress command log. |
See the Cypress screenshot API reference for the current behavior and full option details. The command is asynchronous; assert the target state first and avoid chaining commands that depend on the subject after calling .screenshot().
4. Add visual comparison and baseline review
Saving a PNG is capture, not visual testing. A comparison workflow adds three things: a baseline image, a comparison rule, and a review path for changes. Cypress does not ship image comparison in cy.screenshot(); select a plugin or service based on who stores the baselines, where rendering happens, and how reviewers approve intentional changes.
| Choice | Baseline and comparison | Tradeoffs to evaluate |
|---|---|---|
| Open-source plugin | Images and pixel comparisons run in your environment, often with baselines in the repository | Low direct tool cost and local image custody; your team owns baseline updates, diff artifacts, and rendering consistency |
| Managed visual-testing service | Service manages capture or comparison, baselines, and review workflow | Can simplify review and browser/viewport coverage; check current pricing, data handling, and supported environments with the vendor |
Cypress documents integrations including Applitools Eyes, Argos, Chromatic, Happo, LambdaTest SmartUI, Percy, Sauce Labs Visual, SmartBear VisualTest, and Wopee.io. These are compatibility options, not endorsements. Check each vendor’s current documentation, pricing, and data terms. For tools that store images in a managed service, decide whether checkout data in the captured page is suitable for upload.
Plugin command names and setup differ. The following shows the integration shape, not an installable plugin-specific command:
it('compares the cart summary', () => {
cy.visit('/cart')
cy.get('[data-cy="cart-summary"]').should('be.visible')
cy.get('[data-cy="cart-summary"]').then(($summary) => {
// Replace with the capture-and-compare command from your chosen tool.
// It should compare this current state with an approved baseline.
})
})
When reviewing a change, first decide whether it is intentional. If intentional, approve or update the baseline through your tool’s documented workflow. If not, inspect the diff and fix the application regression. Never accept a baseline update simply to make CI green without reviewing the visual change.
5. Make the rendering stable
Visual tests can fail because the app changed or because test data, timing, fonts, or the rendering environment changed. Reduce unrelated variation before adding more snapshots.
- Use a fixed viewport. Keep viewport dimensions explicit and create separate snapshots for supported breakpoints rather than allowing CI defaults to vary.
- Control API data. Stub changing prices, inventory, shipping estimates, personalized recommendations, and order IDs when the purpose is visual layout. Keep separate integration tests for the live API behavior.
- Wait on meaningful signals. Wait for the relevant request or assert that the target content is visible. Avoid arbitrary sleeps unless the application has an unavoidable transition with no observable completion condition.
- Wait for visual assets. Ensure fonts and layout-changing images have loaded before capturing. A missing font or late image can shift line wrapping and create a large diff.
- Control motion and time. Disable or finish carousels, animations, rotating banners, and ticking clocks. Cypress disables timers and animations during screenshots by default, but app-specific motion may need an explicit stable state.
- Mask only genuine variability. Black out a timestamp or private account value only when it cannot be fixed in the fixture. Broad masks can hide real layout regressions.
- Pin the CI rendering setup. Keep Cypress, Node, browser, operating system image, and fonts consistent. Cypress Docker images can pin the environment through the selected image tag.
- Keep snapshots purposeful. Cover important flows and shared components instead of taking a comparison image after every test action.
Locale and checkout details
Use deterministic test addresses, postal codes, currency values, and tax/shipping responses. Verify the actual formatting rules your app promises. For each supported language and viewport, include a state with long product names or labels; check for overflow and controls that become hard to identify. Do not infer a locale requirement merely from a payment method or the country focus of this guide.
For UPI, snapshot browser-visible states that belong to the website: the selected option, QR or instructions, handoff initiation, return state, and any pending or failed message your integration exposes. External app screens may not be available to Cypress running in a desktop browser. Assert a provider callback or controlled backend fixture for transaction status in functional tests; a screenshot alone is not a financial record.
6. Run in CI and retain useful artifacts
A reliable CI job installs dependencies, starts the app, waits for its URL, and runs Cypress in a pinned browser environment. This GitHub Actions example uses a generic Ubuntu runner and waits for the local server before testing; adapt the start command and artifact handling to your project.
name: Cypress screenshots
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run start -- --host 0.0.0.0 &
- run: npx wait-on http://localhost:3000
- run: npx cypress run --browser chrome
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore
Pin the Cypress and browser versions in the project and CI image according to your support policy. Cypress’s official Docker images provide consistent environments; its CI guide documents image variants and provider-specific configurations. A test that starts the server and immediately launches Cypress can fail simply because the server has not finished booting.
7. Pair visual checks with functional and accessibility checks
A screenshot diff can catch a missing price, clipped button, or altered layout. It does not prove that a button submits the intended form, that an order reached the backend, or that text meets contrast requirements. Keep behavioral checks explicit and add accessibility checks for critical checkout forms.
it('keeps checkout controls labeled and keyboard reachable', () => {
cy.visit('/checkout')
cy.get('label[for="delivery-postcode"]').should('be.visible')
cy.get('#delivery-postcode').should('have.attr', 'name')
cy.findByRole('button', { name: /place order/i }).should('be.enabled')
// Add a configured accessibility scan here if your project uses one.
// Also retain manual keyboard and assistive-technology review where needed.
})
Cypress recommends explicit accessibility behavior checks in critical areas such as forms and checkout. Automated scans detect classes of known problems but cannot prove the whole interface is accessible. See the Cypress accessibility testing guide.
8. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| No failure screenshot appears | The test ran with cypress open, or screenshotOnRunFailure is disabled |
Use cypress run for automatic failure screenshots; enable the config setting and check the screenshots folder or CI artifacts. |
| Screenshot exists, but there is no regression result | cy.screenshot() only writes an image |
Configure a visual comparison integration and add its capture/compare command plus baseline review workflow. |
| Large diffs appear on every CI run | Fonts, browser versions, viewport, dynamic content, or test data vary | Pin the browser environment and viewport; fixture changing responses; wait for fonts and images; mask only unavoidable values. |
| Capture shows a loading or intermediate state | The test captured before the page reached the intended state | Wait for the relevant request and assert the target element or status before capture. |
| Full-page image repeats a sticky header | Full-page capture scrolls and stitches sections | Prefer viewport or component capture, or temporarily adjust the sticky element in a controlled test hook. |
| Checkout screenshots expose customer data | Real or persistent user data was used in the test | Use synthetic fixtures, avoid personal data, and narrowly blackout values that cannot be controlled. Review artifact access and retention. |
| UPI test hangs on app handoff | A desktop browser test cannot complete an external mobile app flow | Test the browser handoff/return contract and server callback separately; use provider sandbox behavior or a controlled fixture for the expected state. |
| CI reports that the app is unavailable | Cypress started before the local server finished booting, or the configured URL/host is wrong | Wait for the correct URL with wait-on, then verify base URL, port binding, and server logs. |
| Intentional design change blocks the build | The current image differs from the approved baseline | Review the diff with the design owner, then approve the new baseline through the selected tool’s documented process if the change is intended. |
9. Performance, reliability, and cost
Each screenshot adds browser work and produces an artifact; full-page and high-resolution captures generally involve more image data than a focused component capture. Keep the suite fast by comparing only high-value states, reusing setup, controlling network responses, and avoiding redundant snapshots. Parallel runs can shorten elapsed time, though Cypress documents that its built-in spec load balancing uses Cypress Cloud recording; factor that service and workflow into the decision.
Open-source comparison plugins can avoid a managed visual-testing subscription and keep image files in your infrastructure, but your team must store baselines, render consistently, produce diffs, and review updates. Managed services may include baseline custody, cross-browser rendering, and pull-request review under their own pricing and data terms. Check current terms directly because vendor pricing and capabilities change.
For reliability, distinguish an application defect from environmental drift before updating baselines. Keep CI artifacts long enough for review under your team’s retention policy, and avoid uploading sensitive checkout data to third parties unless your data-handling requirements allow it.
Or skip the browser setup
If you need a screenshot of a live page rather than a Cypress test and baseline comparison, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It returns a PNG, JPEG, WebP, or PDF from one GET request. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/checkout \
-o checkout.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/checkout"},
timeout=90,
)
open("checkout.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/checkout'
})
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`)
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`)
const bytes = Buffer.from(await res.arrayBuffer())
await import('node:fs/promises').then(fs => fs.writeFile('checkout.webp', bytes))
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, no card required.
FAQ
Does Cypress compare screenshots by itself?
No. Cypress captures screenshots; a visual-testing integration adds baseline comparison and review.
Will a screenshot prove a UPI payment succeeded?
No. It documents browser-visible UI. Verify transaction status through the application or payment-provider result your integration exposes.
Should every checkout test take a screenshot?
No. Choose a compact set of high-value states and shared components so baseline review remains useful.
Can a browser test capture the screen inside a UPI app?
A desktop Cypress browser run generally covers the website’s handoff and return states, not the external mobile app UI. Test that app separately if it is in your product’s scope.


