storybook-addon-playwright
一个用于在 Storybook 环境中对多个浏览器中的故事进行视觉测试的插件。
该插件在 Storybook 静态构建中无法正常工作,但可以针对静态构建文件测试屏幕截图。
此包仅在 React 框架中进行了测试,因此可能不适用于其他框架。
仅适用于 组件故事格式 (CSF)。
兼容性
包 | 版本 |
---|---|
storybook | ~6.4 |
playwright | ~1.17 |
动机
能够在所有浏览器中实现相同外观和感觉的组件一直是一个挑战,开发人员需要不断在浏览器之间切换并进行视觉检查。跟踪更改并尽快检测更改也很重要。因此,创建了此插件。借助 Playwright 和 Storybook,现在可以对组件进行视觉检查并在一处接收更改通知。
示例
storybook-addon-playwright-example
入门
所需软件包
- storybook-addon-playwright
- @storybook/addon-knobs
yarn add storybook-addon-playwright @storybook/addon-knobs --dev
配置
在 .storybook/main.js
中
module.exports = {
stories: ['../**/*.stories.[tj]sx'],
addons: [
'@storybook/addon-knobs/register',
'storybook-addon-playwright/preset',
'storybook-addon-playwright/register',
],
};
如果 Storybook 无法在
@storybook/addon-knobs/register
路径中找到register
,则可能需要将其指向目标文件夹:@storybook/addon-knobs/dist/register
在 .storybook/main.js
或 .storybook/middleware.js
中
const { setConfig } = require('storybook-addon-playwright/configs');
const playwright = require('playwright');
(async () => {
let browser = {
chromium: await playwright['chromium'].launch(),
firefox: await playwright['firefox'].launch(),
webkit: await playwright['webkit'].launch(),
};
setConfig({
storybookEndpoint: `https://127.0.0.1:6006/`,
getPage: async (browserType, options) => {
const page = await browser[browserType].newPage(options);
return page;
},
afterScreenshot: async (page) => {
await page.close();
},
});
})();
在 .storybook/middleware.js
中
const middleware = require('storybook-addon-playwright/middleware');
module.exports = middleware;
setConfig 选项
- storybookEndpoint
- enableMigration
- beforeScreenshot
- afterScreenshot
- beforeStoryImageDiff
- afterStoryImageDiff
- beforeFileImageDiff
- afterFileImageDiff
- beforeAllImageDiff
- afterAllImageDiff
- pageGotoOptions
- afterUrlConstruction
- afterNavigation
- releaseModifierKey
- screenshotOptions
- theme
storybookEndpoint
storybookEndpoint
必须与 Storybook 的 ip/port 匹配。
对于 Docker 和非本地浏览器,需要 Storybook 的公网 IP 地址。
enableMigration
如果设置为 true,将应用更改到插件生成的 json 文件,在 迁移
部分了解更多信息。
beforeScreenshot
将在截取屏幕截图之前调用,用于操作页面。
afterScreenshot
将在截取屏幕截图之后调用。
afterStoryImageDiff
将在对整个应用程序屏幕截图运行图像差异测试之前/之后调用。
beforeStoryImageDiff/afterStoryImageDiff
将在对特定故事运行图像差异测试之前/之后调用。
beforeFileImageDiff/afterFileImageDiff
将在对特定文件运行图像差异测试之前/之后调用。
beforeAllImageDiff/afterAllImageDiff
将在所有故事屏幕截图的图像差异处理完成后调用。
pageGotoOptions
请参阅 Playwright API page.goto 选项
afterUrlConstruction
将在 page.goto 之前调用,可用于操作 URL。
afterNavigation
在页面导航到故事时调用。
releaseModifierKey
当设置为 true 时,将在截取屏幕截图后对修改键(Shift、Meta、Control 或 Alt)执行键盘.up。
screenshotOptions
截取屏幕截图时要应用的默认选项。
theme
它将覆盖插件的默认主题。它是一个 json
,类型为 Material-UI Theme
对象。
.
.
.
setConfig({
theme: {
palette: {
primary: {
main: '#0052cc',
},
secondary: {
main: '#edf2ff',
},
},
}
});
.
.
.
工作原理
此插件本质上是 Playwright 和 Storybook 故事之间的接口。插件在配置中提供的页面上执行用户指令。
用户创建的指令将保存在故事文件旁边的 json 文件中,因此在下次加载时可以使用。
此插件包含三个部分
- 操作列表面板
- 屏幕截图列表面板
- 屏幕截图预览面板
操作列表面板
操作面板类似于一个游乐场,它包含用户为特定故事创建的操作集列表,这些操作集将在选中时在浏览器页面中执行。
一个操作集可以包含一个或多个操作。
操作指的是 Playwright 页面方法,例如点击、鼠标移动等。
屏幕截图列表面板
此面板包含用户之前截取的屏幕截图,您可以在此处管理屏幕截图,例如删除、编辑或排序屏幕截图。
屏幕截图预览面板
预览面板显示 Playwright 截取的最新屏幕截图,可以选择性地显示 Playwright 支持的所有浏览器或部分浏览器。
您可以在此处保存和更改屏幕截图设置,例如宽度、高度等。
屏幕截图保存在故事文件夹下名为 __screenshots__
的文件夹中。
添加或扩展 Playwright 页面方法
要添加或扩展 Playwright 方法,setConfig
方法中提供了以下属性
- customActionSchema
customActionSchema
此属性允许开发人员向 Playwright 页面添加新方法。customActionSchema
属性中的每个条目都将显示在 操作
面板下的“添加操作”菜单中。
此属性遵循 json-schema 规则,并带有一个名为
parameters
的附加属性,因此需要清楚地了解json-schema
。
以下是如何在页面中添加框的示例
//async function addBox(this: Page, position: { x: number; y: number })
async function addBox(position) {
await this.evaluate((pos) => {
if (!pos) return;
const div = document.createElement('div');
div.style.backgroundColor = '#009EEA';
div.style.width = '200px';
div.style.height = '200px';
div.style.position = 'absolute';
div.style.top = pos.y + 'px';
div.style.left = pos.x + 'px';
document.body.append(div);
}, position);
}
(async () => {
let browser = {
chromium: await playwright['chromium'].launch(),
firefox: await playwright['firefox'].launch(),
webkit: await playwright['webkit'].launch(),
};
setConfig({
storybookEndpoint: `https://127.0.0.1:6006/`,
getPage: async (browserType, options) => {
const page = await browser[browserType].newPage(options);
page.addBox = addBox;
return page;
},
afterScreenshot: async (page) => {
await page.close();
},
customActionSchema: {
addBox: {
type: 'promise',
parameters: {
position: {
type: 'object',
properties: {
x: {
type: 'number',
},
y: {
type: 'number',
},
},
required: ['x', 'y'],
},
},
},
},
});
})();
其他页面方法
已向 Playwright 页面添加以下自定义方法
- clearInput,
- mouseDownOnSelector
- mouseMoveToSelector
- setSelectorSize
- scrollSelector
- dragDropSelector
- takeScreenshot
- takeScreenshotOptions
- selectorMouseWheel
- mouseFromTo
clearInput
此方法使用 selector
获取元素,等待可操作性检查,聚焦元素,清除元素并触发输入事件。
mouseDownOnSelector
此方法使用 selector
获取元素,并在选择器中心执行 mousedown。
mouseMoveToSelector
此方法使用 selector
获取元素,并将鼠标移动到选择器中心。
setSelectorSize
此方法使用 selector
获取元素,并设置选择器的宽度或高度。
scrollSelector
此方法使用 selector
获取元素,并设置选择器的 scrollLeft 和 scrollTop。
dragDropSelector
此方法使用 selector
获取元素,并将其移动到用户指定的位置。
takeScreenshot
此方法将在操作之间截取屏幕截图,它对于按顺序截取事件/操作的屏幕截图很有用。最后,屏幕截图将与最终屏幕截图合并。
takeScreenshotOptions
此操作的目的是为所有屏幕截图提供集中化的选项。此操作仅可与 takeScreenshot 操作一起使用。只能使用一个实例。
selectorMouseWheel
此方法使用 selector
获取元素,并分派 WheelEvent。
mouseFromTo
此方法将执行鼠标按下、移动和抬起操作,从一个位置移动到另一个位置。
测试
使用插件保存的屏幕截图也可以使用像 Jest 这样的测试框架进行测试。为此,请按照以下步骤配置 Jest
在 jest.config.js
中添加设置文件
module.exports = {
setupFilesAfterEnv: ['./jest.setup.js'],
};
在 jest.setup.js
中
const playwright = require('playwright');
const { setConfig } = require('storybook-addon-playwright/configs');
const { toMatchScreenshots } = require('storybook-addon-playwright');
expect.extend({ toMatchScreenshots });
let browser = {};
beforeAll(async () => {
browser = {
chromium: await playwright['chromium'].launch(),
firefox: await playwright['firefox'].launch(),
webkit: await playwright['webkit'].launch(),
};
setConfig({
storybookEndpoint: `https://127.0.0.1:6006/`, // or `./storybook-static`
getPage: async (browserType, options) => {
const page = await browser[browserType].newPage(options);
return page;
},
afterScreenshot: async (page) => {
await page.close();
},
});
});
afterAll(async () => {
const promises = Object.keys(browser).map((browserType) =>
browser[browserType].close(),
);
await Promise.resolve(promises);
});
在测试文件中
describe('test screenshots', () => {
it('should pass image diff', async () => {
await expect('*').toMatchScreenshots();
}, 10000);
});
或使用 toMatchImageSnapshot
const { getScreenshots } = require('storybook-addon-playwright');
describe('test screenshots manually', () => {
it('should pass image diff', async () => {
await getScreenshots({
onScreenshotReady: (screenshotBuffer, baselineScreenshotPath) => {
expect(screenshotBuffer).toMatchImageSnapshot({
customSnapshotIdentifier: baselineScreenshotPath.screenshotIdentifier,
customSnapshotsDir: baselineScreenshotPath.screenshotsDir,
});
},
});
}, 10000);
});
确保为您的测试设置适当的超时时间。
Typescript
如果您的编辑器无法识别 toMatchScreenshots
匹配器,请在您的项目中添加一个 global.d.ts 文件,内容如下
import 'storybook-addon-playwright';
迁移
插件生成的 json 文件的结构可能会因新功能而发生更改,要修复和应用更改,请在 setConfig
中将 enableMigration
设置为 true
并运行 Storybook。
确保在迁移后将
enableMigration
设置为false