From aa10f478ca310ad7b6e271896112243e8c7f2e0f Mon Sep 17 00:00:00 2001 From: "Nanashi." Date: Tue, 3 Dec 2024 22:40:11 +0900 Subject: [PATCH] =?UTF-8?q?Storybook=E3=81=AEVRT=E3=81=AB=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E4=B8=80=E8=A6=A7=E3=81=AE=E5=90=8C?= =?UTF-8?q?=E6=9C=9F=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=20(#2385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions[bot] Co-authored-by: Hiroshiba Kazuyuki --- .github/workflows/test.yml | 13 ++-- ...\203\247\343\203\203\343\203\210.spec.mts" | 59 +++++++++++++++--- ...heckbox--default-dark-storybook-win32.png" | Bin 1594 -> 0 bytes ...eckbox--default-light-storybook-win32.png" | Bin 1443 -> 0 bytes ...quencerruler--default-storybook-win32.png" | Bin 6606 -> 0 bytes 5 files changed, 58 insertions(+), 14 deletions(-) delete mode 100644 "tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts-snapshots/components-base-basecheckbox--default-dark-storybook-win32.png" delete mode 100644 "tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts-snapshots/components-base-basecheckbox--default-light-storybook-win32.png" delete mode 100644 "tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts-snapshots/components-sing-sequencerruler--default-storybook-win32.png" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0349c884cc..542941b0f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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' @@ -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 diff --git "a/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts" "b/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts" index 460a7f7cd9..b5a1b7c3cd 100644 --- "a/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts" +++ "b/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts" @@ -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( @@ -19,12 +26,19 @@ const storybookIndexSchema = z.object({ }); type StorybookIndex = z.infer; 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; @@ -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", @@ -95,7 +106,7 @@ for (const [story, stories] of Object.entries(allStories)) { elementToScreenshot = root; } await expect(elementToScreenshot).toHaveScreenshot( - `${story.id}-${theme}.png`, + toSnapshotFileName(story, theme), ); }); } @@ -103,3 +114,33 @@ for (const [story, stories] of Object.entries(allStories)) { } }); } + +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); + } +}); diff --git "a/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts-snapshots/components-base-basecheckbox--default-dark-storybook-win32.png" "b/tests/e2e/storybook/\343\202\271\343\202\257\343\203\252\343\203\274\343\203\263\343\202\267\343\203\247\343\203\203\343\203\210.spec.mts-snapshots/components-base-basecheckbox--default-dark-storybook-win32.png" deleted file mode 100644 index e5600783b2d28961952e774e6c93a41a28b39694..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1594 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