This episode covers visual testing: integration with Percy and Applitools, the concept of baseline screenshots and visual diffs, detecting layout regressions and UI drift, and visual regression best practices.

Text assertions cannot catch visual changes: a button that shifts, a color that changes, or a broken font. Episode 16 covers visual testing — integration with Percy and Applitools, baseline screenshots, visual diffs, and detecting layout regressions and UI drift.
Visual testing adds a testing dimension: what you see, not just what is written.
Percy from BrowserStack and Applitools are visual regression services. Both provide SDKs that capture screenshots, store them as baselines, and compare them with the next snapshot. Install the SDK of your choice:
npm install -D @percy/cli @percy/cypressnpm install -D @percy/cli @percy/cypress adds the CLI and the Cypress adapter for Percy. Applitools uses @applitools/eyes-cypress with a similar Eyes concept.
import "@percy/cypress";
it("tampilan dashboard konsisten", () => {
cy.visit("/dashboard");
cy.get("[data-cy=konten]").should("be.visible");
cy.percySnapshot("dashboard");
});cy.percySnapshot("dashboard") sends a snapshot to the Percy service named dashboard. The first snapshot becomes the baseline; subsequent snapshots are compared against it.
Visual regression depends on a baseline — the image considered correct. When a new snapshot differs from the baseline, the service flags it as a change to approve or reject:
npx percy exec -- npx cypress runnpx percy exec -- npx cypress run wraps the Cypress run with the Percy agent. A PERCY_TOKEN must be provided in the environment so snapshots can be uploaded.
The diff is computed pixel by pixel with a tolerance. Small differences like font anti-aliasing can be ignored via a threshold; large changes are shown with highlighted areas. The approve or reject decision becomes part of the team's review flow — this is not just CI work.
Layout regressions appear when CSS changes unnoticed: elements overlap, margins shift, or components get clipped. Visual testing catches them clearly:
["desktop", "mobile"].forEach((ukuran) => {
it(`tampilan konsisten di ${ukuran}`, () => {
if (ukuran === "mobile") {
cy.viewport(375, 667);
}
cy.visit("/beranda");
cy.percySnapshot(`beranda-${ukuran}`);
});
});cy.viewport(375, 667) simulates a phone screen. A snapshot per viewport makes layout regressions across different screen sizes detectable.
UI drift is a gradual visual change that goes unnoticed until it accumulates. Baselines updated with discipline — not automatically without review — keep drift visible. Assign a snapshot review owner so no change slips through without oversight.
Not every page needs a visual test. Prioritize:
Update baselines deliberately when a change is actually intended:
npx percy approve <build-id>npx percy approve <build-id> approves changed snapshots and makes them the new baseline. This process must involve humans — approving automatically means deleting the very purpose of visual testing.
Warning
Do not run visual tests against ever-changing data such as real-time clocks or random feeds. Freeze the data using stubs from episode 12 so baselines stay consistent.
Episode 16 added a visual dimension to testing: Percy and Applitools integration, the baseline and visual diff concepts, detecting layout regressions across viewports, and best practices for keeping snapshots relevant through human review.
Key takeaways:
In the next episode, episode 17, we will cover accessibility testing — adding axe-core for accessibility assertions, testing keyboard navigation, ARIA roles, and semantic HTML, understanding accessibility rules, and building accessible test flows.