Visual Testing
with cypress.io
Przemek Suchodolski
@przemuh
About me
- @przemuh / przemuh.dev
- Sénior Software Engineer @Egnyte
- Frontend dev since 2012
-
🇰🇷 korpo, 🇵🇱 software-house, 🇺🇸 startup/korpo
-
Musician amateur 🎸 🥁 🎹 (soundcloud.com/przemuh)
Visual testing
Visual regression testing
Automated UI testing
it("should have a proper className", () => {
const myComponent = render();
expect(myComponent).toHaveClass("pink-blink");
})
.parent {
.pink-blink {
color: red;
animation: none;
}
}
Code review
Refactoring CSS
Visual Testing
basics
Snapshots 📸
Visual tests
- don't fail
- don't pass
- human component is crucial
Pros
- Catching visual bugs
- Time save during manual QA
- Confidence when refactoring
- UI/UX consistency/review
- Writing fewer functional tests
Challenges
- Where to store snapshots?
- How to review the results of the tests?
- What about false-positives/false-negatives?
- Smart testing? AI? Grouping similar changes?
Visual testing in
describe("Cypress test suite", () => {
it("test case", () => {
cy.server();
cy.route("/api/some/REST/endpoint", { response: [1,2,3] });
cy.visit("#/my-page");
cy.findByText("Something").should("be.visible");
});
});
Setup
- npm install
- plugins/index.js
- support/commands.js
// home-page.spec.js
describe("Home page", () => {
it("for logged-in user", () => {
cy.login("admin")
cy.vistit("/home")
cy.matchImageSnapshot() // "Home page for logged-in user.png"
cy.matchImageSnapshot(name);
cy.matchImageSnapshot(options);
cy.matchImageSnapshot(name, options);
})
})
Baseline
Current snapshot
Diff
Results
iTerm
Success
🤔
- When to generate snapshots?
- How to review/approve them?
- Where to store them?
- What about local dev?
- Merge request?
- Prepare gallery?
- git-lfs?
- ?
v.X
v.Y
WHAT!?
🔎 Searching
for cloud service
Requirements
- Quick setup ⚡️
- Relatively cheap 💰
- Solid 🚀
awesome-*
⚡️ quick setup - 😢
💰cheap - 😢
🚀 solid - 😢
~21 online services
- Free plan
-
10unlimited users - 5000 snapshots
- 29$ / mo - 10k snapshots
- Cypress integration
- Puppeteer, WebdriverIO, Nightmare, Nightwatch, Protractor, TestCafe, Storybook
How does percy work?
percy agent
cy.percySnapshot();
cypress runner
Visual rendering & testing infrastructure
percy builds
DOM + assets (SHA256)
Pros
- Snapshot grouping
- Smart baseline picking
- Automatic animation freezing *css
- Testing across different device sizes
- Ignoring 1px diffs *within 7px surrounding circle
- Builds expire in 30 days / 1 year (depends on plan)
Setup
- npm install
- plugins/index.js
- support/commands.js
- cypress run -> percy exec -- cypress run
// home-page.spec.js
describe("Home page", () => {
it("for logged-in user", () => {
cy.login("admin")
cy.vistit("/home")
cy.percySnapshot() // "Home page for logged-in user.png"
cy.percySnapshot(name);
cy.percySnapshot(options);
cy.percySnapshot("Homepage responsive test", { widths: [768, 992, 1200] });
})
})
Can I have both? 🤔
local comparison
cloud comparison
Cypress.Commands.add("vrt", (...args) => {
let name;
let options;
if (typeof args[0] === "object") {
options = args[0];
} else {
[name, options] = args;
}
// Set the proper viewport height for local vrt when minHeight-percy option is set
if (options && options.minHeight) {
const PERCY_MAX_HEIGHT = 2000;
if (options.minHeight > PERCY_MAX_HEIGHT) {
throw new Error(
`Your minHeight ${options.minHeight} is greater than
PERCY_MAX_HEIGHT=${PERCY_MAX_HEIGHT}`,
);
}
cy.viewport(Cypress.config("viewportWidth"), options.minHeight);
}
if (Cypress.env("percy")) {
cy.percySnapshot(name, options);
} else {
cy.matchImageSnapshot(name, options);
}
});
percy exec -- cypress run --env percy=true
VRT flow
👩💻
generate baseline
snapshots
local comparison
Merge Request
label: vrt
🏁
VRT flow
6am
branch: develop
255 snapshots
~ 15min (including app build)
Watch out for it!
aka - good practicies
cypress open
(window size)
Disable none-css animations
import Highcharts from "highcharts";
Highcharts.setOptions({
chart: {
animation: false,
},
plotOptions: {
series: {
animation: false,
},
},
});
//webpack.config
{
...,
entry: [
"core-js/stable",
"regenerator-runtime/runtime",
"custom-event-polyfill",
"isomorphic-fetch",
target === "test" && "./src/main/javascript/uiTestEntry.js",
"./src/main/javascript/main.js",
].filter(Boolean),
}
Data based on date
cy.clock
// setTimeout, setInterval etc. ...
cy.clock(Date.UTC(2020, 1, 11));
// only Date
cy.clock(Date.UTC(2020, 1, 11), ["Date"]);
Waiting for DOM element
cy.route("/api/something").as("APICall");
cy.wait("@APICall");
cy.doSnapshot();
cy.route("/api/something").as("APICall");
cy.findByText("Text to wait for");
// or
cy.getByTestId("spinner").should("not.be.visible");
cy.doSnapshot();
Scroll position
cy.get('footer').scrollIntoView()
Where to start?
- Are most important to revenue and user experience
- Have the most teams working on them
- Are the most brittle or have the most legacy code
- Have the lowest test coverage
Pages that:
Thank you!
Visual Testing with cypress.io
By Przemek Suchodolski
Visual Testing with cypress.io
- 778