After Loki.js served us well for some years we finally converted our Visual Regression Testing** logic to use the Storybook Test Runner.
The benefits:
- it's way faster
- it has better official support
- it does more (component smoke tests,
play
tests, extensibility for more like a11y tests) - it's way more stable
But the setup might be non-trivial and there are some rough edges. The biggest downside: while it is way faster it could be even more faster, if we could just use generate images against the Storybook Dev Server. Sadly this was very flaky and error boundaries would always fail (I assume this is because of Reacts unfortunate decision to re-throw errors in Dev Mode). See also this issue.
What I do now in order to generate images locally is a custom "production" build (without minification and optimization and just the stories that I actually want to test visually).
The other tricky part was the Playwright setup. In order to get the same results across machines we want to run Playwright in a Docker container. For various reasons I decided to use the Network API of Playwright. That means I'll only run Playwright in the Docker container, but I keep my running Storybook instance on the host. (At least in the local setup. Within the Gitlab CI we run everything inside Docker.)
As a general note: We have a lot of stories in Storybook, but we only want to take images of stories that are inside a components/
directory.
Here we have custom "scripts"
. The idea is the following:
In case we want to update images during local development we run $ pnpm start-playwright-server
to start the Playwright server which will be reachable on ws://127.0.0.1:3000
. Then we run $ pnpm test-storybook:generate-images
which will create a "small" (not optimized, only relevant stories) production build of Storybook and then runs the Storybook Test Runner which will generate new images.
The Gitlab CI will later run $ test-storybook:build-and-run
within the Playwright Docker image (so no need for $ pnpm start-playwright-server
). This command will be run on all stories.
Here we say to always use ws://127.0.0.1:3000
in order to connect to Playwright. (Exception: if STORYBOOK_TEST_RUNNER_CI
is set in the Gitlab CI.)
Just shows the "small" production build for the image generation.
Sadly we needed to add a custom prepare
function here. This is only needed, because of TARGET_URL
. Usually this tells the Storybook Test Runner where it can find the Storybook instance (e.g. http://127.0.0.1:58414
), but we'd actually need a custom PLAYWRIGHT_TARGET_URL
in case the Playwright Server needs to reach the Storybook instance outside of the image (e.g. http://host.docker.internal:58414
).
Besides that we have our custom postRender
. Here we want to take an image for all stories within a Components
directory. Besides that we can add custom settings per story. Super nice! I also use the storeReceivedOnFailure: true
option, because I like to check and download images right away from the failed job in the Gitlab CI. Sadly this seems to only work for image conflicts, not new images.
Nothing suprising here. Uses the Playwright Docker image, builds Storybook (all stories) and runs the Storybook Test Runner. Generated images and diffs are persisted as artifacts.