You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is it possible to monitor React states from Storybook side for stability purposes? The goal would be to wait for the component to be ready (after state or prop changes) before making test assertions. I know that the @storybook/test package provides the waitFor function to evaluate test assertions until a timeout expires, but I would like to have a more sophisticated and stable solution.
I was able to implement this successfully with React class components, but I would like to be able to do the same with React functional components. This solution uses spyOn which spies on componentDidUpdate lifecycle function to return { fetchIsComplete: true, componentIsReady: true } before asserting the final state of the component.
TestComponent.tsx
import React, { Component } from 'react';
interface TestComponentState {
data: string;
fetchIsComplete: null | boolean;
}
/**
* Sample component that fetches data and displays it, using React
* class-based syntax and lifecycle methods.
*/
class TestComponent extends Component<Readonly<{}>, TestComponentState> {
constructor(props) {
super(props);
this.state = {
data: 'Hello',
fetchIsComplete: null
};
this.fetchData = this.fetchData.bind(this);
}
// Checks the current state of the component after an update was performed,
// and returns true if it is in a ready state.
componentDidUpdate(
prevProps: Readonly<{}>,
prevState: TestComponentState,
snapshot?: any
): Object {
return this.state.fetchIsComplete
? { fetchIsComplete: this.state.fetchIsComplete, componentIsReady: true }
: { fetchIsComplete: this.state.fetchIsComplete, componentIsReady: false };
}
async fetchData() {
this.setState({ data: 'Fetching data...', fetchIsComplete: false });
// simulate fetch, then change state
await new Promise(res => setTimeout(res, 5000));
this.setState({ data: 'Sample data', fetchIsComplete: true });
}
render(): JSX.Element {
return (
<div>
<h1 style={{ color: 'white' }} data-auto="data">
{this.state.data}
</h1>
<button data-auto="fetch-data-btn" onClick={this.fetchData}>
Fetch data
</button>
</div>
);
}
}
export default TestComponent;
TestComponent.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { expect, spyOn, userEvent, waitFor, within } from '@storybook/test';
import React from 'react';
import TestComponent from './TestComponent';
const meta: Meta<typeof TestComponent> = {
component: TestComponent
};
export default meta;
type Story = StoryObj<typeof meta>;
export const FetchDataExample: Story = {
render: () => {
return <TestComponent />;
},
play: async ({ canvasElement }) => {
const dataElement = await within(canvasElement).findByTestId('data');
const fetchDataElement = await within(canvasElement).findByTestId('fetch-data-btn');
const spyComponentUpdate = spyOn(TestComponent.prototype, 'componentDidUpdate');
// initial state
expect(dataElement.textContent).toBe('Hello');
// user interaction
await userEvent.click(fetchDataElement);
// wait for the componentDidUpdate to be called and component to be ready for assertions
await waitFor(
() => {
expect(spyComponentUpdate).toBeCalled();
expect(spyComponentUpdate).toReturnWith({ fetchIsComplete: true, componentIsReady: true });
},
{ timeout: 10000, interval: 500 } // check every 500 ms (times out after 10000 ms)
);
// verify new state
expect(dataElement.textContent).toBe('Sample data');
}
};
Additional information
No response
Create a reproduction
No response
The text was updated successfully, but these errors were encountered:
Discussed in #30210
Originally posted by dpizzolongo January 7, 2025
Summary
Is it possible to monitor React states from Storybook side for stability purposes? The goal would be to wait for the component to be ready (after state or prop changes) before making test assertions. I know that the
@storybook/test
package provides thewaitFor
function to evaluate test assertions until a timeout expires, but I would like to have a more sophisticated and stable solution.I was able to implement this successfully with React class components, but I would like to be able to do the same with React functional components. This solution uses
spyOn
which spies oncomponentDidUpdate
lifecycle function to return{ fetchIsComplete: true, componentIsReady: true }
before asserting the final state of the component.TestComponent.tsx
TestComponent.stories.tsx
Additional information
No response
Create a reproduction
No response
The text was updated successfully, but these errors were encountered: