Playwright

一個在 Storybook 環境中,於多個瀏覽器中視覺化測試 Story 的擴充功能

在 Github 上檢視

storybook-addon-playwright

一個在 Storybook 環境中,於多個瀏覽器中視覺化測試 Story 的擴充功能。

此擴充功能無法在 Storybook 靜態建置中運作,但螢幕截圖可以針對靜態建置檔案進行測試。

此套件僅使用 React 框架進行測試,因此可能無法與其他框架一起運作。

僅適用於元件 Story 格式 (CSF)

addon-screenshot

相容性

套件 版本
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 檔案,請參閱 Migration 區段以了解更多資訊。

beforeScreenshot

將在擷取螢幕截圖之前呼叫,有助於操作頁面。

afterScreenshot

將在擷取螢幕截圖後呼叫。

afterStoryImageDiff

將在對整個應用程式螢幕截圖執行影像差異測試之前/之後呼叫。

beforeStoryImageDiff/afterStoryImageDiff

將在對特定 Story 執行影像差異測試之前/之後呼叫。

beforeFileImageDiff/afterFileImageDiff

將在對特定檔案執行影像差異測試之前/之後呼叫。

beforeAllImageDiff/afterAllImageDiff

將在所有 Story 螢幕截圖的影像差異處理完成後呼叫。

pageGotoOptions

請參閱 Playwright API page.goto 選項

afterUrlConstruction

將在 page.goto 之前呼叫,可用於操作 URL。

afterNavigation

當頁面導覽至 Story 時將呼叫。

releaseModifierKey

當設定為 true 時,在擷取螢幕截圖後,將對修飾鍵 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 的元素,並在選取器中心執行滑鼠按下。

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