From f2372d1cc0cd62d973464fc66301be28166c4bcd Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 8 Nov 2023 19:15:08 +0100 Subject: [PATCH 1/9] Add storyshots migration guides --- MIGRATION.md | 21 ++ MIGRATION.portable-stories.md | 365 ++++++++++++++++++++++++++++++++++ MIGRATION.test-runner.md | 207 +++++++++++++++++++ 3 files changed, 593 insertions(+) create mode 100644 MIGRATION.md create mode 100644 MIGRATION.portable-stories.md create mode 100644 MIGRATION.test-runner.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 00000000..979b990b --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,21 @@ +# `@storybook/addon-storyshots` Migration Guide + +`@storybook/addon-storyshots` was replaced by the [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) in 2021, due to storyshots being a performance and maintenance problem for Storybook. As Storybook 8 moves forward with the `storyStoreV7` and its on-demand architecture, `@storybook/addon-storyshots` will become incompatible, and you'll have to migrate from it. This migration guide will aid you in that process. + +Below you will find two options to migrate. We recommend migrating to and using the Storybook test-runner, but you can decide for yourself which path to choose, by following the guides below. + +## Option 1 - Portable stories + +Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. + +If your project uses React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3, and you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the most seamless to you. + +Follow the [migration steps to portable stories here](./MIGRATION.portable-stories.md). + +## Option 2 - Storybook test-runner + +The Storybook test-runner turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. + +The test-runner supports any official Storybook framework, and is compatible with community frameworks (support may vary). If you use Storybook for React Native, you can use the test-runner as long as you set up the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web) in your project. + +Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). diff --git a/MIGRATION.portable-stories.md b/MIGRATION.portable-stories.md new file mode 100644 index 00000000..b4181ce5 --- /dev/null +++ b/MIGRATION.portable-stories.md @@ -0,0 +1,365 @@ +# Migration Guide: From `@storybook/addon-storyshots` to portable stories + +## Table of Contents + +- [Migration Guide: From `@storybook/addon-storyshots` to portable stories](#migration-guide-from-storybookaddon-storyshots-to-portable-stories) + - [Table of Contents](#table-of-contents) + - [Pre-requisites](#pre-requisites) + - [What are portable stories?](#what-are-portable-stories) + - [What will you achieve at the end of this migration?](#what-will-you-achieve-at-the-end-of-this-migration) + - [Getting started](#getting-started) + - [1 - Disable your existing storyshots test](#1---disable-your-existing-storyshots-test) + - [2 - Import project level annotations from Storybook](#2---import-project-level-annotations-from-storybook) + - [3 - Use the portable stories recipe](#3---use-the-portable-stories-recipe) + - [Vitest](#vitest) + - [Jest](#jest) + - [4 - (Optional) extend your testing recipe](#4---optional-extend-your-testing-recipe) + - [5 - Remove storyshots from your project](#5---remove-storyshots-from-your-project) + - [6 - Provide feedback](#6---provide-feedback) + +## Pre-requisites + +Before you begin the migration process, ensure that you have: + +- [ ] A Storybook project with `@storybook/react` or `@storybook/vue3`. +- [ ] A working Storybook setup with version 7. +- [ ] Familiarity with your current Storybook and its testing setup. + +> **Note** +> If you are using a different renderer for your project, such as Angular or Svelte, this migration is not possible for you. Please refer to the [test-runner migration](./MIGRATION.md) instead. + +## What are portable stories? + +Storybook provides a `composeStories` utility that assists in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM. It also makes sure to apply all their necessary decorators, args, etc so that your component can render correctly. We call this portable stories. + +Currently, the only available renderers that provide this functionality are React and Vue3. We have plans to implement this for other renderers in the near future. + +## What will you achieve at the end of this migration? + +Portable stories will provide you the closest experience possible with storyshots. You will still have a single test file in node, which runs in a JSDOM environment, that render all of your stories and snapshots them. However, you will still face the same challenges you did with storyshots: + +- You are not testing against a real browser. +- You will have to mock many browser utilities (e.g. canvas, window APIs, etc). +- Your debugging experience will not be as good, given you can't access the browser as part of your tests. + +You could consider migrating to the [test-runner](./MIGRATION.md) instead, which is more powerful, runs against a real browser with Playwright, provides multi-browser support, and more. + +## Getting started + +The first thing you have to do is to disable your storyshots tests. You can keep it while doing the migration, as it might be helpful in the process, but your ultimate goal is to remove `@storybook/addon-storyshots`. + +### 1 - Disable your existing storyshots test + +Rename your `storybook.test.ts` (or whatever your storyshots test is called) to `storybook.test.ts.old`. This will disable the test from being detected, and allow you to create an updated test file with the same name. + +### 2 - Import project level annotations from Storybook + +If you need project level annotations such as decorators, styles, or anything that is applied to your stories via your `.storybook/preview` file, you will have to add the following code to your test setup file. Please refer to the documentation from [Jest](https://jestjs.io/docs/configuration#setupfilesafterenv-array) or [Vitest](https://vitest.dev/config/#setupfiles) on setup files. + +```ts +// your-setup-file.js +import * as projectAnnotations from './.storybook/preview'; +import { setProjectAnnotations } from '@storybook/react'; + +// apply the global annotations from Storybook preview file +setProjectAnnotations(projectAnnotations); +``` + +If you are using the new recommended format in your preview file, which is to have a single default export for all the configuration, you should adjust that slightly: + +```diff +- import * as projectAnnotations from './.storybook/preview' ++ import projectAnnotations from './.storybook/preview' +``` + +### 3 - Use the portable stories recipe + +Then, create a `storybook.test.ts` file, and depending on your tool of choice, follow the recipes below. + +- [Vitest](#vitest) +- [Jest](#jest) + +#### Vitest + +This recipe will do the following: + +1. Import all story files based on a glob pattern +2. Iterate over these files and use `composeStories` on each of their modules, resulting in a list of renderable components from each story +3. Iterave over the stories, render them and snapshot them + +Fill in your `storybook.test.ts` file with the following recipe. Please read the code comments to understand + +```ts +// @vitest-environment jsdom +import { describe, expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import { composeStories } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; + +type StoryFile = { + default: Meta; + [name: string]: StoryFn | Meta; +}; + +const compose = (entry: StoryFile): ReturnType> => { + try { + return composeStories(entry); + } catch (e) { + throw new Error( + `There was an issue composing stories for the module: ${JSON.stringify(entry)}, ${e}` + ); + } +}; + +function getAllStoryFiles() { + // Place the glob you want to match your stories files + const storyFiles = Object.entries( + import.meta.glob('./stories/**/*.(stories|story).@(js|jsx|mjs|ts|tsx)', { + eager: true, + }) + ); + + return storyFiles.map(([filePath, storyFile]) => { + const storyDir = path.dirname(filePath); + const componentName = path.basename(filePath).replace(/\.(stories|story)\.[^/.]+$/, ''); + return { filePath, storyFile, componentName, storyDir }; + }); +} + +// recreate similar options to storyshots, place your configuration below +const options = { + suite: 'Storybook Tests', + storyKindRegex: /^.*?DontTest$/, + storyNameRegex: /UNSET/, + snapshotsDirName: '__snapshots__', + snapshotExtension: '.storyshot', +}; + +describe(options.suite, () => { + getAllStoryFiles().forEach(({ storyFile, componentName, storyDir }) => { + const meta = storyFile.default; + const title = meta.title || componentName; + + if (options.storyKindRegex.test(title) || meta.parameters?.storyshots?.disable) { + // skip component tests entirely if they are disabled + return; + } + + describe(title, () => { + const stories = Object.entries(compose(storyFile)) + .map(([name, story]) => ({ name, story })) + .filter(({ name, story }) => { + // Create your own logic to filter stories here if you like. + // This is recreating the default behavior of storyshots. + return !options.storyNameRegex?.test(name) && !story.parameters.storyshots?.disable; + }); + + if (stories.length <= 0) { + throw new Error( + `No stories found for this module: ${title}. Make sure there is at least one valid story for this module, without a disable parameter, or add parameters.storyshots.disable in the default export of this file.` + ); + } + + stories.forEach(({ name, story }) => { + // Instead of not running the test, you can create logic to skip it instead, so it's shown as skipped in the test results. + const testFn = story.parameters.storyshots?.skip ? test.skip : test; + + testFn(name, async () => { + const mounted = render(story()); + // add a slightly delay to allow a couple render cycles to complete, resulting in a more stable snapshot. + await new Promise((resolve) => setTimeout(resolve, 1)); + + expect(mounted.container).toMatchSnapshot(); + }); + }); + }); + }); +}); +``` + +The snapshots will all be aggregated in a single `storybook.test.ts.snap` file. If you had storyshots configured with multisnapshots, you should change the above recipe a little to use `toMatchFileSnapshot` from vitest: + +```ts +// ...everything else + +describe(options.suite, () => { + // πŸ‘‡ add storyDir in the arguments list + getAllStoryFiles().forEach(({ filePath, storyFile, storyDir }) => { + // ...existing code + describe(title, () => { + // ...existing code + stories.forEach(({ name, story }) => { + // ...existing code + testFn(name, async () => { + // ...existing code + + // πŸ‘‡ define the path to save the snapshot to: + const snapshotPath = path.join( + storyDir, + options.snapshotsDirName, + `${componentName}${options.snapshotExtension}` + ); + expect(mounted.container).toMatchFileSnapshot(snapshotPath); + }); + }); + }); + }); +}); +``` + +This will result in separate snapshot files per story, located near their stories file e.g.: + +``` +components/Button/Button.stories.ts +components/Button/__snapshots__/Primary.storyshot +components/Button/__snapshots__/Secondary.storyshot +// ... +``` + +#### Jest + +This recipe will do the following: + +1. Import all story files based on a glob pattern +2. Iterate over these files and use `composeStories` on each of their modules, resulting in a list of renderable components from each story +3. Iterave over the stories, render them and snapshot them + +Fill in your of your `storybook.test.ts` file with the following recipe: + +```ts +// storybook.test.ts +import path from 'path'; +import * as glob from 'glob'; +import { describe, test, expect } from '@jest/globals'; +import { render } from '@testing-library/react'; +import { composeStories } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; + +type StoryFile = { + default: Meta; + [name: string]: StoryFn | Meta; +}; + +const compose = (entry: StoryFile): ReturnType> => { + try { + return composeStories(entry); + } catch (e) { + throw new Error( + `There was an issue composing stories for the module: ${JSON.stringify(entry)}, ${e}` + ); + } +}; + +function getAllStoryFiles() { + // Place the glob you want to match your stories files + const storyFiles = glob.sync( + path.join(__dirname, 'stories/**/*.(stories|story).@(js|jsx|mjs|ts|tsx)') + ); + + return storyFiles.map((filePath) => { + const storyFile = require(filePath); + return { filePath, storyFile }; + }); +} + +// recreate similar options to storyshots, place your configuration below +const options = { + suite: 'Storybook Tests', + storyKindRegex: /^.*?DontTest$/, + storyNameRegex: /UNSET/, + snapshotsDirName: '__snapshots__', + snapshotExtension: '.storyshot', +}; + +describe(options.suite, () => { + getAllStoryFiles().forEach(({ storyFile, componentName }) => { + const meta = storyFile.default; + const title = meta.title || componentName; + + if (options.storyKindRegex.test(title) || meta.parameters?.storyshots?.disable) { + return; + } + + describe(title, () => { + const stories = Object.entries(compose(storyFile)) + .map(([name, story]) => ({ name, story })) + .filter(({ name, story }) => { + // Create your own logic to filter stories here if you like. + // This is recreating the default behavior of storyshots. + return !options.storyNameRegex.test(name) && !story.parameters.storyshots?.disable; + }); + + if (stories.length <= 0) { + throw new Error( + `No stories found for this module: ${title}. Make sure there is at least one valid story for this module, without a disable parameter, or add parameters.storyshots.disable in the default export of this file.` + ); + } + + stories.forEach(({ name, story }) => { + // Instead of not running the test, you can create logic to skip it instead, so it's shown as skipped in the test results. + const testFn = story.parameters.storyshots?.skip ? test.skip : test; + + testFn(name, async () => { + const mounted = render(story()); + // add a slightly delay to allow a couple render cycles to complete, resulting in a more stable snapshot. + await new Promise((resolve) => setTimeout(resolve, 1)); + expect(mounted.container).toMatchSnapshot(); + }); + }); + }); + }); +}); +``` + +The snapshots will all be aggregated in a single `__snapshots__/storybook.test.ts.snap` file. If you had storyshots configured with multisnapshots, you can change the above recipe a little by using `jest-specific-snapshot` (you will have to install this dependency): + +```ts +// πŸ‘‡ augment expect with jest-specific-snapshot +import 'jest-specific-snapshot'; +// ...everything else + +describe(options.suite, () => { + // πŸ‘‡ add storyDir in the arguments list + getAllStoryFiles().forEach(({ filePath, storyFile, storyDir }) => { + // ...existing code + describe(title, () => { + // ...existing code + stories.forEach(({ name, story }) => { + // ...existing code + testFn(name, async () => { + // ...existing code + + // πŸ‘‡ define the path to save the snapshot to: + const snapshotPath = path.join( + storyDir, + options.snapshotsDirName, + `${componentName}${options.snapshotExtension}` + ); + expect(mounted.container).toMatchSpecificSnapshot(snapshotPath); + }); + }); + }); + }); +}); +``` + +This will result in separate snapshot files per component, located near their stories file e.g.: + +``` +components/__snapshots__/Button.stories.storyshot +components/__snapshots__/Header.stories.storyshot +components/__snapshots__/Page.stories.storyshot +// ... +``` + +### 4 - (Optional) extend your testing recipe + +The aforementioned recipes will only get you so far, depending on how you used storyshots. If you used it for image snapshot testing, acessibility testing, or other scenarios, you can extend the recipe to suit your needs. You can also consider using [the Storybook test-runner](https://github.com/storybookjs/test-runner), which provides solutions for such use cases as well. + +### 5 - Remove storyshots from your project + +Once you make sure that the portable stories solution suits you, make sure to remove your old storyshots test file and uninstall `@storybook/addon-storyshots`. + +### 6 - Provide feedback + +We are looking for feedback on your experience, and would really appreciate if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! diff --git a/MIGRATION.test-runner.md b/MIGRATION.test-runner.md new file mode 100644 index 00000000..21d75a61 --- /dev/null +++ b/MIGRATION.test-runner.md @@ -0,0 +1,207 @@ +# Migration Guide: From `@storybook/addon-storyshots` to `@storybook/test-runner` + +## Table of Contents + +- [Migration Guide: From `@storybook/addon-storyshots` to `@storybook/test-runner`](#migration-guide-from-storybookaddon-storyshots-to-storybooktest-runner) + - [Table of Contents](#table-of-contents) + - [Pre-requisites](#pre-requisites) + - [What is the Storybook Test Runner?](#what-is-the-storybook-test-runner) + - [Migration Steps](#migration-steps) + - [Replacing `@storybook/addon-storyshots` with `@storybook/test-runner`:](#replacing-storybookaddon-storyshots-with-storybooktest-runner) + - [Migrating storyshots features](#migrating-storyshots-features) + - [Smoke testing](#smoke-testing) + - [Accessibility testing](#accessibility-testing) + - [Image snapshot testing](#image-snapshot-testing) + - [DOM Snapshot testing](#dom-snapshot-testing) + - [Troubleshooting](#troubleshooting) + - [Handling unexpected failing tests](#handling-unexpected-failing-tests) + - [Snapshot path differences](#snapshot-path-differences) + - [HTML Snapshots Formatting](#html-snapshots-formatting) + - [Provide feedback](#provide-feedback) + +## Pre-requisites + +Before you begin the migration process, ensure that you have: + +- [ ] A working Storybook setup with version 7. +- [ ] Familiarity with your current Storybook and its testing setup. + +> **Note** +> If you are coming from a highly complex storyshots setup, which includes snapshot serializers, tons of mocking, etc. and end up hitting a few bumps in this migration, you might consider checking the [portable stories](./MIGRATION.portable-stories.md) migration. + +## What is the Storybook Test Runner? + +The [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/)and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. + +Check [this video](https://www.youtube.com/watch?v%253DwEa6W8uUGSA) for a quick look on the test-runner. + +## Migration Steps + +### Replacing `@storybook/addon-storyshots` with `@storybook/test-runner`: + +First, remove the `@storybook/addon-storyshots` dependency and add the `@storybook/test-runner`: + +```sh +yarn remove @storybook/addon-storyshots +yarn add --save-dev @storybook/test-runner +``` + +Then, update your `package.json` scripts to include a `test-storybook` command: + +```json +{ + "scripts": { + "test-storybook": "test-storybook" + } +} +``` + +Now, run test the setup by running Storybook and the test-runner in separate terminals: + +```sh +# Terminal 1 +yarn storybook +``` + +```sh +# Terminal 2 +yarn test-storybook +``` + +Check the results to ensure that tests are running as expected. + +### Migrating storyshots features + +Storyshots was quite flexible and could be used for different purposes. Below you will find different recipes based on your needs. If you were not using storyshots that extensively, you can benefit from following the recipes and improve your testing experience within Storybook. + +#### Smoke testing + +Storyshots provided a `renderOnly` utility to just render the story and not check the output at all, which is useful as a low-effort way of smoke testing your components and ensure they do not error. + +The test-runner does smoke testing by default, so if you used storyshots with `renderOnly`, you don't have to configure anything extra with the test-runner. The test-runner will also assert the [play function](https://storybook.js.org/docs/react/writing-stories/play-function) of your stories, providing you a better experience and more confidence. + +#### Accessibility testing + +If you used [`@storybook/addon-storyshots-puppeteer`](https://storybook.js.org/addons/@storybook/addon-storyshots-puppeteer)'s `axeTest` utility to test the accessibility of your components, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#accessibility-testing + +#### Image snapshot testing + +If you used [`@storybook/addon-storyshots-puppeteer`](https://storybook.js.org/addons/@storybook/addon-storyshots-puppeteer)'s `imageSnapshot` utility to run visual regression tests of your components, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#image-snapshot + +#### DOM Snapshot testing + +If you used storyshots default functionality for DOM snapshot testing, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#dom-snapshot-html + +### Troubleshooting + +#### Handling unexpected failing tests + +If tests that passed in storyshots fail in the test-runner, it could be because there are uncaught errors in the browser which were not detected correctly in storyshots. The test-runner treats them as failure. If this is the case, use this as an opportunity to review and fix these issues. If these errors are actually intentional (e.g. your story tests an error), then you can tell the test-runner to ignore this particular story instead by defining patterns to ignore via the `testPathIgnorePatterns` configuration. (TODO: Improve this once skipping stories is simpler in the test-runner) + +#### Snapshot path differences + +Snapshot paths and names generated by `@storybook/test-runner` differ from those by `@storybook/addon-storyshots`. You'll need to configure the test-runner to align the naming convention. + +To configure the test-runner, use its `--eject` command: + +```sh +yarn test-storybook --eject +``` + +This command will generate a `test-runner-jest.config.js` file which you can use to configure Jest. +Update the file to use a custom snapshotResolver like so: + +```ts +// ./test-runner-jest.config.js +import { getJestConfig } from '@storybook/test-runner'; + +const defaultConfig = getJestConfig(); + +const config = { + // The default configuration comes from @storybook/test-runner + ...defaultConfig, + snapshotResolver: './snapshot-resolver.js', +}; + +export default config; +``` + +Now create a `snapshot-resolver.js` file to implement a custom snapshot resolver: + +```ts +// ./snapshot-resolver.js +import path from 'path'; + +export default { + resolveSnapshotPath: (testPath) => { + const fileName = path.basename(testPath); + const fileNameWithoutExtension = fileName.replace(/\.[^/.]+$/, ''); + const modifiedFileName = `${fileNameWithoutExtension}.storyshot`; + + // make Jest generate snapshots in a path like __snapshots__/Button.storyshot + return path.join(path.dirname(testPath), '__snapshots__', modifiedFileName); + }, + resolveTestPath: (snapshotFilePath, snapshotExtension) => + path.basename(snapshotFilePath, snapshotExtension), + testPathForConsistencyCheck: 'example.storyshot', +}; +``` + +#### HTML Snapshots Formatting + +The test-runner uses `jest-serializer-html` for HTML snapshots which might have slightly different formatting than your existing snapshots. + +Additionally, you might have elements that contain random or hashed properties which might cause your snapshot tests to fail every time they run. For instance, Emotion class names, or Angular ng attributes. You can circumvent this issue by configuring the test-runner to use a custom snapshot serializer. + +To configure the test-runner, use its `--eject` command: + +```sh +yarn test-storybook --eject +``` + +This command will generate a `test-runner-jest.config.js` file which you can use to configure Jest. +Update the file to use a custom snapshotSerializer like so: + +```ts +// ./test-runner-jest.config.js +import { getJestConfig } from '@storybook/test-runner'; + +const defaultConfig = getJestConfig(); + +const config = { + ...defaultConfig, + snapshotSerializers: [ + // use your own serializer to preprocess the HTML before it's passed onto the test-runner + './snapshot-serializer.js', + ...defaultConfig.snapshotSerializers, + ], +}; + +export default config; +``` + +Now create a `snapshot-serializer.js` file to implement a custom snapshot serializer: + +```tsx +// ./snapshot-serializer.js +const jestSerializerHtml = require('jest-serializer-html'); // available as dependency of test-runner + +const DYNAMIC_ID_PATTERN = /"react-aria-\d+(\.\d+)?"/g; + +module.exports = { + // this will be called once expect(SomeHTMLElement).toMatchSnapshot() is called from the test-runner + serialize(val) { + // from + // to + const withFixedIds = val.replace(DYNAMIC_ID_PATTERN, 'mocked_id'); + return jestSerializerHtml.print(withFixedIds); + }, + test(val) { + return jestSerializerHtml.test(val); + }, +}; +``` + +### Provide feedback + +We are looking for feedback on your experience, and would really appreciate if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! From 6fbaa99120bda50aad099182bd667f7cf5bfd4f8 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Fri, 10 Nov 2023 23:34:06 +0800 Subject: [PATCH 2/9] Update MIGRATION.md --- MIGRATION.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 979b990b..ca966826 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,10 +1,25 @@ # `@storybook/addon-storyshots` Migration Guide -`@storybook/addon-storyshots` was replaced by the [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) in 2021, due to storyshots being a performance and maintenance problem for Storybook. As Storybook 8 moves forward with the `storyStoreV7` and its on-demand architecture, `@storybook/addon-storyshots` will become incompatible, and you'll have to migrate from it. This migration guide will aid you in that process. +Storyshots (`@storybook/addon-storyshots`) was Storybook's original testing solution. It providing automatic snapshot testing and rich configurability. However, it was fundamentally incompatible with Storybook 7's high-performance [on-demand architecture](https://storybook.js.org/blog/storybook-on-demand-architecture/), and suffered from other limitations. In response, we created [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) as a successor in 2021. -Below you will find two options to migrate. We recommend migrating to and using the Storybook test-runner, but you can decide for yourself which path to choose, by following the guides below. +Now that we are finally removing the old architecture in Storybook 8, Storyshots will become incompatible. If you're using Storyshots and you want to upgrade to Storybook 8, you'll need to migrate to something else. This guide will aid you in that process. -## Option 1 - Portable stories +Below you will find serveral options to migrate: +1. **Storybook test-runner** is Storybook's recommended open source testing tool. +1. **Portable stories** is an alternative approach that might be an easier migration from Storyshots, and is also supported by the core team. +3. **Chromatic** is a great option if you are looking for a fully hosted service, and built by Storybook maintainers. + +You can decide for yourself which path to choose, by following the guides below. + +## Option 1 - Storybook test-runner + +Storybook test-runner turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. + +The test-runner supports any official Storybook framework, and is compatible with community frameworks (support may vary). If you use Storybook for React Native, you can use the test-runner as long as you set up the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web) in your project. + +Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). + +## Option 2 - Portable stories Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. @@ -12,10 +27,6 @@ If your project uses React, React Native (without the [react-native-web addon](h Follow the [migration steps to portable stories here](./MIGRATION.portable-stories.md). -## Option 2 - Storybook test-runner - -The Storybook test-runner turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. +## Option 3 - Chromatic -The test-runner supports any official Storybook framework, and is compatible with community frameworks (support may vary). If you use Storybook for React Native, you can use the test-runner as long as you set up the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web) in your project. - -Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). +[Chromatic](https://www.chromatic.com/) is a cloud service for taking visual snapshots of your stories, developed by the maintainers of Storybook. It is easy to set up and configure, and has a free plan. We recommend it if you are looking for a high quality hosted testing solution. See the [Chromatic site](https://www.chromatic.com/) for setup instructions. From 5f870a098d2b21ac1a35c9f04682f350991aebd6 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Fri, 10 Nov 2023 23:40:47 +0800 Subject: [PATCH 3/9] Update MIGRATION.md --- MIGRATION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index ca966826..ce8e8e87 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -21,9 +21,9 @@ Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). ## Option 2 - Portable stories -Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. +Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. If you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the simplest option. -If your project uses React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3, and you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the most seamless to you. +This option is currently only available for React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3. However, we plan to support more renderers in the future. Follow the [migration steps to portable stories here](./MIGRATION.portable-stories.md). From 58f00f10cb9f6a4f3d4f61bbd5e8be2676cd6da0 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 10 Nov 2023 17:13:49 +0100 Subject: [PATCH 4/9] add comparison table --- MIGRATION.test-runner.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/MIGRATION.test-runner.md b/MIGRATION.test-runner.md index 21d75a61..09cadba8 100644 --- a/MIGRATION.test-runner.md +++ b/MIGRATION.test-runner.md @@ -6,6 +6,7 @@ - [Table of Contents](#table-of-contents) - [Pre-requisites](#pre-requisites) - [What is the Storybook Test Runner?](#what-is-the-storybook-test-runner) + - [Storyshots x Test Runner Comparison table](#storyshots-x-test-runner-comparison-table) - [Migration Steps](#migration-steps) - [Replacing `@storybook/addon-storyshots` with `@storybook/test-runner`:](#replacing-storybookaddon-storyshots-with-storybooktest-runner) - [Migrating storyshots features](#migrating-storyshots-features) @@ -35,6 +36,26 @@ The [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/te Check [this video](https://www.youtube.com/watch?v%253DwEa6W8uUGSA) for a quick look on the test-runner. +## Storyshots x Test Runner Comparison table + +| | Storyshots | Test runner | +| -------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Coverage reports | βœ… | βœ… | +| Access parameters in tests | βœ… | βœ… | +| DOM snapshots testing | βœ… | βœ… | +| Visual snapshot testing | βœ…Β with puppeteer | βœ… | +| A11y tests | βœ… | βœ… | +| Extra customization | βœ…Β via `initStoryshots` | βœ…Β via `--eject` | +| Run subset of tests | βœ…Β storyKindRegex + storyNameRegex | βœ… via story tags | +| Skip story via parameter | βœ… via parameters | βœ… via story tags | +| Custom test function | βœ… | βœ… | +| Interaction testing | ❌  | βœ… | +| Real Browser | ❌ | βœ… | +| Cross browser testing | ❌ | βœ… | +| Parallel Testing | ❌ | βœ… | +| storyStoreV7 compatibility | ❌ | βœ… | +| React Native support | βœ… | βœ…Β via [@storybook/addon-react-native-web](https://storybook.js.org/addons/@storybook/addon-react-native-web) | + ## Migration Steps ### Replacing `@storybook/addon-storyshots` with `@storybook/test-runner`: From 9e1e948bcdd3250270ac4cbe2b7dc56efa93cbab Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 10 Nov 2023 17:22:09 +0100 Subject: [PATCH 5/9] add link to test filtering section --- MIGRATION.test-runner.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION.test-runner.md b/MIGRATION.test-runner.md index 09cadba8..e24531cb 100644 --- a/MIGRATION.test-runner.md +++ b/MIGRATION.test-runner.md @@ -117,7 +117,7 @@ If you used storyshots default functionality for DOM snapshot testing, you can u #### Handling unexpected failing tests -If tests that passed in storyshots fail in the test-runner, it could be because there are uncaught errors in the browser which were not detected correctly in storyshots. The test-runner treats them as failure. If this is the case, use this as an opportunity to review and fix these issues. If these errors are actually intentional (e.g. your story tests an error), then you can tell the test-runner to ignore this particular story instead by defining patterns to ignore via the `testPathIgnorePatterns` configuration. (TODO: Improve this once skipping stories is simpler in the test-runner) +If tests that passed in storyshots fail in the test-runner, it could be because there are uncaught errors in the browser which were not detected correctly in storyshots. The test-runner treats them as failure. If this is the case, use this as an opportunity to review and fix these issues. If these errors are actually intentional (e.g. your story tests an error), then you can tell the test-runner to exclude or skip this particular story instead by using story tags. [Read more about that here](./README.md#filtering-tests-experimental). #### Snapshot path differences From 5f486e0f07099d9e4874559bfe0179d7de393309 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 10 Nov 2023 21:53:10 +0000 Subject: [PATCH 6/9] Documentation updates --- MIGRATION.md | 9 +- MIGRATION.portable-stories.md | 177 +++++++++++++++++----------------- MIGRATION.test-runner.md | 99 +++++++++++-------- 3 files changed, 154 insertions(+), 131 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index ce8e8e87..77f96f70 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -5,9 +5,10 @@ Storyshots (`@storybook/addon-storyshots`) was Storybook's original testing solu Now that we are finally removing the old architecture in Storybook 8, Storyshots will become incompatible. If you're using Storyshots and you want to upgrade to Storybook 8, you'll need to migrate to something else. This guide will aid you in that process. Below you will find serveral options to migrate: + 1. **Storybook test-runner** is Storybook's recommended open source testing tool. 1. **Portable stories** is an alternative approach that might be an easier migration from Storyshots, and is also supported by the core team. -3. **Chromatic** is a great option if you are looking for a fully hosted service, and built by Storybook maintainers. +1. **Chromatic** is a great option if you are looking for a fully hosted service, and built by Storybook maintainers. You can decide for yourself which path to choose, by following the guides below. @@ -15,15 +16,15 @@ You can decide for yourself which path to choose, by following the guides below. Storybook test-runner turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. -The test-runner supports any official Storybook framework, and is compatible with community frameworks (support may vary). If you use Storybook for React Native, you can use the test-runner as long as you set up the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web) in your project. +The test-runner supports any official Storybook framework and is compatible with community frameworks (support may vary). If you use Storybook for React Native, you can use the test-runner as long as you set up the [react-native-web addon](https://storybook.js.org/addons/@storybook/addon-react-native-web/) in your project. Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). ## Option 2 - Portable stories -Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. If you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the simplest option. +Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. If you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the simplest option. -This option is currently only available for React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3. However, we plan to support more renderers in the future. +This option is currently only available for React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3. However, we plan to support more renderers in the future. Follow the [migration steps to portable stories here](./MIGRATION.portable-stories.md). diff --git a/MIGRATION.portable-stories.md b/MIGRATION.portable-stories.md index b4181ce5..d2f45c73 100644 --- a/MIGRATION.portable-stories.md +++ b/MIGRATION.portable-stories.md @@ -8,14 +8,13 @@ - [What are portable stories?](#what-are-portable-stories) - [What will you achieve at the end of this migration?](#what-will-you-achieve-at-the-end-of-this-migration) - [Getting started](#getting-started) - - [1 - Disable your existing storyshots test](#1---disable-your-existing-storyshots-test) - - [2 - Import project level annotations from Storybook](#2---import-project-level-annotations-from-storybook) - - [3 - Use the portable stories recipe](#3---use-the-portable-stories-recipe) + - [1 - Import project-level annotations from Storybook](#1---import-project-level-annotations-from-storybook) + - [2 - Configure your testing framework to use portable stories](#2---configure-your-testing-framework-to-use-portable-stories) - [Vitest](#vitest) - [Jest](#jest) - - [4 - (Optional) extend your testing recipe](#4---optional-extend-your-testing-recipe) - - [5 - Remove storyshots from your project](#5---remove-storyshots-from-your-project) - - [6 - Provide feedback](#6---provide-feedback) + - [3 - Remove storyshots from your project](#3---remove-storyshots-from-your-project) + - [4 - (Optional) Extend your testing coverage](#4---optional-extend-your-testing-coverage) + - [5 - Provide feedback](#5---provide-feedback) ## Pre-requisites @@ -25,76 +24,78 @@ Before you begin the migration process, ensure that you have: - [ ] A working Storybook setup with version 7. - [ ] Familiarity with your current Storybook and its testing setup. -> **Note** -> If you are using a different renderer for your project, such as Angular or Svelte, this migration is not possible for you. Please refer to the [test-runner migration](./MIGRATION.md) instead. - ## What are portable stories? -Storybook provides a `composeStories` utility that assists in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM. It also makes sure to apply all their necessary decorators, args, etc so that your component can render correctly. We call this portable stories. +Storybook provides a `composeStories` utility that assists in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM. It also makes sure to apply all their necessary decorators, args, etc., so your component can render correctly. We call these portable stories. -Currently, the only available renderers that provide this functionality are React and Vue3. We have plans to implement this for other renderers in the near future. +Currently, the only available renderers that provide this functionality are React and Vue3. We have plans to implement this for other renderers soon. If you are using a different renderer (e.g., Angular, Svelte), we recommend that you follow the [test-runner migration](./MIGRATION.test-runner.md) instead. ## What will you achieve at the end of this migration? -Portable stories will provide you the closest experience possible with storyshots. You will still have a single test file in node, which runs in a JSDOM environment, that render all of your stories and snapshots them. However, you will still face the same challenges you did with storyshots: +If you want to have a similar experience you had with the Storyshots addon, portable stories can help you achieve that. With it, you still have a single test file that can run in a JSDOM environment, rendering all your stories and snapshotting them. However, you may run into similar limitations as you had with the Storyshots addon: - You are not testing against a real browser. -- You will have to mock many browser utilities (e.g. canvas, window APIs, etc). +- You must mock many browser utilities (e.g., canvas, window APIs, etc). - Your debugging experience will not be as good, given you can't access the browser as part of your tests. -You could consider migrating to the [test-runner](./MIGRATION.md) instead, which is more powerful, runs against a real browser with Playwright, provides multi-browser support, and more. +Alternatively, you may want to consider migrating to the [test-runner](./MIGRATION.test-runner.md), which is more powerful, runs against a real browser with [Playwright](https://playwright.dev/), provides multi-browser support, and more. ## Getting started -The first thing you have to do is to disable your storyshots tests. You can keep it while doing the migration, as it might be helpful in the process, but your ultimate goal is to remove `@storybook/addon-storyshots`. +We recommend you turn off your current storyshots tests to start the migration process. To do this, rename the configuration file (i.e., `storybook.test.ts` or similar) to a different name. This will prevent the tests from being detected, as you'll be creating a new testing configuration file with the same name. By doing this, you'll be able to preserve your existing tests while transitioning to portable stories. -### 1 - Disable your existing storyshots test +### 1 - Import project-level annotations from Storybook -Rename your `storybook.test.ts` (or whatever your storyshots test is called) to `storybook.test.ts.old`. This will disable the test from being detected, and allow you to create an updated test file with the same name. +If you need project-level annotations to be included in your tests, such as [decorators](https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators), styles or any other features applied to your `.storybook/preview.js|ts` file, adjust your test set up file to import the annotations from Storybook as follows: -### 2 - Import project level annotations from Storybook +```ts +// your-setup-file.ts -If you need project level annotations such as decorators, styles, or anything that is applied to your stories via your `.storybook/preview` file, you will have to add the following code to your test setup file. Please refer to the documentation from [Jest](https://jestjs.io/docs/configuration#setupfilesafterenv-array) or [Vitest](https://vitest.dev/config/#setupfiles) on setup files. +// Adjust the import based on the supported framework or Storybook's testing libraries (e.g., react, testing-vue3) +import { setProjectAnnotations } from '@storybook/your-framework'; -```ts -// your-setup-file.js import * as projectAnnotations from './.storybook/preview'; -import { setProjectAnnotations } from '@storybook/react'; -// apply the global annotations from Storybook preview file +// Apply the global annotations from the Storybook preview file setProjectAnnotations(projectAnnotations); ``` -If you are using the new recommended format in your preview file, which is to have a single default export for all the configuration, you should adjust that slightly: +If you are using the new recommended format in your preview file, which is to have a single default export for all the configurations, you should adjust it accordingly: ```diff - import * as projectAnnotations from './.storybook/preview' + import projectAnnotations from './.storybook/preview' ``` -### 3 - Use the portable stories recipe +> Based on your testing framework, you might have to adjust the above code to work with your setup file. Refer to the documentation from [Jest](https://jestjs.io/docs/configuration#setupfilesafterenv-array) or [Vitest](https://vitest.dev/config/#setupfiles) on setup files for more information. -Then, create a `storybook.test.ts` file, and depending on your tool of choice, follow the recipes below. +### 2 - Configure your testing framework to use portable stories -- [Vitest](#vitest) -- [Jest](#jest) +To help you migrate from Storyshots addon to Storybook's portable stories with the `composeStories` helper API, we've prepared examples to help you get started. Listed below are examples of two of the most popular testing frameworks: [Jest](https://jestjs.io/) and [Vitest](https://vitest.dev/). We recommend placing the code in a newly created `storybook.test.ts` file and adjusting the code accordingly, depending on your testing framework. Both examples below will: -#### Vitest - -This recipe will do the following: +- Import all story files based on a glob pattern +- Iterate over these files and use `composeStories` on each of their modules, resulting in a list of renderable components from each story +- Cycle through the stories, render them, and snapshot them -1. Import all story files based on a glob pattern -2. Iterate over these files and use `composeStories` on each of their modules, resulting in a list of renderable components from each story -3. Iterave over the stories, render them and snapshot them +#### Vitest -Fill in your `storybook.test.ts` file with the following recipe. Please read the code comments to understand +If you're using [Vitest](https://vitest.dev/) as your testing framework, you can begin migrating your snapshot tests to Storybook's portable stories with the `composeStories` helper API by referring to the following example. You will need to modify the code in your `storybook.test.ts` file as follows: ```ts +// storybook.test.ts + // @vitest-environment jsdom + +// Replace your-framework with one of the supported Storybook frameworks (react, vue3) +import type { Meta, StoryFn } from '@storybook/your-framework'; + import { describe, expect, test } from 'vitest'; -import { render } from '@testing-library/react'; -import { composeStories } from '@storybook/react'; -import type { Meta, StoryFn } from '@storybook/react'; + +// Replace your-testing-library with one of the supported testing libraries (e.g., react, vue) +import { render } from '@testing-library/your-testing-library'; + +// Adjust the import based on the supported framework or Storybook's testing libraries (e.g., react, testing-vue3) +import { composeStories } from '@storybook/your-framework'; type StoryFile = { default: Meta; @@ -112,7 +113,7 @@ const compose = (entry: StoryFile): ReturnType> }; function getAllStoryFiles() { - // Place the glob you want to match your stories files + // Place the glob you want to match your story files const storyFiles = Object.entries( import.meta.glob('./stories/**/*.(stories|story).@(js|jsx|mjs|ts|tsx)', { eager: true, @@ -126,7 +127,7 @@ function getAllStoryFiles() { }); } -// recreate similar options to storyshots, place your configuration below +// Recreate similar options to storyshots. Place your configuration below const options = { suite: 'Storybook Tests', storyKindRegex: /^.*?DontTest$/, @@ -141,7 +142,7 @@ describe(options.suite, () => { const title = meta.title || componentName; if (options.storyKindRegex.test(title) || meta.parameters?.storyshots?.disable) { - // skip component tests entirely if they are disabled + // Skip component tests if they are disabled return; } @@ -149,8 +150,7 @@ describe(options.suite, () => { const stories = Object.entries(compose(storyFile)) .map(([name, story]) => ({ name, story })) .filter(({ name, story }) => { - // Create your own logic to filter stories here if you like. - // This is recreating the default behavior of storyshots. + // Implements a filtering mechanism to avoid running stories that are disabled via parameters or that match a specific regex mirroring the default behavior of Storyshots. return !options.storyNameRegex?.test(name) && !story.parameters.storyshots?.disable; }); @@ -161,12 +161,12 @@ describe(options.suite, () => { } stories.forEach(({ name, story }) => { - // Instead of not running the test, you can create logic to skip it instead, so it's shown as skipped in the test results. + // Instead of not running the test, you can create logic to skip it, flagging it accordingly in the test results. const testFn = story.parameters.storyshots?.skip ? test.skip : test; testFn(name, async () => { const mounted = render(story()); - // add a slightly delay to allow a couple render cycles to complete, resulting in a more stable snapshot. + // Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot. await new Promise((resolve) => setTimeout(resolve, 1)); expect(mounted.container).toMatchSnapshot(); @@ -177,23 +177,23 @@ describe(options.suite, () => { }); ``` -The snapshots will all be aggregated in a single `storybook.test.ts.snap` file. If you had storyshots configured with multisnapshots, you should change the above recipe a little to use `toMatchFileSnapshot` from vitest: +Running this example will generate a single snapshot file (i.e., `storybook.test.ts.snap`) with all the stories. However, if you were previously using a multi-snapshot configuration with the Storyshots addon, you can adjust the example above to include Vitest's [`toMatchFileSnapshot`](https://vitest.dev/guide/snapshot.html#file-snapshots) API. For example: ```ts -// ...everything else +// ...Code omitted for brevity describe(options.suite, () => { - // πŸ‘‡ add storyDir in the arguments list + // πŸ‘‡ Add storyDir in the arguments list getAllStoryFiles().forEach(({ filePath, storyFile, storyDir }) => { - // ...existing code + // ...Previously existing code describe(title, () => { - // ...existing code + // ...Previously existing code stories.forEach(({ name, story }) => { - // ...existing code + // ...Previously existing code testFn(name, async () => { - // ...existing code + // ...Previously existing code - // πŸ‘‡ define the path to save the snapshot to: + // πŸ‘‡ Define the path to save the snapshot to: const snapshotPath = path.join( storyDir, options.snapshotsDirName, @@ -207,33 +207,34 @@ describe(options.suite, () => { }); ``` -This will result in separate snapshot files per story, located near their stories file e.g.: +When the example above runs, it will generate individual snapshot files, one per story, using the following naming convention and location: ``` components/Button/Button.stories.ts components/Button/__snapshots__/Primary.storyshot components/Button/__snapshots__/Secondary.storyshot -// ... ``` #### Jest -This recipe will do the following: - -1. Import all story files based on a glob pattern -2. Iterate over these files and use `composeStories` on each of their modules, resulting in a list of renderable components from each story -3. Iterave over the stories, render them and snapshot them - -Fill in your of your `storybook.test.ts` file with the following recipe: +If you're using Jest as your testing framework, you can begin migrating your snapshot tests to Storybook's portable stories with the `composeStories` helper API by referring to the following example. You will need to modify the code in your `storybook.test.ts` file as follows: ```ts // storybook.test.ts + import path from 'path'; import * as glob from 'glob'; + +// Replace your-framework with one of the supported Storybook frameworks (react, vue3) +import type { Meta, StoryFn } from '@storybook/your-framework'; + import { describe, test, expect } from '@jest/globals'; -import { render } from '@testing-library/react'; -import { composeStories } from '@storybook/react'; -import type { Meta, StoryFn } from '@storybook/react'; + +// Replace your-testing-library with one of the supported testing libraries (e.g., react, vue) +import { render } from '@testing-library/your-testing-library'; + +// Adjust the import based on the supported framework or Storybook's testing libraries (e.g., react, testing-vue3) +import { composeStories } from '@storybook/your-framework'; type StoryFile = { default: Meta; @@ -262,7 +263,7 @@ function getAllStoryFiles() { }); } -// recreate similar options to storyshots, place your configuration below +// Recreate similar options to Storyshots. Place your configuration below const options = { suite: 'Storybook Tests', storyKindRegex: /^.*?DontTest$/, @@ -277,6 +278,7 @@ describe(options.suite, () => { const title = meta.title || componentName; if (options.storyKindRegex.test(title) || meta.parameters?.storyshots?.disable) { + // Skip component tests if they are disabled return; } @@ -284,8 +286,7 @@ describe(options.suite, () => { const stories = Object.entries(compose(storyFile)) .map(([name, story]) => ({ name, story })) .filter(({ name, story }) => { - // Create your own logic to filter stories here if you like. - // This is recreating the default behavior of storyshots. + // Implements a filtering mechanism to avoid running stories that are disabled via parameters or that match a specific regex mirroring the default behavior of Storyshots. return !options.storyNameRegex.test(name) && !story.parameters.storyshots?.disable; }); @@ -296,12 +297,12 @@ describe(options.suite, () => { } stories.forEach(({ name, story }) => { - // Instead of not running the test, you can create logic to skip it instead, so it's shown as skipped in the test results. + // Instead of not running the test, you can create logic to skip it, flagging it accordingly in the test results. const testFn = story.parameters.storyshots?.skip ? test.skip : test; testFn(name, async () => { const mounted = render(story()); - // add a slightly delay to allow a couple render cycles to complete, resulting in a more stable snapshot. + // Ensures a consistent snapshot by waiting for the component to render by adding a delay of 1 ms before taking the snapshot. await new Promise((resolve) => setTimeout(resolve, 1)); expect(mounted.container).toMatchSnapshot(); }); @@ -311,25 +312,28 @@ describe(options.suite, () => { }); ``` -The snapshots will all be aggregated in a single `__snapshots__/storybook.test.ts.snap` file. If you had storyshots configured with multisnapshots, you can change the above recipe a little by using `jest-specific-snapshot` (you will have to install this dependency): +Running this example will generate a single snapshot file (i.e., `__snapshots__/storybook.test.ts.snap`) with all the stories. However, if you were previously using a multi-snapshot configuration with the Storyshots addon, you can adjust the example above to include the [`jest-specific-snapshot`](https://github.com/igor-dv/jest-specific-snapshot) package. For example: ```ts -// πŸ‘‡ augment expect with jest-specific-snapshot +// storybook.test.ts + +// πŸ‘‡ Augment expect with jest-specific-snapshot import 'jest-specific-snapshot'; -// ...everything else + +// ...Code omitted for brevity describe(options.suite, () => { - // πŸ‘‡ add storyDir in the arguments list + //πŸ‘‡ Add storyDir in the arguments list getAllStoryFiles().forEach(({ filePath, storyFile, storyDir }) => { - // ...existing code + // ...Previously existing code describe(title, () => { - // ...existing code + // ...Previously existing code stories.forEach(({ name, story }) => { - // ...existing code + // ...Previously existing code testFn(name, async () => { - // ...existing code + // ...Previously existing code - // πŸ‘‡ define the path to save the snapshot to: + //πŸ‘‡ Define the path to save the snapshot to: const snapshotPath = path.join( storyDir, options.snapshotsDirName, @@ -343,23 +347,22 @@ describe(options.suite, () => { }); ``` -This will result in separate snapshot files per component, located near their stories file e.g.: +When the example above runs, it will generate individual snapshot files, one per story, using the following naming convention and location: ``` components/__snapshots__/Button.stories.storyshot components/__snapshots__/Header.stories.storyshot components/__snapshots__/Page.stories.storyshot -// ... ``` -### 4 - (Optional) extend your testing recipe +### 3 - Remove Storyshots from your project -The aforementioned recipes will only get you so far, depending on how you used storyshots. If you used it for image snapshot testing, acessibility testing, or other scenarios, you can extend the recipe to suit your needs. You can also consider using [the Storybook test-runner](https://github.com/storybookjs/test-runner), which provides solutions for such use cases as well. +After you confirm that the portable stories solution suits your needs, delete your old storyshots test file and uninstall `@storybook/addon-storyshots` from your project. -### 5 - Remove storyshots from your project +### 4 - (Optional) Extend your testing coverage -Once you make sure that the portable stories solution suits you, make sure to remove your old storyshots test file and uninstall `@storybook/addon-storyshots`. +The examples above will give you the closest possible experience with the Storyshots addon. However, if you are using Storyshots for other use cases, such as accessibility testing, image snapshot testing, or different testing scenarios, you can extend them to suit your needs or extend your testing solution to use the [Storybook test-runner](https://github.com/storybookjs/test-runner), that provides out-of-the-box solutions for such use cases. -### 6 - Provide feedback +### 5 - Provide feedback -We are looking for feedback on your experience, and would really appreciate if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! +We are looking for feedback on your experience and would appreciate it if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! diff --git a/MIGRATION.test-runner.md b/MIGRATION.test-runner.md index e24531cb..70153b88 100644 --- a/MIGRATION.test-runner.md +++ b/MIGRATION.test-runner.md @@ -28,13 +28,31 @@ Before you begin the migration process, ensure that you have: - [ ] Familiarity with your current Storybook and its testing setup. > **Note** -> If you are coming from a highly complex storyshots setup, which includes snapshot serializers, tons of mocking, etc. and end up hitting a few bumps in this migration, you might consider checking the [portable stories](./MIGRATION.portable-stories.md) migration. +> If you're using a complex Storyshots setup that involves snapshot serialization, mocking, and other advanced features, and you are experiencing issues while migrating to the test-runner, you might want to consider taking a look at the [portable stories](./MIGRATION.portable-stories.md) migration guide. ## What is the Storybook Test Runner? -The [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) turns all of your stories into executable tests, powered by [Jest](https://jestjs.io/)and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing and more. +The [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/test-runner) turns your stories into executable tests. Powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/). It's powerful and provides multi-browser testing, and you can achieve many things with it, such as smoke testing, DOM snapshot testing, Accessibility testing, Visual Regression testing, and more. -Check [this video](https://www.youtube.com/watch?v%253DwEa6W8uUGSA) for a quick look on the test-runner. +## Storyshots x Test Runner Comparison table + +| | Storyshots | Test runner | +| -------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Coverage reports | βœ… | βœ… | +| Access parameters in tests | βœ… | βœ… | +| DOM snapshots testing | βœ… | βœ… | +| Visual snapshot testing | βœ… with puppeteer | βœ… | +| A11y tests | βœ… | βœ… | +| Extra customization | βœ… via `initStoryshots` | βœ… via `--eject` | +| Run subset of tests | βœ… storyKindRegex + storyNameRegex | βœ… via story tags | +| Skip story via parameter | βœ… via parameters | βœ… via story tags | +| Custom test function | βœ… | βœ… | +| Interaction testing | ❌ | βœ… | +| Real Browser | ❌ | βœ… | +| Cross browser testing | ❌ | βœ… | +| Parallel Testing | ❌ | βœ… | +| storyStoreV7 compatibility | ❌ | βœ… | +| React Native support | βœ… | βœ… via [@storybook/addon-react-native-web](https://storybook.js.org/addons/@storybook/addon-react-native-web) | ## Storyshots x Test Runner Comparison table @@ -60,14 +78,14 @@ Check [this video](https://www.youtube.com/watch?v%253DwEa6W8uUGSA) for a quick ### Replacing `@storybook/addon-storyshots` with `@storybook/test-runner`: -First, remove the `@storybook/addon-storyshots` dependency and add the `@storybook/test-runner`: +Remove the `@storybook/addon-storyshots` dependency and add the `@storybook/test-runner`: ```sh yarn remove @storybook/addon-storyshots yarn add --save-dev @storybook/test-runner ``` -Then, update your `package.json` scripts to include a `test-storybook` command: +Update your `package.json` and enable the test-runner. ```json { @@ -77,41 +95,39 @@ Then, update your `package.json` scripts to include a `test-storybook` command: } ``` -Now, run test the setup by running Storybook and the test-runner in separate terminals: +Start your Storybook with: ```sh -# Terminal 1 yarn storybook ``` +Finally, open a new terminal window and run the test-runner with: + ```sh -# Terminal 2 yarn test-storybook ``` -Check the results to ensure that tests are running as expected. +If all goes well, you should see a report of all your stories and their tests. -### Migrating storyshots features +### Migrating Storyshots features -Storyshots was quite flexible and could be used for different purposes. Below you will find different recipes based on your needs. If you were not using storyshots that extensively, you can benefit from following the recipes and improve your testing experience within Storybook. +The Storyshots addon offered a highly customizable testing solution, allowing users to extend testing coverage in various ways. However, the test-runner provides a similar experience but with a different API. Below, you will find additional examples of using the test-runner to achieve similar results as those you achieved with Storyshots. If you did not use the Storyshots addon extensively, we encourage you to read through the examples and improve your testing experience within Storybook. #### Smoke testing -Storyshots provided a `renderOnly` utility to just render the story and not check the output at all, which is useful as a low-effort way of smoke testing your components and ensure they do not error. - -The test-runner does smoke testing by default, so if you used storyshots with `renderOnly`, you don't have to configure anything extra with the test-runner. The test-runner will also assert the [play function](https://storybook.js.org/docs/react/writing-stories/play-function) of your stories, providing you a better experience and more confidence. +The Storyshots addon provided a `renderOnly` helper function that allowed you to render the story without checking the output. This helper function was helpful for smoke testing your components and ensuring they do not error. This functionality is now built into the test-runner by default and requires no additional configuration. Furthermore, the test-runner will also assert your interaction tests enabled through the [play function](https://storybook.js.org/docs/react/writing-stories/play-function), providing an extended testing experience. #### Accessibility testing -If you used [`@storybook/addon-storyshots-puppeteer`](https://storybook.js.org/addons/@storybook/addon-storyshots-puppeteer)'s `axeTest` utility to test the accessibility of your components, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#accessibility-testing +If you have used `@storybook/addon-storyshots-puppeteer`'s `axeTest` utility to check the accessibility of your components, you can achieve a similar experience with the test-runner by following this example: https://github.com/storybookjs/test-runner#accessibility-testing #### Image snapshot testing -If you used [`@storybook/addon-storyshots-puppeteer`](https://storybook.js.org/addons/@storybook/addon-storyshots-puppeteer)'s `imageSnapshot` utility to run visual regression tests of your components, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#image-snapshot +If you have used [`@storybook/addon-storyshots-puppeteer`](https://storybook.js.org/addons/@storybook/addon-storyshots-puppeteer)'s `imageSnapshot` utility to run visual regression tests of your components, you can achieve a similar experience with the test-runner by following this example: https://github.com/storybookjs/test-runner#image-snapshot #### DOM Snapshot testing -If you used storyshots default functionality for DOM snapshot testing, you can use the following recipe to achieve a similar experience with the test-runner: https://github.com/storybookjs/test-runner#dom-snapshot-html +If you have been using the default functionality of the Storyshots addon for DOM snapshot testing, you can achieve a similar experience by following this example: https://github.com/storybookjs/test-runner#dom-snapshot-html ### Troubleshooting @@ -121,19 +137,19 @@ If tests that passed in storyshots fail in the test-runner, it could be because #### Snapshot path differences -Snapshot paths and names generated by `@storybook/test-runner` differ from those by `@storybook/addon-storyshots`. You'll need to configure the test-runner to align the naming convention. +If you've enabled snapshot testing with the test-runner, the snapshot paths and names differ from those generated by the Storyshots addon. This is because the test-runner uses a different naming convention for snapshot files. Using a custom snapshot resolver, you can configure the test-runner to use the same naming convention as the Storyshots addon. -To configure the test-runner, use its `--eject` command: +Start by running the test-runner with the `--eject` flag to generate a custom configuration file that you can use to configure Jest: ```sh yarn test-storybook --eject ``` -This command will generate a `test-runner-jest.config.js` file which you can use to configure Jest. -Update the file to use a custom snapshotResolver like so: +Update the file and enable the `snapshotResolver` option to use a custom snapshot resolver: -```ts +```js // ./test-runner-jest.config.js + import { getJestConfig } from '@storybook/test-runner'; const defaultConfig = getJestConfig(); @@ -147,10 +163,11 @@ const config = { export default config; ``` -Now create a `snapshot-resolver.js` file to implement a custom snapshot resolver: +Finally, create a `snapshot-resolver.js` file to implement a custom snapshot resolver: -```ts +```js // ./snapshot-resolver.js + import path from 'path'; export default { @@ -159,7 +176,7 @@ export default { const fileNameWithoutExtension = fileName.replace(/\.[^/.]+$/, ''); const modifiedFileName = `${fileNameWithoutExtension}.storyshot`; - // make Jest generate snapshots in a path like __snapshots__/Button.storyshot + // Configure Jest to generate snapshot files using the following naming convention (__snapshots__/Button.storyshot) return path.join(path.dirname(testPath), '__snapshots__', modifiedFileName); }, resolveTestPath: (snapshotFilePath, snapshotExtension) => @@ -170,21 +187,19 @@ export default { #### HTML Snapshots Formatting -The test-runner uses `jest-serializer-html` for HTML snapshots which might have slightly different formatting than your existing snapshots. - -Additionally, you might have elements that contain random or hashed properties which might cause your snapshot tests to fail every time they run. For instance, Emotion class names, or Angular ng attributes. You can circumvent this issue by configuring the test-runner to use a custom snapshot serializer. +The test-runner uses [`jest-serializer-html`](https://github.com/algolia/jest-serializer-html) by default to serialize HTML snapshots. This may cause differences in formatting compared to your existing snapshots, even if you're using certain CSS-in-JS libraries like [Emotion](https://emotion.sh/docs/introduction) or Angular's `ng` attributes. However, you can configure the test-runner to use a custom snapshot serializer to solve this issue. -To configure the test-runner, use its `--eject` command: +Start by running the test-runner with the `--eject` flag to generate a custom configuration file that you can use to provide additional configuration options. ```sh yarn test-storybook --eject ``` -This command will generate a `test-runner-jest.config.js` file which you can use to configure Jest. -Update the file to use a custom snapshotSerializer like so: +Update the file and enable the `snapshotSerializers` option to use a custom snapshot resolver: -```ts +```js // ./test-runner-jest.config.js + import { getJestConfig } from '@storybook/test-runner'; const defaultConfig = getJestConfig(); @@ -192,7 +207,7 @@ const defaultConfig = getJestConfig(); const config = { ...defaultConfig, snapshotSerializers: [ - // use your own serializer to preprocess the HTML before it's passed onto the test-runner + // Sets up the custom serializer to preprocess the HTML before it's passed onto the test-runner './snapshot-serializer.js', ...defaultConfig.snapshotSerializers, ], @@ -201,19 +216,23 @@ const config = { export default config; ``` -Now create a `snapshot-serializer.js` file to implement a custom snapshot serializer: +Finally, create a `snapshot-serializer.js` file to implement a custom snapshot serializer: -```tsx +```js // ./snapshot-serializer.js -const jestSerializerHtml = require('jest-serializer-html'); // available as dependency of test-runner + +// The jest-serializer-html package is available as a dependency of the test-runner +const jestSerializerHtml = require('jest-serializer-html'); const DYNAMIC_ID_PATTERN = /"react-aria-\d+(\.\d+)?"/g; module.exports = { - // this will be called once expect(SomeHTMLElement).toMatchSnapshot() is called from the test-runner + /* + * The test-runner calls the serialize function when the test reaches the expect(SomeHTMLElement).toMatchSnapshot(). + * It will replace all dynamic IDs with a static ID so that the snapshot is consistent. + * For instance, from to + */ serialize(val) { - // from - // to const withFixedIds = val.replace(DYNAMIC_ID_PATTERN, 'mocked_id'); return jestSerializerHtml.print(withFixedIds); }, @@ -225,4 +244,4 @@ module.exports = { ### Provide feedback -We are looking for feedback on your experience, and would really appreciate if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! +We are looking for feedback on your experience and would appreciate it if you filled [this form](some-google-form-here) to help us shape our tooling in the right direction. Thank you so much! From a86b4a10c10227af43701830e0c138fee5f22bcb Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 10 Nov 2023 21:57:30 +0000 Subject: [PATCH 7/9] Adjust link --- MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index 77f96f70..fc33b7a9 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -24,7 +24,7 @@ Follow the [migration steps to test-runner here](./MIGRATION.test-runner.md). Portable stories are utilities from Storybook that assist in converting stories from a story file into renderable elements that can be reused in your Node tests with JSDOM with tools like [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). This is the closest you will get from storyshots, but with the caveat that you will face similar challenges, given that the tests still run in Node. If you use storyshots extensively with complex mocking mechanisms and snapshot serializers, this migration will be the simplest option. -This option is currently only available for React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/%2540storybook/addon-react-native-web)) or Vue3. However, we plan to support more renderers in the future. +This option is currently only available for React, React Native (without the [react-native-web addon](https://storybook.js.org/addons/@storybook/addon-react-native-web/)) or Vue3. However, we plan to support more renderers in the future. Follow the [migration steps to portable stories here](./MIGRATION.portable-stories.md). From 01efb07ea24bc93cbcdaf4f3ca3a7ced68842b86 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 10 Nov 2023 22:02:46 +0000 Subject: [PATCH 8/9] Fix table (C'mon Github...) --- MIGRATION.test-runner.md | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/MIGRATION.test-runner.md b/MIGRATION.test-runner.md index 70153b88..0d5f8985 100644 --- a/MIGRATION.test-runner.md +++ b/MIGRATION.test-runner.md @@ -36,26 +36,6 @@ The [Storybook test-runner](https://storybook.js.org/docs/react/writing-tests/te ## Storyshots x Test Runner Comparison table -| | Storyshots | Test runner | -| -------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| Coverage reports | βœ… | βœ… | -| Access parameters in tests | βœ… | βœ… | -| DOM snapshots testing | βœ… | βœ… | -| Visual snapshot testing | βœ… with puppeteer | βœ… | -| A11y tests | βœ… | βœ… | -| Extra customization | βœ… via `initStoryshots` | βœ… via `--eject` | -| Run subset of tests | βœ… storyKindRegex + storyNameRegex | βœ… via story tags | -| Skip story via parameter | βœ… via parameters | βœ… via story tags | -| Custom test function | βœ… | βœ… | -| Interaction testing | ❌ | βœ… | -| Real Browser | ❌ | βœ… | -| Cross browser testing | ❌ | βœ… | -| Parallel Testing | ❌ | βœ… | -| storyStoreV7 compatibility | ❌ | βœ… | -| React Native support | βœ… | βœ… via [@storybook/addon-react-native-web](https://storybook.js.org/addons/@storybook/addon-react-native-web) | - -## Storyshots x Test Runner Comparison table - | | Storyshots | Test runner | | -------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | | Coverage reports | βœ… | βœ… | @@ -133,7 +113,7 @@ If you have been using the default functionality of the Storyshots addon for DOM #### Handling unexpected failing tests -If tests that passed in storyshots fail in the test-runner, it could be because there are uncaught errors in the browser which were not detected correctly in storyshots. The test-runner treats them as failure. If this is the case, use this as an opportunity to review and fix these issues. If these errors are actually intentional (e.g. your story tests an error), then you can tell the test-runner to exclude or skip this particular story instead by using story tags. [Read more about that here](./README.md#filtering-tests-experimental). +If tests that passed in storyshots fail in the test-runner, it could be because there are uncaught errors in the browser which were not detected correctly in storyshots. The test-runner treats them as failure. If this is the case, use this as an opportunity to review and fix these issues. If these errors are actually intentional (e.g. your story tests an error), then you can tell the test-runner to exclude or skip this particular story instead by using story tags. Read more about that [here](./README.md#filtering-tests-experimental). #### Snapshot path differences From fc2e3510cdc26fbd6a150750f26f0d6bef5b3db9 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Tue, 14 Nov 2023 15:20:36 +0000 Subject: [PATCH 9/9] Addressed feedback --- MIGRATION.md | 4 ++-- MIGRATION.portable-stories.md | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index fc33b7a9..4201b016 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,8 +7,8 @@ Now that we are finally removing the old architecture in Storybook 8, Storyshots Below you will find serveral options to migrate: 1. **Storybook test-runner** is Storybook's recommended open source testing tool. -1. **Portable stories** is an alternative approach that might be an easier migration from Storyshots, and is also supported by the core team. -1. **Chromatic** is a great option if you are looking for a fully hosted service, and built by Storybook maintainers. +2. **Portable stories** is an alternative approach that might be an easier migration from Storyshots, and is also supported by the core team. +3. **Chromatic** is a great option if you are looking for a fully hosted service, and built by Storybook maintainers. You can decide for yourself which path to choose, by following the guides below. diff --git a/MIGRATION.portable-stories.md b/MIGRATION.portable-stories.md index d2f45c73..11ef9bda 100644 --- a/MIGRATION.portable-stories.md +++ b/MIGRATION.portable-stories.md @@ -42,7 +42,7 @@ Alternatively, you may want to consider migrating to the [test-runner](./MIGRATI ## Getting started -We recommend you turn off your current storyshots tests to start the migration process. To do this, rename the configuration file (i.e., `storybook.test.ts` or similar) to a different name. This will prevent the tests from being detected, as you'll be creating a new testing configuration file with the same name. By doing this, you'll be able to preserve your existing tests while transitioning to portable stories. +We recommend you turn off your current storyshots tests to start the migration process. To do this, rename the configuration file (i.e., `storybook.test.ts` or similar) to `storybook.test.ts.old`. This will prevent the tests from being detected, as you'll be creating a new testing configuration file with the same name. By doing this, you'll be able to preserve your existing tests while transitioning to portable stories. ### 1 - Import project-level annotations from Storybook @@ -60,6 +60,9 @@ import * as projectAnnotations from './.storybook/preview'; setProjectAnnotations(projectAnnotations); ``` +> **Note**: +> If you're using Vue3, you must install the [`@storybook/testing-vue3`](https://storybook.js.org/addons/@storybook/testing-vue3) package to use the `setProjectAnnotations` API in your setup file and the `composeStories` API in your existing tests. + If you are using the new recommended format in your preview file, which is to have a single default export for all the configurations, you should adjust it accordingly: ```diff @@ -361,7 +364,7 @@ After you confirm that the portable stories solution suits your needs, delete yo ### 4 - (Optional) Extend your testing coverage -The examples above will give you the closest possible experience with the Storyshots addon. However, if you are using Storyshots for other use cases, such as accessibility testing, image snapshot testing, or different testing scenarios, you can extend them to suit your needs or extend your testing solution to use the [Storybook test-runner](https://github.com/storybookjs/test-runner), that provides out-of-the-box solutions for such use cases. +The examples above will give you the closest possible experience with the Storyshots addon. However, if you are using Storyshots for other use cases, such as accessibility testing, image snapshot testing, or different testing scenarios, you can extend them to suit your needs or extend your testing solution to use the [Storybook test-runner](https://github.com/storybookjs/test-runner), that offers a similar experience, with minimal changes to your existing testing setup. You can read more about it in the test-runner [migration guide](./MIGRATION.test-runner.md). ### 5 - Provide feedback