storybook-addon-playwright
一個可在 storybook 環境中於多個瀏覽器中視覺測試 story 的擴充功能。
此擴充功能無法在 storybook 靜態建置中運作,但可以針對靜態建置檔案測試螢幕截圖。
此套件僅在 react 框架下測試過,因此可能無法在其他框架下運作。
相容性
套件 | 版本 |
---|---|
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/埠。
對於 docker 和非本機瀏覽器,需要 storybook 的公用 IP 位址。
enableMigration
如果設定為 true,將會將變更套用至由擴充功能產生的 json 檔案,請在「遷移」章節中閱讀更多資訊。
beforeScreenshot
將在擷取螢幕截圖之前呼叫,可用於操作頁面。
afterScreenshot
將在擷取螢幕截圖之後呼叫。
afterStoryImageDiff
將在執行整個應用程式螢幕截圖的影像差異測試之前/之後呼叫。
beforeStoryImageDiff/afterStoryImageDiff
將在針對特定 story 執行影像差異測試之前/之後呼叫。
beforeFileImageDiff/afterFileImageDiff
將在針對特定檔案執行影像差異測試之前/之後呼叫。
beforeAllImageDiff/afterAllImageDiff
將在完成所有 story 螢幕截圖的 imageDiff 程序之後呼叫。
pageGotoOptions
請參閱頂部的 Playwright API page.goto 選項
afterUrlConstruction
將在 page.goto 之前呼叫,可用於操作 URL。
afterNavigation
當頁面導覽至 story 時將會呼叫。
releaseModifierKey
設定為 true 時,將會在擷取螢幕截圖後,對 modifier 鍵 Shift、Meta、Control 或 Alt 執行 keyboard.up。
screenshotOptions
擷取螢幕截圖時套用的預設選項。
theme
它會覆寫擴充功能的預設主題。它是 material-ui Theme
物件類型的 json
。
.
.
.
setConfig({
theme: {
palette: {
primary: {
main: '#0052cc',
},
secondary: {
main: '#edf2ff',
},
},
}
});
.
.
.
運作方式
此擴充功能基本上是 playwright 和 storybook story 之間的介面。擴充功能會在組態中提供的頁面上執行使用者指令。
使用者建立的指令將會儲存在 story 檔案旁邊的 json 檔案中。因此,它可以在下次載入時使用。
此擴充功能包含三個部分
- 動作清單面板
- 螢幕截圖清單面板
- 螢幕截圖預覽面板
動作清單面板
動作面板的作用類似於遊樂場,它包含使用者針對特定 story 建立的動作集清單,選取後會在瀏覽器頁面中執行。
一個動作集可以有一個或多個動作。
動作是指 playwright 頁面方法,例如 click、mouse move 等...
螢幕截圖清單面板
此面板會保留使用者先前擷取的螢幕截圖,您可以在此管理螢幕截圖,例如刪除、編輯或排序螢幕截圖。
螢幕截圖預覽面板
預覽面板會顯示 playwright 擷取的最新螢幕截圖,它可以選擇性地顯示 playwright 支援的所有或部分瀏覽器。
您可以在此儲存和變更螢幕截圖設定,例如寬度、高度等。
螢幕截圖會儲存在 story 資料夾下的名為 __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
擷取元素,並在 selector 的中心執行 mousedown。
mouseMoveToSelector
此方法會使用 selector
擷取元素,並將滑鼠移動至 selector 的中心。
setSelectorSize
此方法會使用 selector
擷取元素,並設定 selector 的寬度或高度。
scrollSelector
此方法會使用 selector
擷取元素,並設定 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