Skip to content

Commit

Permalink
StorybookのVRTにファイル一覧の同期チェックを追加 (#2385)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hiroshiba Kazuyuki <[email protected]>
  • Loading branch information
3 people authored Dec 3, 2024
1 parent bdd6052 commit aa10f47
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 14 deletions.
13 changes: 8 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,12 @@ jobs:
- name: Collect patch for snapshots
if: needs.config.outputs.shouldUpdateSnapshots == 'true'
run: |
git add --intent-to-add tests/ # git diff に表示されるようにする
git diff tests/ # ロギング用
git diff --binary tests/ > patch-${{ matrix.os }}.diff
# ログ用
git status
# git diff に表示されるようにする
git add --intent-to-add --all tests/
git diff --binary --cached tests/ > patch-${{ matrix.os }}.diff
- name: Upload patch to artifact
if: needs.config.outputs.shouldUpdateSnapshots == 'true'
Expand All @@ -184,14 +187,14 @@ jobs:
uses: actions/download-artifact@v4
with:
pattern: updated-snapshots-*
path: patches
path: ${{ env.RUNNER_TEMP }}/patches
merge-multiple: true

- name: Commit updated snapshots
id: commit-updated-snapshots
run: |
# パッチを適用
for patch in patches/*.diff; do
for patch in ${{ env.RUNNER_TEMP }}/patches/*.diff; do
git apply --allow-empty $patch
rm $patch
done
Expand Down
59 changes: 50 additions & 9 deletions tests/e2e/storybook/スクリーンショット.spec.mts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
// 起動中のStorybookで様々なStoryを表示し、スクリーンショットを撮って比較するVRT。
// テスト自体はend-to-endではないが、Playwrightを使う関係でe2eディレクトリ内でテストしている。
/*
* 起動中のStorybookで様々なStoryを表示し、スクリーンショットを撮って比較するVRT。
* テスト自体はend-to-endではないが、Playwrightを使う関係でe2eディレクトリ内でテストしている。
*
* --update-snapshotsをつけてPlaywrightを実行するとスクリーンショットが更新される。
* 同時に、Storyが消えたスクリーンショットの削除も行う。
*/
import fs from "fs/promises";
import path from "path";
import { test, expect, Locator } from "@playwright/test";
import z from "zod";

// Storybook 8.3.5時点でのindex.jsonのスキーマ。
// もしスキーマが変わってテストが通らなくなった場合は、このスキーマを修正する
// もしスキーマが変わってテストが通らなくなった場合は、このスキーマを修正すること
const storybookIndexSchema = z.object({
v: z.literal(5),
entries: z.record(
Expand All @@ -19,12 +26,19 @@ const storybookIndexSchema = z.object({
});
type StorybookIndex = z.infer<typeof storybookIndexSchema>;
type Story = StorybookIndex["entries"][string];
type Theme = "light" | "dark";

const toSnapshotFileName = (story: Story, theme: Theme) =>
`${story.id}-${theme}.png`;

// テスト対象の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"),
(entry) =>
entry.type === "story" &&
!entry.tags.includes("play-fn") &&
!entry.tags.includes("skip-screenshot"),
);

let index: StorybookIndex;
Expand Down Expand Up @@ -52,14 +66,11 @@ for (const story of currentStories) {
for (const [story, stories] of Object.entries(allStories)) {
test.describe(story, () => {
for (const story of stories) {
if (story.tags.includes("skip-screenshot")) {
continue;
}
test.describe(story.name, () => {
for (const [theme, name] of [
["light", "ライト"],
["dark", "ダーク"],
]) {
] as const) {
test(`テーマ:${name}`, async ({ page }) => {
test.skip(
process.platform !== "win32",
Expand Down Expand Up @@ -95,11 +106,41 @@ for (const [story, stories] of Object.entries(allStories)) {
elementToScreenshot = root;
}
await expect(elementToScreenshot).toHaveScreenshot(
`${story.id}-${theme}.png`,
toSnapshotFileName(story, theme),
);
});
}
});
}
});
}

test("スクリーンショットの一覧に過不足が無い", async () => {
test.skip(process.platform !== "win32", "Windows以外のためスキップします");
const screenshotFiles = await fs.readdir(test.info().snapshotDir);
const screenshotPaths = screenshotFiles.map((file) =>
path.join(test.info().snapshotDir, file),
);

const expectedScreenshots = currentStories.flatMap((story) =>
(["light", "dark"] as const).map((theme) =>
test.info().snapshotPath(toSnapshotFileName(story, theme)),
),
);

screenshotPaths.sort();
expectedScreenshots.sort();

// update-snapshotsが指定されていたら、余分なスクリーンショットを削除する。
// 指定されていなかったら、スクリーンショットの一覧が一致していることを確認する。
if (test.info().config.updateSnapshots === "all") {
for (const screenshot of screenshotPaths) {
if (!expectedScreenshots.includes(screenshot)) {
await fs.unlink(screenshot);
console.log(`Deleted: ${path.basename(screenshot)}`);
}
}
} else {
expect(screenshotPaths).toEqual(expectedScreenshots);
}
});
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit aa10f47

Please sign in to comment.