Opinions & Insights

How Netlify Uses Storybook for Visual Regression Testing

Opinions & Insights

How Netlify Uses Storybook for Visual Regression Testing

Have you ever wondered how companies keep all of their websites looking consistent from page to page? You might be thinking, “well, doesn’t someone come up with a design and someone (maybe even that same designer) writes some CSS that you just reuse from component to component?” And you’d be right! That’s exactly how things happen… sometimes. However, the web development community has largely flocked to building reusable “components” and a pattern of colocating markup, JavaScript logic, and styles all in one file. That way, if you need a button or a link or a dropdown, your team can write it once and anyone of can just include <Button />, <Link />, or <Dropdown /> and get all the same styles, logic, and (hopefully) accessibility.

Now, if you’re a developer, you’re probably thinking “awesome, now I can compose sections and pages, I can test them independently, and I can be pretty sure things will be consistent!” You might even have a few dozen test accounts so that you can see all of the different permutations of your pages. But do your designers have those same test accounts to confirm they match designs and your brand? How about your technical writers to document it all? Product managers to make sure it matches their expectations?

Enter: Storybook.

💡 Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of teams use it for UI development, testing, and documentation. It’s open source and free.

An example of Storybook running for the Netlify Button component.

An example of Storybook running for the Netlify Button component.

At Netlify, we use a lot of reusable components and we also work very closely with our designers. Being able to have a single, visible source of truth is invaluable in our development process. Designers can reference stories (distinct collections of code composed to demonstrate specific scenarios) for proposed changes, bugs, etc. – in fact, our designers reference a lot of Storybook stories for our UI library components directly in Figma! With every pull request to the app.netlify.com codebase, a Deploy Preview is created not only for the Netlify app but for every *.stories.* file. Anyone at Netlify can see what the app looks like with the changes from the pull request and also how every component is affected in isolation.

An example of stories for an entire page (the Team Overview) and its constituent components (we call them Cards).

An example of stories for an entire page (the Team Overview) and its constituent components (we call them Cards).

Automated Visual Regression Testing (VRT)

Visual Testing is the process of checking the visible output of an application and comparing it to the expected results. Put another way, it helps find “visual bugs” in the appearance of a page or screen different from purely functional bugs.
— Ramona Schwering for Smashing Magazine

It’s awesome that any Netlifolk can see our Storybook for every pull request, but what if we could get robots to do our work for us? 🤖 I mean, that’s why we have them, right? We use a service called Chromatic (who are also Storybook’s maintainers) to run VRTs for us. During the build process, we upload the storybook build to their servers, they take screenshots of our stories, and we get a nice UI that shows us the differences (if any) the new build has against our main branch.

As part of our Visual Regression Testing strategy, we’ve globally-configured our stories to turn off snapshots by default and, instead, have our developers manually enable snapshots for their stories. From there, we have a special story for each component or page that we put at the end of most stories, especially ones with a lot of variants, that we call a Snapshot.

By lumping all of a component’s stories into one “meta-story,” Chromatic has fewer stories to load and take screenshots of. We get faster builds and tests at a cheaper price, since most VRT services charge by the screenshot. In fact, with >2200 stories and >50 CI runs a day, even with TurboSnap (which we enabled), we’d reach our Chromatic snapshot quota in less than a week if we didn’t use this strategy!

// .storybook/preview.tsx
// this sets the default for all stories

export const parameters = {
  chromatic: {
    disableSnapshot: true,
 },
};
// src/components/Component.stories.tsx

export const Snapshot = () => (
  <SnapshotContainer>
    {[Loading, WithoutData, WithData, WithAnEnterprisePlan].map(
      (Story, index) => (
        <Story key={index} {...Story.args} />
      )
    )}
  </SnapshotContainer>
);
Snapshot.parameters = {
  // we disabled chromatic in .storybook/preview.tsx, so we need to
  // un-disable it (double negatives are fun)
  chromatic: { disableSnapshot: false },
};

An example of a Snapshot story

An example of a Snapshot story

