[RFC]: Add the .test method to CSF factories #30119
Replies: 16 comments 19 replies
-
|
This looks great and is better than what I was imagining when commenting on #22361 I love that you've put tons of thought into this especially things like them not cluttering the sidebar until you open the parent story, just come up with an awesome icon for them 😄 |
Beta Was this translation helpful? Give feedback.
-
|
A derivative of the AAA pattern consists of writing data suites for tests, and then running tests on each item of a data suite. How could we do that with stories acting as test data? Could I bundle together a bunch of stories (e.g. different variants of a button) and check they all satisfy a behaviour (e.g. all become aria-disabled once clicked and until the click handler returns, or all contain an aria-label and role button)? I don't know how this should be written so here are a bunch of proposals in no particular order and to which I have no particular attachment, just for the sake of triggering conversation. // meta providing an each method?
meta
.each([Primary, Secondary, Tertiary, Frogginary])
.test('prevent double-submit by setting aria-disabled while the handler is running for variant %s', () => {
// act
// assert
})
// With a test global?
test
.each([Primary, Secondary, Tertiary, Frogginary])
('prevent double-submit by setting aria-disabled while the handler is running for variant %s', () => {
// implicit arrange?
// act
// assert
})
// If there is a test global, does it enable other syntax formats, e.g.
test('prevent double-submit by setting aria-disabled while the handler is running', () => {
arrange(Primary); // or render(Primary), etc. naming here indicates purpose and is not an actual suggestion
// act
// assert
})
// Leading to an explicit handling of arrange in test.each?
test
.each([Primary, Secondary, Tertiary, Frogginary])
('prevent double-submit by setting aria-disabled while the handler is running for variant %s', (story) => {
arrange(story);
// act
// assert
}) |
Beta Was this translation helpful? Give feedback.
-
|
This would be a dream come true, I wholeheartedly approve of this proposal. The tiniest bit of feedback would be perhaps we can try to visually distinguish the tests in the sidebar when expanded so that they're more obviously "this is a test rather than a traditional story", that would be great since we also use Storybook for non-technical folks to be able to explore the current state of the application development. Perhaps something like replacing the bookmark with a small "T" or "Test" (space depending) in some kind of badge styling? |
Beta Was this translation helpful? Give feedback.
-
|
Do Storybook features like tags and parameters apply to these tests? |
Beta Was this translation helpful? Give feedback.
-
|
This looks awesome! This system could also benefit the ability to provide afterEach, beforeEach, ie. for tests in global or local scope. When Vitest is already being used under the hood implementing more vitest functionalities in this .test method would provide more usecases for this and might make writing Vitest tests unessessary in the future. |
Beta Was this translation helpful? Give feedback.
-
|
I love this! We've having similar organizational challenges here at Brex with the current approach. Having a clear separation between what's interaction testing and what's meant as documentation/snapshot testing would be awesome |
Beta Was this translation helpful? Give feedback.
-
|
Excellent proposal. I love the syntax, and love the idea of using I do have a suggestion for the UI. IMO, the sidebar is not the right place to show these tests. Each storybook "tells a story" and the sidebar is how we navigate that story. So I would suggest these tests belong in the bottom "Component Tests" panel. It currently shows the steps of the current test. Imagine if this panel was split, and the left side was the list of tests. You select one from the list and watch it run. |
Beta Was this translation helpful? Give feedback.
-
|
Hi, I mentioned this feedback in yesterday's Storybook 9 webinar and was asked to share it here: I use BDD-style (i.e. nested With this RFC, I would like to be able to use all of |
Beta Was this translation helpful? Give feedback.
-
|
This is grand! What is the status? Is there a branch or (draft) PR to look at? |
Beta Was this translation helpful? Give feedback.
-
|
@shilman might be worth doing something like this via test.extend, you could probably model the same APIs where extend here would be able to allow you to have typed contexts for the test, and potentially use it to setup the metadata for storybook. |
Beta Was this translation helpful? Give feedback.
-
|
Hey everyone! Thank you so much for your comments and thoughts. While we can still add comments in this RFC, I'd like to invite you all to a new channel in our discord, #sb-test-early-access. We will be implementing this RFC and would like to soon provide you canary versions with some prototypes for early feedback. We'd also like to hear more about your motivations, needs, etc, so we can note them down to consider. Please join and share your thoughts! Pinging you all of you who have shown interest ❤️ |
Beta Was this translation helpful? Give feedback.
-
|
Are there any plans to support Svelte CSF? |
Beta Was this translation helpful? Give feedback.
-
|
LFG, I think |
Beta Was this translation helpful? Give feedback.
-
|
Are there any plans to allow separating tests from the story files? In general, I love the idea of getting visual feedback for my tests in Storybook, but I think mixing stories with tests might bloat the story files. Perhaps the solution is bringing tests based on portable stories from .test files into the Storybook UI? |
Beta Was this translation helpful? Give feedback.
-
|
I've started using |
Beta Was this translation helpful? Give feedback.
-
|
what version is this on? can't seem to get it to work on 10.1.2 |
Beta Was this translation helpful? Give feedback.



Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Note
This is now available experimentally in Storybook 10 pre-releases for React-based frameworks, when you enable it via the
experimentalTestSyntaxfeature flag.We have adapted this RFC to reflect the current state of the feature and serve as an API reference and usage guide.
ℹ️ Why we're proposing this
We've learned over the years that many teams use or want to use Storybook to test their components. That's why we've built interaction testing, the test runner, and our Vitest addon.
We've also learned that learning a new testing API can be a barrier to adoption. Many teams are already familiar with tools like Vitest and Jest, and would prefer to use similar syntax when writing tests in Storybook.
At the same time, there's a competing concern about cluttering the Storybook sidebar with too many stories. If every test case is a separate story, it can quickly become overwhelming and hard to navigate.
That's why we're proposing to add the
.testmethod to CSF Factories, which allows you to write tests using a syntax more similar to traditional testing tools (while still sharing most of its API with the play function) and presents them in a way that reduces clutter in the sidebar.This approach has several benefits:
📺 Prefer video? We just hosted a webinar covering both CSF Factories and the .test method.
What is it?
The
.testmethod is a new addition to the Component Story Format (CSF) Factories. It allows you to define test cases for your stories directly within the story file, using a syntax more similar to traditional testing tools like Vitest or Jest.Here's an example stories file using CSF Factories, which defines a test with the
.testmethod:Tip
Need more details on CSF Factories? Check the docs!
Importantly, tests have different presentation than stories in the Storybook UI:
Here's how the above is shown in Storybook's UI, after the Disabled story's tests have been expanded:
Get started
Tip
Just want to give it a quick spin? You can try it out here:
To use the
.testmethod in your project:Upgrade to the latest Storybook pre-release:
Run the automigration to convert your stories to use the new CSF Factories format:
When you run this command, you will be prompted with two choices.
First, you can choose to use relative imports (recommended) or absolute imports within your story files.
Second, you will prompted for a glob pattern to match the story files that you want to convert. We strongly recommend first running the command with a pattern that matches only a few files, to verify that the conversion works as expected. Once you are happy with the results, you can run the command again with a broader pattern to convert all your stories.
After answering, the automigration will update your
.storybook/preview.js|ts,.storybook/main.js|ts, and requested story files to use the new CSF Factories format. Please see the CSF Factories documentation for more details.Enable the
experimentalTestSyntaxfeature flag in yourmain.ts:You can now add tests to your stories using the
.testmethod, which we'll cover in the next section. When you do, they'll appear in the sidebar, nested under their parent story.Writing tests
The
.testmethod is called on a story, and takes two (or three) arguments: a name for the test and an async function that contains the test code. The test function receives a context object, which includes the same properties as theplayfunction context, such ascanvas,userEvent,args, andstep.A few things to note:
beforeEach, steps, etc. If/when the.testmethod becomes stable, that documentation will be updated to reflect both APIs.When to write a test
Tests can be used to verify just about any aspect of a component, e.g. visual appearance, interactivity, or accessibility. However, they are particularly well suited for testing non-visual logic and behavior, such as:
When to write a test vs. a story
Let's look more closely at the last example above, because it can help illustrate when to write a test vs. a story.
Imagine we have an email input component, and we want to ensure that it correctly validates email addresses. We can create stories for some known valid and invalid email addresses, which we can visually test to ensure their appearance. That covers the basic cases.
But there are multiple paths that we also need to verify to have full coverage of our invalidation logic. Each of these paths all produce similar visual results and aren't useful as examples to learn from, so we don't need separate stories for each one. Doing so wouldn't provide helpful documentation for our teammates and would likely be considered noise in our sidebar.
Instead, we can write tests to cover these cases. Because they're nested under the parent story, they don't clutter the sidebar, but are still easily accessed when we need them.
In general, if the result of your story or test serves as documentation or can be referenced as an example, it should be a story. If it is purely for verification and doesn't add to the documentation value of your component, it should be a test.
When to write a test vs. a play function
Everything tested in play function can be done in a test instead. However, that doesn’t mean you should remove or stop using all of your play functions. They are still useful for placing your components in a usable state (e.g. opening a select menu).
Stories with play functions can also be combined with tests. You could have a story for a Select component which defines the
argsand aplayfunction that opens the menu. And that story could have tests to verify that the correct menu items are displayed.Although test and play are in many ways interchangeable, the big difference is how they appear in the sidebar: tests are nested under stories and hidden by default (they're also hidden entirely from documentation). That means that choosing between test and play comes down to the non-testing aspects: Will the example be useful as documentation? Do you think you'll reference it with colleagues? Then choose a play function. Otherwise, choose a test.
In other words, for teams choosing to use tests in their project, we believe the most typical usage will break down something like this:
Using tests
Tests can, of course, be tested with the Vitest addon, either in the Storybook UI with the test widget or in your CLI/CI.
They also integrate with Storybook 10's improved sidebar filtering. Once you have a test in your project, you will see a new Testing item in your filter menu:
You can choose to include tests, which will filter the sidebar to only show tests. Or you can exclude them, which hides them from the sidebar. By default, neither selection is made, but this behavior can be configured (see the "Exclude all tests from the sidebar" technique, below).
Advanced techniques
Overwriting annotations in a test
If you’re writing a test against a story and need to adjust the context (
tags,parameters, etc.), you can do so by passing a context object (the same shape as the context argument formeta.story()) as the second argument, in between the name and callback function:Reusing test logic
You can reuse logic between multiple tests by extracting it into a function and calling it within each test:
Exclude all tests from the sidebar
By default, tests are nested and collapsed under their stories. But you can also filter them out entirely, so that they're only visible by deselecting the filter in the menu. To do this, configure the built-in
_testtag, which is automatically applied to all tests.You can make this conditional, e.g. dependent on an environment variable to only filter them out in the published Storybook.
Proposed future work
All of the above is available today, in Storybook 10. The following work is being considered for future releases:
Sharing data
We can share data between the story and the test in a typesafe way.
One possible API would be to return that data from the story's
playfunction, and receive it as part of the test's callback context:Requested feedback
Thanks for considering this proposal! We'd love to know any thoughts or suggestions you have, particularly:
.testsyntax feel?.test? Even if only for new test cases?And finally, a reminder that you do not need to use
.test(which is still experimental) to use CSF Factories (which is part of CSF Next, currently in preview, the stage before stable). So if you migrated your stories to use CSF Factories as part of evaluating this RFC, you should keep that part!Beta Was this translation helpful? Give feedback.
All reactions