test-runner
test-runner copied to clipboard
[Bug] Tests always run in the default viewport
Describe the bug
The test runner runs tests full screen inside the default playwright Chrome viewport. It does not resize the viewport according to the viewport
parameter of the story.
Steps to reproduce the behavior
I have a component where a dropdown is hidden on large screens using a media query. I configured the story to use a mobile2 viewport by default and wrote a play function to test the visibility of the element:
export const Mobile = Template.bind({})
Mobile.parameters = {
viewport: {
defaultViewport: 'mobile2',
},
}
Mobile.play = async ({ canvasElement }) => {
const canvas = within(canvasElement)
const toggleButton = await canvas.findByTestId('dropdownToggle')
await waitFor(() => expect(toggleButton).toBeVisible())
}
When I run storybook locally and use the interactions addon, the test runs fine, but when I run the test runner, it fails.
I debugged the test runner using this custom test-runner-jest.config.js
, --maxWorkers 1
and by adding sleep timeouts after every line. I can see that the playwright Chrome window has the wrong size and that it loads the story iframe in fullscreen.
const { getJestConfig } = require('@storybook/test-runner');
module.exports = {
...getJestConfig(),
testEnvironmentOptions: {
'jest-playwright': {
launchOptions: {
headless: false,
}
},
},
}
Environment
- Storybook 6.4.20
-
@storybook/test-runner
0.0.4 -
@storybook/testing-library
0.0.9
Additional context
As a workaround, I wrote a prerender hook in .storybook/test-runner.js
that resizes the playwright page if the test has Mobile
in its name. A solution that does not rely on magic strings would be better though.
module.exports = {
preRender(page, story) {
if (story.name.includes('Mobile')) {
page.setViewportSize({ width: 896, height: 414 })
}
},
}
Thanks for using this library and opening this issue @schneefux! There's currently a limitation that won't allow you to get info from the viewports addon, hopefully we can add support for that soon.
@shilman WDYT of adding parameters to the context? It will make stories-json mode feel more limited but it is what it is I guess
It will make stories-json mode feel more limited.
Is that because parameters are not available in stories-json mode? This could be another good use for tags
in storybook, perhaps.
I just want to share that this is possible now with the getStoryContext/page.evaluate.
I'm using it to get storyContext.parameters.viewport.defaultViewport
and then calling page.setViewportSize(devices[storyContext.parameters.viewport.defaultViewport].viewport)
where devices is a shared list between the custom viewports I've set up in storybook (and I'm leveraging playwright's devices because why not :) const { devices } = require("playwright");
)
Thanks @VickyKoblinski! Your project must be super cool, given all the stuff you've been doing with the test runner!
TODO: add a recipe for that ☝️
We got inspired by @VickyKoblinski's answer with little changes, our stories are configured with viewports imported or customized with @storybook/addon-viewport
. So here's our little magic 🍡 :
`
const { getStoryContext } = require('@storybook/test-runner');
module.exports = {
async preRender(page, story) {
const context = await getStoryContext(page, story);
const viewPortParams = context.parameters?.viewport;
const defaultViewport = viewPortParams?.defaultViewport;
const viewport =
defaultViewport && viewPortParams.viewports[defaultViewport].styles;
const parsedViewportSizes =
viewport &&
Object.entries(viewport).reduce(
(acc, [screen, size]) => ({
...acc,
[screen]: parseInt(size),
}),
{},
);
if (parsedViewportSizes) page.setViewportSize(parsedViewportSizes);
},
};`
@ibrahimgabsi I want to note that when the viewPort is resized, it will not return to its original state for the following stories. So we have to call something like setViewportSize(globalDefaultSize) if no custom window is specified.
I slightly improved solution of @ibrahimgabsi to use built-in viewport sizes or reset to default on pages without viewport
parameter.
Create .storybook/test-runner.js
:
const { getStoryContext } = require('@storybook/test-runner');
const { MINIMAL_VIEWPORTS } = require('@storybook/addon-viewport');
const DEFAULT_VP_SIZE = { width: 1280, height: 720 };
module.exports = {
async preRender(page, story) {
const context = await getStoryContext(page, story);
const vpName = context.parameters?.viewport?.defaultViewport;
const vpParams = MINIMAL_VIEWPORTS[vpName];
if (vpParams) {
const vpSize = Object.entries(vpParams.styles).reduce(
(acc, [screen, size]) => ({
...acc,
[screen]: parseInt(size),
}),
{}
);
page.setViewportSize(vpSize);
} else {
page.setViewportSize(DEFAULT_VP_SIZE);
}
},
};
Hey peeps! I will close this issue as the solution is now available and its recipe has been documented in the README here. Thank you!