Description
Describe the bug
When a story render-function uses the useArgs-hook to change the story args in the middle of a play-function, the test-runner stops running the play-function at that point and instead acts as if the interaction was successful
Steps to reproduce the behavior
Full example react story:
import type { Meta, StoryObj } from "@storybook/react";
import { useArgs } from "@storybook/client-api";
import { userEvent, waitFor, within } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
type SomeArgs = {
cnt: number;
};
const meta: Meta<SomeArgs> = {
title: "Example/SomeStory",
};
export default meta;
type Story = StoryObj<SomeArgs>;
export const SomeStory: Story = {
args: {
cnt: 0,
},
render: () => {
const [args, updateArgs] = useArgs();
return (
<>
<p>
Count: <span data-testid={"cnt"}>{args.cnt}</span>
</p>
<button onClick={() => updateArgs({ cnt: args.cnt + 1 })}>
Increment
</button>
</>
);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await waitFor(() =>
expect(canvas.getByTestId("cnt")).toHaveTextContent("0")
);
await userEvent.click(canvas.getByText("Increment"));
// BUG: nothing after the click (which calls updateArgs) is executed in the test-runner:
await waitFor(() =>
expect(canvas.getByTestId("cnt")).toHaveTextContent("-77")
);
expect(5).toBe(10);
},
};
Expected behavior
When running "npm run test-storybook", I expect the test-runner to fail because 1 isn't equal to -77 (or because 5 isn't equal to 10), just like it correctly does in the browser in the "Interactions"-tab
Actual behavior
$ npm run test-storybook
> [email protected] test
> test-storybook
PASS browser: chromium stories/Header.stories.ts
PASS browser: chromium stories/Button.stories.ts
PASS browser: chromium stories/SomeStory.stories.tsx
Test Suites: 3 passed, 3 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 4.568 s
Ran all test suites.
Environment
- OS: Windows
- Node.js version: v18.12.1
- NPM version: 8.19.2
- package.json dependencies:
"dependencies": {
"@types/node": "20.1.4",
"@types/react": "18.2.6",
"@types/react-dom": "18.2.4",
"autoprefixer": "10.4.14",
"eslint": "8.40.0",
"eslint-config-next": "13.4.2",
"next": "13.4.2",
"postcss": "8.4.23",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.2",
"typescript": "5.0.4"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.11",
"@storybook/addon-interactions": "^7.0.11",
"@storybook/addon-links": "^7.0.11",
"@storybook/blocks": "^7.0.11",
"@storybook/jest": "^0.1.0",
"@storybook/nextjs": "^7.0.11",
"@storybook/react": "^7.0.11",
"@storybook/test-runner": "^0.10.0",
"@storybook/testing-library": "^0.0.14-next.2",
"eslint-plugin-storybook": "^0.6.12",
"storybook": "^7.0.11"
}
Additional context
The same issue also occurs with angular instead of react, and it also already occurred (at least with angular) with storybook 6.5.14
The goal of using the useArgs-hook during a play-function is to enable changing the args during an interaction, which would be very useful for testing certain component behaviors, see storybookjs/storybook#17140 . Currently, due to this bug, there seems to be no way to write tests in the style of "first the component was rendered with these props/Inputs, then they got changed, I want to test whether the component reacted to the change correctly, updated itself correctly".
If anyone has another way of changing the args in the middle of a play-function that doesn't use the useArgs-hook, please say so. The only other approach I found was modifying the "args"-object that gets passed to the play-function, however since storybook 7.0 this doesn't do anything, and under storybook 6.5.14 that did work in the browser, but also not in the test-runner.