Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StorybookのVRTをPlaywrightでやる #2291

Merged
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
af60b54
Migrate: storybookのmigrationを実行
sevenc-nanashi Oct 9, 2024
c860316
Add: browserモードで動かすように
sevenc-nanashi Oct 9, 2024
4b7f5b8
Delete: vitest-exampleを削除
sevenc-nanashi Oct 9, 2024
91ab76f
Add: vitestにstorybookを統合
sevenc-nanashi Oct 9, 2024
088b707
Delete: test:storybook周りを削除
sevenc-nanashi Oct 9, 2024
6cf57aa
Add: ui modeのscriptを追加
sevenc-nanashi Oct 9, 2024
a01518c
Change: headlessにする
sevenc-nanashi Oct 9, 2024
e3912a0
Add: コメントを追加
sevenc-nanashi Oct 9, 2024
dc7d24c
Fix: postinstall周りを修正
sevenc-nanashi Oct 9, 2024
72c5cf3
Fix: vite.config.mtsの型エラーを修正
sevenc-nanashi Oct 9, 2024
83beea7
Fix: Chromiumを指定
sevenc-nanashi Oct 9, 2024
06ce4fa
Fix: スタイルを修正
sevenc-nanashi Oct 9, 2024
5648fe4
Change: ubuntuでテストする
sevenc-nanashi Oct 9, 2024
6655515
Add: ハックを追加
sevenc-nanashi Oct 9, 2024
db69e76
Update: playwright/testを更新
sevenc-nanashi Oct 9, 2024
b529a7c
Update: READMEを更新
sevenc-nanashi Oct 9, 2024
4818061
Change: test-watch:unit:ui -> test-ui:unit
sevenc-nanashi Oct 9, 2024
b85a959
Change: headlessで動かすように
sevenc-nanashi Oct 9, 2024
a909f32
Change: バージョンを固定
sevenc-nanashi Oct 10, 2024
de96dca
Update: READMEを更新
sevenc-nanashi Oct 10, 2024
62a5c08
Refactor: workspace周りを整理
sevenc-nanashi Oct 10, 2024
b422aa2
Add: NOTEを追加
sevenc-nanashi Oct 10, 2024
9e334eb
Fix: 日本語を修正
sevenc-nanashi Oct 10, 2024
f2bf5d6
Change: fnをStoryのargsに動かす
sevenc-nanashi Oct 10, 2024
0590c61
Add: エラーメッセージを追加
sevenc-nanashi Oct 10, 2024
2288aa5
Delete: 不要そうなパッケージを削除
sevenc-nanashi Oct 10, 2024
324329f
Change: もっといい解決方法を使う
sevenc-nanashi Oct 10, 2024
0ef0596
Add: PlaywrightでVRTする
sevenc-nanashi Oct 10, 2024
ddcc8ef
Change: VITE_TARGET -> TARGET
sevenc-nanashi Oct 10, 2024
6970ca5
Add: StorybookのVRTをCIに追加
sevenc-nanashi Oct 11, 2024
6e6e958
(CI用)
sevenc-nanashi Oct 11, 2024
c8b65db
Fix: viteのコマンドを修正
sevenc-nanashi Oct 11, 2024
5579f85
Delete: Linuxでのスクショを削除
sevenc-nanashi Oct 11, 2024
65f7268
(スナップショットを更新)
github-actions[bot] Oct 11, 2024
724b265
(CI用)
sevenc-nanashi Oct 11, 2024
6b83cde
Fix: 最初の方のコメントを修正
sevenc-nanashi Oct 11, 2024
c261e6b
Merge: main -> poc/storybook-vrt-by-playwright
sevenc-nanashi Oct 13, 2024
539b289
Fix: マージミス
sevenc-nanashi Oct 13, 2024
49799a5
Delete: 不要な依存を削除
sevenc-nanashi Oct 13, 2024
b431900
Change: fullPageをtrueにする
sevenc-nanashi Oct 13, 2024
22f8da5
Add: 説明を追加
sevenc-nanashi Oct 13, 2024
30699cf
Change: CIでstoryIdsも更新する
sevenc-nanashi Oct 13, 2024
622de49
(スナップショットを更新)
github-actions[bot] Oct 13, 2024
38b07a7
(CI用)
sevenc-nanashi Oct 13, 2024
7d09805
Refactor: WebServerを上に持っていく
sevenc-nanashi Oct 13, 2024
87b721b
CHange: Top level awaitを使う
sevenc-nanashi Oct 13, 2024
1847850
Delete: 不要な依存を削除
sevenc-nanashi Oct 14, 2024
127fa61
(CI用)
sevenc-nanashi Oct 14, 2024
469d3c7
(スナップショットを更新)
github-actions[bot] Oct 14, 2024
4e9ee4a
Revert: package-lock.jsonの変更を戻す
sevenc-nanashi Oct 14, 2024
5e24ad9
Code: コメントを追加
sevenc-nanashi Oct 14, 2024
72e4379
Add: zodでバリデーションするように
sevenc-nanashi Oct 14, 2024
6f20373
Fix: helpersを使えなかったので修正
sevenc-nanashi Oct 14, 2024
acfe019
Change: causeを使う
sevenc-nanashi Oct 14, 2024
01aa04c
Change: describeでまとめる
sevenc-nanashi Oct 14, 2024
b6d9e74
Change: 一番上のスクリーンショットを外す
sevenc-nanashi Oct 14, 2024
59141c1
Fix: テストを修正
sevenc-nanashi Oct 14, 2024
b74cc28
Code: コメントを追加
sevenc-nanashi Oct 14, 2024
da8473a
Fix: ESLintのエラーを修正
sevenc-nanashi Oct 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ jobs:
npm run test:electron-e2e
fi

