[RFC]: Add the .test method to CSF factories #30119
Replies: 4 comments 2 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.
-
RFC: Add the .test method to CSF factories
In modern frontend development, Storybook has become the go-to tool for building and documenting UI components. It allows developers to create "stories" that highlight various component states, aiding both development and documentation. Yet, integrating comprehensive testing within Storybook can be challenging. This post examines the current testing limitations in Storybook and proposes a new syntax for seamlessly incorporating tests with a jasmine-style syntax on top of static stories.
Are stories tests?
Storybook provides testing features through hooks like
play
andbeforeEach
and leverages Vitest for a fast, browser-based test runner. However, using Storybook for testing means learning a new testing syntax and best practices built on top of the Component Story Format (CSF).Every story, even those without a
play
hook, functions as a render test. Rendering a component in a known configuration and verifying that it doesn’t break is a basic yet valuable form of validation.Even though those stories are run automatically in CI with our Vitest integration, traditional tests usually have clear assertions and specifications.
The Limitations of the
play
FunctionThe
play
function in Storybook allows users to add those assertions on top of a story. However, its limitation of supporting only oneplay
function per story complicates the process of writing multiple tests against the same component state.A common workaround is to create separate "test stories" with their own
play
functions. This approach often involves duplicating base stories and adding differentplay
functions to test various behaviors:This best practice is not obvious from an API perspective, but even if you discover this, it's not ideal. These test stories clutter the sidebar, which is problematic if Storybook is used primarily for documentation. And when you click on it, you might not expect the story to take 15 seconds to finish. Additionally, using PascalCase for lengthy spec names reduces readability.
We're essentially treating
AddLabelForm
andSubmittingInvalidLabelShowsInlineError
as both a story and a test at the same time. But are they truly the same? Arguably not—they serve different purposes.Embrace “Jasmine” test syntax on top of static stories
In the CSF factories proposal, we introduced this new factory syntax for writing stories:#30112
This proposal is about adding a
.test
method on the story object.This model aligns with the natural thinking of stories as documented UI states and tests as actions performed on those states. Here, stories represent the "arrange" step of a test, while the
test
method handles the "act" and "assert" phases, mirroring familiar testing patterns.The UI
To prevent tests from cluttering the sidebar, they only appear when you open a specific story.
You can also search for specific tests in the search bar.
Play gives you extra control over the “arrange” step
We created the
args
abstraction because it enables you to quickly define your key UI states, while also allowing you to adjust props in the controls panel interactively.However, sometimes you need more flexibility. What if you must set up mocks before rendering the component? What if you need to open a dropdown after the story has been rendered?
From a testing perspective, you can think of the
play
hook as providing full control over thearrange
step in your tests:Sharing data
We can share data between the story and the test in a typesafe way:
Overwriting parameters for a test
We can support an API that overrides story props such as parameters and tags:
Beta Was this translation helpful? Give feedback.
All reactions