There is one caveat that we have to account for, however. Since we’re not running snapshots on every story, we won’t know if any of the stories that have Chromatic disabled fail to build! To remedy this, we use @storybook/test-runner in CI. What it does, in their own words:

The Storybook test runner uses Jest as a runner, and Playwright as a testing framework. Each one of your .stories files is transformed into a spec file, and each story becomes a test, which is run in a headless browser.

The test runner is simple in design – it just visits each story from a running Storybook instance and makes sure the component is not failing

What makes a component worth writing a story?

From the screenshots, above, it may seem a bit 🌌 nebulous what qualifies somethings as “story-worthy.” We build stories for… pretty much everything! When we go about building a component or a section or a page, we start from a set of atomic components (<Button />, <Link />, <Dropdown />, etc., mentioned above) that live in src/components/ui. Each atomic component has a very comprehensive set of stories detailing its variants, which developers frequently reference when turning designs into code. You can see an example of our many Button variants in the example Snapshot story in the VRT section.

If any part of our UI is worth creating its own component for, then it’s probably worth creating a story to show it off. It would be untenable for anyone who ever wanted to see the variations in our app to keep track of test accounts with each and every possible permutation of account, user, and site data. We already curate a set of fixture data for integration and unit tests, so there’s no real added overhead there. If anything, I’d be so bold as to say that the ease of creation and maintenance of our stories is the main driver that keeps our fixtures relevant and up to date.

What makes a page worth writing a story?

It’s easy to look at the previous section and say, “Okay, so you write stories for an test pretty much every component in your app - wouldn’t testing entire pages be redundant? Wouldn’t you end up owing Chromatic an arm and a leg??”

My answers would be “probably” to that first question, and “gee, I hope not!” to the second one.

Every testing strategy requires a bit of give and take.

Do you prioritize writing unit tests for every single function, or do you write adequate integration tests and consider a set of code functionally covered?

Do you write stories (and, in our case, snapshot stories) for every single page, or does your snapshot story for the Team Overview page list a subset of its stories to give reasonable certainty that the latest changes didn’t completely break the very first page that everyone lands on when they first log into Netlify?

We have some leeway because of this composite stories/snapshot strategy so that we don’t end up paying too much for Storybook build time nor for Chromatic VRT, but our main philosophy with testing is about coverage that we can live with and that we deem adequate, and it’s about using what tools can get us there.

Hosting Storybook

As an astute reader, you may have noticed that I said that we upload our Storybook build to Chromatic’s servers. They provide a service called Publish so that others in your organization can view your app’s Storybook. You may also be asking yourself, “wait, isn’t Netlify a hosting platform for this exact type of site?” And you’d be right! We used to host our own Storybook and point our previous VRT vendor there. Once we switched to Chromatic, they required our builds be sent directly to them so they could host and run tests on their own servers. (This also had the benefit of making our VRTs less flaky since they didn’t have to run across the open internet).

We really enjoyed being able to visit storybook.netlify.com (accessible only by Netlifolk) to view the production Storybook or deploy-preview-123--storybook.netlify.app for our build previews, however, so we decided to take advantage of some Netlify ✨ magic ✨.

echo "/ https://${COMMIT_REF:0:7}--${CHROMATIC_PROJECT_TOKEN}.chromatic.com" > _redirects

We have this script added to our Storybook build scripts, which uses Git Metadata that is injected in every build on Netlify to form a url that’s then written to the site’s Redirects file. That way, every Deploy Preview and published production deploy url will automatically redirect to the Chromatic-hosted Storybook build.

Wrap-up

With Storybook, anyone and everyone at Netlify is empowered to be involved in changes large and small, we get a playground for developers to show off the building blocks of our app and how they fit together, and we get a static target for our visual regression tests.

If you want to know more about our stack and see how much Storybook is an integral part of the way we build UI, you can listen to this episode of Full Stack Radio, starring our CEO Matt Biilmann.

Keep reading

Recent posts

Book cover with the title Deliver web project 10 times faster with Jamstack enterprise

Deliver web projects 10× faster

Get the whitepaper