- name: Run npm run test:storybook-vrt
run: |
if [ -n "${{ runner.debug }}" ]; then
export DEBUG="pw:browser*"
fi
ARGS=""
if [[ ${{ needs.config.outputs.shouldUpdateSnapshots }} == 'true' ]]; then
ARGS="--update-snapshots"
fi
npm run test:storybook-vrt -- $ARGS

- name: Upload playwright report to artifact
if: failure()
uses: actions/upload-artifact@v4
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ electron-builder.yml

# generated licenses.json
/*licenses.json

# Storybook
storybook-static/
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,20 @@ npx playwright codegen http://localhost:5173/ --viewport-size=1024,630

詳細は [Playwright ドキュメントの Test generator](https://playwright.dev/docs/codegen-intro) を参照してください。

### Storybook の Visual Regression Testing

Storybook のコンポーネントのスクリーンショットを比較して、変更がある場合は差分を表示します。
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved

> [!NOTE]
> このテストは Windows でのみ実行できます。

```bash
npm run test:storybook-vrt
```

#### スクリーンショットの更新

ブラウザ End to End テストでは Visual Regression Testing を行っています。
ブラウザ End to End テストと Storybook では Visual Regression Testing を行っています。
現在 VRT テストは Windows のみで行っています。
以下の手順でスクリーンショットを更新できます:

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"test-watch:electron-e2e": "cross-env PWTEST_WATCH=1 VITE_TARGET=electron playwright test",
"test:browser-e2e": "cross-env VITE_TARGET=browser playwright test",
"test-watch:browser-e2e": "cross-env PWTEST_WATCH=1 VITE_TARGET=browser playwright test",
"test:storybook-vrt": "cross-env TARGET=storybook playwright test",
"lint": "eslint --ext .js,.vue,.ts *.config.* src tests build .storybook",
"fmt": "eslint --ext .js,.vue,.ts *.config.* src tests build .storybook --fix",
"markdownlint": "markdownlint --ignore node_modules/ --ignore dist/ --ignore dist_electron/ ./",
Expand Down
73 changes: 39 additions & 34 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,51 @@ import dotenv from "dotenv";
dotenv.config({ override: true });

let project: Project;
const additionalWebServer: PlaywrightTestConfig["webServer"] = [];
let webServers: PlaywrightTestConfig["webServer"];
const isElectron = process.env.VITE_TARGET === "electron";
const isBrowser = process.env.VITE_TARGET === "browser";
const isStorybook = process.env.TARGET === "storybook";
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved

// エンジンの起動が必要
const defaultEngineInfosEnv = process.env.VITE_DEFAULT_ENGINE_INFOS ?? "[]";
const envSchema = z // FIXME: electron起動時のものと共通化したい
.object({
host: z.string(),
executionFilePath: z.string(),
executionArgs: z.array(z.string()),
executionEnabled: z.boolean(),
})
.passthrough()
.array();
const engineInfos = envSchema.parse(JSON.parse(defaultEngineInfosEnv));

const engineServers = engineInfos
.filter((info) => info.executionEnabled)
.map((info) => ({
command: `${info.executionFilePath} ${info.executionArgs.join(" ")}`,
url: `${info.host}/version`,
reuseExistingServer: !process.env.CI,
}));
const viteServer = {
command: "vite --mode test --port 7357",
port: 7357,
reuseExistingServer: !process.env.CI,
};
const storybookServer = {
command: "storybook dev --ci --port 7357",
port: 7357,
reuseExistingServer: !process.env.CI,
};

if (isElectron) {
project = { name: "electron", testDir: "./tests/e2e/electron" };
webServers = [viteServer];
} else if (isBrowser) {
project = { name: "browser", testDir: "./tests/e2e/browser" };

// エンジンの起動が必要
const defaultEngineInfosEnv = process.env.VITE_DEFAULT_ENGINE_INFOS ?? "[]";
const envSchema = z // FIXME: electron起動時のものと共通化したい
.object({
host: z.string(),
executionFilePath: z.string(),
executionArgs: z.array(z.string()),
executionEnabled: z.boolean(),
})
.passthrough()
.array();
const engineInfos = envSchema.parse(JSON.parse(defaultEngineInfosEnv));

for (const info of engineInfos) {
if (!info.executionEnabled) {
continue;
}

additionalWebServer.push({
command: `${info.executionFilePath} ${info.executionArgs.join(" ")}`,
url: `${info.host}/version`,
reuseExistingServer: !process.env.CI,
});
}
webServers = [viteServer, ...engineServers];
} else if (isStorybook) {
project = { name: "storybook", testDir: "./tests/e2e/storybook" };
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
webServers = [storybookServer];
} else {
throw new Error(`VITE_TARGETの指定が不正です。${process.env.VITE_TARGET}`);
}
Expand Down Expand Up @@ -90,14 +102,7 @@ const config: PlaywrightTestConfig = {
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/',

webServer: [
{
command: "vite --mode test --port 7357",
port: 7357,
reuseExistingServer: !process.env.CI,
},
...additionalWebServer,
],
webServer: webServers,
};

export default config;
39 changes: 39 additions & 0 deletions tests/e2e/storybook/スクリーンショット.spec.mts
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { test, expect } from "@playwright/test";
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved

type StorybookIndex = {
v: 5;
entries: Record<string, StorybookEntry>;
};
type StorybookEntry = {
type: string;
id: string;
name: string;
title: string;
tags: string[];
};

// テスト対象のStory一覧を取得する。
// play-fnが付いているStoryはUnit Test用Storyとみなしてスクリーンショットを撮らない
const getStoriesToTest = (index: StorybookIndex) =>
Object.values(index.entries).filter(
(entry) => entry.type === "story" && !entry.tags.includes("play-fn"),
);

const index = (await fetch("http://localhost:7357/index.json").then((res) =>
res.json(),
)) as StorybookIndex;
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved

const currentStories = getStoriesToTest(index);

const storyIds = currentStories.map((entry) => entry.id);

for (const story of storyIds) {
test(`スクリーンショット:${story}`, async ({ page }) => {
test.skip(process.platform !== "win32", "Windows以外のためスキップします");

await page.goto(`http://localhost:7357/iframe.html?id=${story}`);
const body = page.locator("body.sb-show-main");
await body.waitFor({ state: "visible" });
await expect(page).toHaveScreenshot(`${story}.png`, { fullPage: true });
});
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading