画像比較(ビジュアルリグレッションテスト)サービス
@wdio/visual-serviceはサードパーティパッケージです。詳細については、GitHub | npm をご覧ください。
WebdriverIOを使用したビジュアルテストのドキュメントについては、ドキュメントを参照してください。このプロジェクトには、WebdriverIOでビジュアルテストを実行するための関連モジュールがすべて含まれています。./packages
ディレクトリ内には、以下のものがあります。
@wdio/visual-testing
: ビジュアルテストを統合するためのWebdriverIOサービスwebdriver-image-comparison
: WebDriverプロトコルをサポートするさまざまなNodeJSテスト自動化フレームワークで使用できる画像比較モジュール
Storybookランナー(ベータ版)
Storybook Runner BETAの詳細なドキュメントについては、こちらをクリックしてください
Storybook Runnerはまだベータ版であり、ドキュメントは後でWebdriverIOドキュメントページに移動します。
このモジュールは、新しいビジュアルランナーでStorybookをサポートするようになりました。このランナーは、ローカル/リモートのStorybookインスタンスを自動的にスキャンし、各コンポーネントの要素スクリーンショットを作成します。これを行うには、次を追加します。
export const config: WebdriverIO.Config = {
// ...
services: ["visual"],
// ....
};
services
に追加し、コマンドラインからnpx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook
を実行します。デフォルトのブラウザーとしてヘッドレスモードでChromeを使用します。
[!NOTE]
- ほとんどのビジュアルテストオプションは、Storybookランナーでも機能します。詳細については、WebdriverIOドキュメントを参照してください。
- Storybookランナーは、すべてのcapabilitiesを上書きし、サポートするブラウザーでのみ実行できます。
--browsers
を参照してください。- Storybookランナーは、Multiremote capabilitiesを使用する既存の構成をサポートしておらず、エラーがスローされます。
- Storybookランナーは、モバイルWebではなくデスクトップWebのみをサポートしています。
Storybookランナーサービスのオプション
サービスオプションは、次のように指定できます
export const config: WebdriverIO.Config = {
// ...
services: [
[
'visual',
{
// Some default options
baselineFolder: join(process.cwd(), './__snapshots__/'),
debug: true,
// The storybook options, see cli options for the description
storybook: {
clip: false,
clipSelector: ''#some-id,
numShards: 4,
// `skipStories` can be a string ('example-button--secondary'),
// an array (['example-button--secondary', 'example-button--small'])
// or a regex which needs to be provided as as string ("/.*button.*/gm")
skipStories: ['example-button--secondary', 'example-button--small'],
url: 'https://www.bbc.co.uk/iplayer/storybook/',
version: 6,
},
},
],
],
// ....
}
StorybookランナーCLIオプション
--browsers
- タイプ:
string
- 必須: いいえ
- デフォルト:
chrome
、chrome|firefox|edge|safari
から選択できます。 - 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --browsers=chrome,firefox,edge,safari
- 注: CLIでのみ使用可能
指定されたブラウザーを使用してコンポーネントのスクリーンショットを撮ります
[!NOTE] ローカルマシンに実行するブラウザーがインストールされていることを確認してください
--clip
- タイプ:
boolean
- 必須: いいえ
- デフォルト:
true
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --clip=false
無効にすると、ビューポートのスクリーンショットが作成されます。有効にすると、--clipSelector
に基づいて要素のスクリーンショットが作成され、コンポーネントのスクリーンショットの周囲の空白が減り、スクリーンショットのサイズが縮小されます。
--clipSelector
- タイプ:
string
- 必須: いいえ
- デフォルト: Storybook V7の場合は
#storybook-root > :first-child
、Storybook V6の場合は#root > :first-child:not(script):not(style)
。また、--version
を参照してください - 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --clipSelector="#some-id"
これは使用されるセレクターです
- スクリーンショットを撮る要素を選択するため
- スクリーンショットを撮る前に要素が表示されるのを待機するため
--devices
- タイプ:
string
- 必須: いいえ
- デフォルト:
deviceDescriptors.ts
から選択できます - 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --devices="iPhone 14 Pro Max","Pixel 3 XL"
- 注: CLIでのみ使用可能
指定されたデバイスとdeviceDescriptors.ts
を照合して、コンポーネントのスクリーンショットを撮ります
[!NOTE]
- デバイス構成が不足している場合は、機能リクエストを送信してください
- これはChromeでのみ機能します
--devices
を指定すると、すべてのChromeインスタンスがモバイルエミュレーションモードで実行されます--devices --browsers=firefox,safari,edge
のようにChrome以外のブラウザーも指定すると、モバイルエミュレーションモードでChromeが自動的に追加されます- Storybookランナーは、デフォルトで要素スナップショットを作成します。モバイルエミュレーションされた完全なスクリーンショットを表示する場合は、コマンドラインから
--clip=false
を指定してください- ファイル名は、たとえば
__snapshots__/example/button/desktop_chrome/example-button--large-local-chrome-iPhone-14-Pro-Max-430x932-dpr-3.png
のようになります- SRC: モバイルエミュレーションを使用してデスクトップでモバイルWebサイトをテストすると役立つ場合がありますが、テスターは次のような多くの微妙な違いがあることに注意する必要があります。
- パフォーマンスの大きな変化につながる可能性のある、完全に異なるGPU。
- モバイルUIはエミュレートされていません(特に、URLバーを非表示にするとページ高さに影響します)。
- あいまいさのポップアップ(いくつかのタッチターゲットの1つを選択する場合)はサポートされていません。
- 多くのハードウェアAPI(たとえば、orientationchangeイベント)は使用できません。
--headless
- タイプ:
boolean
- 必須: いいえ
- デフォルト:
true
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --headless=false
- 注: CLIでのみ使用可能
これにより、テストはデフォルトでヘッドレスモード(ブラウザーがサポートしている場合)で実行されるか、無効にすることができます
--numShards
- タイプ:
number
- 必須: いいえ
- デフォルト:
true
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --numShards=10
これは、ストーリーを実行するために使用される並列インスタンスの数です。これは、wdio.conf
ファイルのmaxInstances
によって制限されます。
[!IMPORTANT]
headless
モードで実行する場合は、リソース制限による不安定さを防ぐために、数を20以上に増やさないでください
--skipStories
- タイプ:
string|regex
- 必須: いいえ
- デフォルト: null
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --skipStories="/.*button.*/gm"
これは
- 文字列(
example-button--secondary,example-button--small
) - または正規表現(
"/.*button.*/gm"
)
特定のストーリーをスキップします。ストーリーのURLにあるストーリーのid
を使用します。たとえば、このURL http://localhost:6006/?path=/story/example-page--logged-out
のid
はexample-page--logged-out
です。
--url
- タイプ:
string
- 必須: いいえ
- デフォルト:
http://127.0.0.1:6006
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --url="https://example.com"
StorybookインスタンスがホストされているURL。
--version
- タイプ:
number
- 必須: いいえ
- デフォルト 7
- 例:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --version=6
これはStorybookのバージョンで、デフォルトは7
です。これは、V6のclipSelector
を使用する必要があるかどうかを判断するために必要です。
Storybook インタラクションテスト
Storybook インタラクションテストでは、WDIO コマンドでカスタムスクリプトを作成することで、コンポーネントを特定の状態に設定し、コンポーネントとインタラクションできます。たとえば、以下のコードスニペットをご覧ください。
import { browser, expect } from "@wdio/globals";
describe("Storybook Interaction", () => {
it("should create screenshots for the logged in state when it logs out", async () => {
const componentId = "example-page--logged-in";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
await $("button=Log out").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
});
it("should create screenshots for the logged out state when it logs in", async () => {
const componentId = "example-page--logged-out";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
await $("button=Log in").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
});
});
2つの異なるコンポーネントに対して2つのテストが実行されます。各テストは最初に状態を設定し、次にスクリーンショットを取得します。また、新しいカスタムコマンドが導入されていることにも気づくでしょう。これはこちらで確認できます。
上記の設定ファイルはフォルダに保存し、次のコマンドでコマンドラインに追加できます。
npm run test.local.desktop.storybook.localhost -- --spec='tests/specs/storybook-interaction/*.ts'
Storybook ランナーは、まず Storybook インスタンスを自動的にスキャンし、比較する必要があるストーリーにテストを追加します。インタラクションテストに使用するコンポーネントを2回比較したくない場合は、--skipStories
フィルターを指定して、スキャンから「default」ストーリーを削除するフィルターを追加できます。これは次のようになります。
npm run test.local.desktop.storybook.localhost -- --skipStories="/example-page.*/gm" --spec='tests/specs/storybook-interaction/*.ts'
新しいカスタムコマンド
browser.waitForStorybookComponentToBeLoaded({ id: 'componentId' })
という新しいカスタムコマンドが、browser/driver
オブジェクトに追加されます。このコマンドは、コンポーネントを自動的にロードして完了するのを待つため、browser.url('url.com')
メソッドを使用する必要はありません。これは次のように使用できます。
import { browser, expect } from "@wdio/globals";
describe("Storybook Interaction", () => {
it("should create screenshots for the logged in state when it logs out", async () => {
const componentId = "example-page--logged-in";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
await $("button=Log out").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
});
it("should create screenshots for the logged out state when it logs in", async () => {
const componentId = "example-page--logged-out";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
await $("button=Log in").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
});
});
オプションは次のとおりです。
clipSelector
- タイプ:
string
- 必須: いいえ
- デフォルト: Storybook V7 の場合は
#storybook-root > :first-child
、Storybook V6 の場合は#root > :first-child:not(script):not(style)
- 例
await browser.waitForStorybookComponentToBeLoaded({
clipSelector: "#your-selector",
id: "componentId",
});
これは使用されるセレクターです
- スクリーンショットを撮る要素を選択するため
- スクリーンショットを撮る前に要素が表示されるのを待機するため
id
- タイプ:
string
- 必須: はい
- 例
await browser.waitForStorybookComponentToBeLoaded({ '#your-selector', id: 'componentId' })
ストーリーの URL にあるストーリーの id
を使用します。たとえば、この URL http://localhost:6006/?path=/story/example-page--logged-out
の id
は example-page--logged-out
です。
timeout
- タイプ:
number
- 必須: いいえ
- デフォルト: 1100 ミリ秒
- 例
await browser.waitForStorybookComponentToBeLoaded({
id: "componentId",
timeout: 20000,
});
ページにロードされた後、コンポーネントが表示されるのを待つ最大タイムアウト
url
- タイプ:
string
- 必須: いいえ
- デフォルト:
http://127.0.0.1:6006
- 例
await browser.waitForStorybookComponentToBeLoaded({
id: "componentId",
url: "https://your.url",
});
StorybookインスタンスがホストされているURL。
貢献
パッケージの更新
簡単な CLI ツールでパッケージを更新できます。すべての依存関係がインストールされていることを確認してから、次を実行できます。
pnpm update.packages
これにより、次の質問をする CLI がトリガーされます。
==========================
🤖 Package update Wizard 🧙
==========================
? Which version target would you like to update to? (Minor|Latest)
? Do you want to update the package.json files? (Y/n)
? Do you want to remove all "node_modules" and reinstall dependencies? (Y/n)
? Would you like reinstall the dependencies? (Y/n)
これにより、次のログが生成されます。
ログの例を表示するには、開いてください
==========================
🤖 Package update Wizard 🧙
==========================
? Which version target would you like to update to? Minor
? Do you want to update the package.json files? yes
Updating root 'package.json' for minor updates...
Updating packages for minor updates in /Users/wswebcreation/Git/wdio/visual-testing...
Using pnpm
Upgrading /Users/wswebcreation/Git/wdio/visual-testing/package.json
[====================] 38/38 100%
@typescript-eslint/eslint-plugin ^8.7.0 → ^8.8.0
@typescript-eslint/parser ^8.7.0 → ^8.8.0
@typescript-eslint/utils ^8.7.0 → ^8.8.0
@vitest/coverage-v8 ^2.1.1 → ^2.1.2
vitest ^2.1.1 → ^2.1.2
Run pnpm install to install new versions.
Updating packages for minor updates in /Users/wswebcreation/Git/wdio/visual-testing/packages/ocr-service...
Using pnpm
Upgrading /Users/wswebcreation/Git/wdio/visual-testing/packages/ocr-service/package.json
[====================] 11/11 100%
All dependencies match the minor package versions :)
Updating packages for minor updates in /Users/wswebcreation/Git/wdio/visual-testing/packages/visual-reporter...
Using pnpm
Upgrading /Users/wswebcreation/Git/wdio/visual-testing/packages/visual-reporter/package.json
[====================] 11/11 100%
eslint-config-next 14.2.13 → 14.2.14
next 14.2.13 → 14.2.14
Run pnpm install to install new versions.
Updating packages for minor updates in /Users/wswebcreation/Git/wdio/visual-testing/packages/visual-service...
Using pnpm
Upgrading /Users/wswebcreation/Git/wdio/visual-testing/packages/visual-service/package.json
[====================] 5/5 100%
All dependencies match the minor package versions :)
Updating packages for minor updates in /Users/wswebcreation/Git/wdio/visual-testing/packages/webdriver-image-comparison...
Using pnpm
Upgrading /Users/wswebcreation/Git/wdio/visual-testing/packages/webdriver-image-comparison/package.json
[====================] 8/8 100%
All dependencies match the minor package versions :)
? Do you want to remove all "node_modules" and reinstall dependencies? yes
Removing root dependencies in /Users/wswebcreation/Git/wdio/visual-testing...
Removing dependencies in ocr-service...
Removing dependencies in visual-reporter...
Removing dependencies in visual-service...
Removing dependencies in webdriver-image-comparison...
? Would you like reinstall the dependencies? yes
Installing dependencies in /Users/wswebcreation/Git/wdio/visual-testing...
> @wdio/visual-testing-monorepo@ pnpm.install.workaround /Users/wswebcreation/Git/wdio/visual-testing
> pnpm install --shamefully-hoist
Scope: all 5 workspace projects
Lockfile is up to date, resolution step is skipped
Packages: +1274
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 1274, reused 1265, downloaded 0, added 1274, done
dependencies:
- @wdio/ocr-service 2.0.0 <- packages/ocr-service
- @wdio/visual-service 6.0.0 <- packages/visual-service
devDependencies:
- @changesets/cli 2.27.8
- @inquirer/prompts 5.5.0
- @tsconfig/node20 20.1.4
- @types/eslint 9.6.1
- @types/jsdom 21.1.7
- @types/node 20.16.4
- @types/react 18.3.5
- @types/react-dom 18.3.0
- @types/xml2js 0.4.14
- @typescript-eslint/eslint-plugin 8.8.0
- @typescript-eslint/parser 8.8.0
- @typescript-eslint/utils 8.8.0
- @vitest/coverage-v8 2.1.2
- @wdio/appium-service 9.1.2
- @wdio/cli 9.1.2
- @wdio/globals 9.1.2
- @wdio/local-runner 9.1.2
- @wdio/mocha-framework 9.1.2
- @wdio/sauce-service 9.1.2
- @wdio/shared-store-service 9.1.2
- @wdio/spec-reporter 9.1.2
- @wdio/types 9.1.2
- eslint 9.11.1
- eslint-plugin-import 2.30.0
- eslint-plugin-unicorn 55.0.0
- eslint-plugin-wdio 9.0.8
- husky 9.1.6
- jsdom 25.0.1
- npm-run-all2 6.2.3
- release-it 17.6.0
- rimraf 6.0.1
- saucelabs 8.0.0
- ts-node 10.9.2
- typescript 5.6.2
- vitest 2.1.2
- webdriverio 9.1.2
. prepare$ husky
└─ Done in 204ms
Done in 9.5s
All packages updated!
質問
このプロジェクトへの貢献に関する質問や問題がある場合は、Discordサーバーにご参加ください。🙏-contributing
チャンネルで貢献者にお問い合わせください。
問題
質問、バグ、または機能のリクエストがある場合は、問題を提出してください。問題を提出する前に、重複を減らすために問題アーカイブを検索し、FAQをお読みください。
そこにない場合は、問題を提出して、以下を提出できます。
- 🐛バグレポート: 改善にご協力ください。
- 📖ドキュメント: 改善点を提案するか、不足または不明確なドキュメントを報告してください。
- 💡機能リクエスト: このモジュールのアイデアを提案してください。
- 💬質問: 質問してください。
開発ワークフロー
このプロジェクトの PR を作成し、貢献を開始するには、次のステップバイステップガイドに従ってください。
-
プロジェクトをフォークします。
-
コンピューターのどこかにプロジェクトをクローンします。
$ git clone https://github.com/webdriverio/visual-testing.git
-
ディレクトリに移動して、プロジェクトをセットアップします。
$ cd visual-testing
$ corepack enable
$ corepack use pnpm@8.x
$ pnpm pnpm.install.workaround -
コードを自動的にトランスパイルするウォッチモードを実行します。
$ pnpm watch
プロジェクトをビルドするには、次を実行します。
$ pnpm build
-
変更によってテストが壊れていないことを確認するには、次を実行します。
$ pnpm test
このプロジェクトでは、changesetsを使用して、変更ログとリリースを自動的に作成します。
テスト
モジュールをテストできるようにするには、いくつかのテストを実行する必要があります。PR を追加するときは、すべてのテストが少なくともローカルテストに合格する必要があります。各 PR は Sauce Labs に対して自動的にテストされます。 GitHub Actions パイプラインを参照してください。PR を承認する前に、コアコントリビューターがエミュレーター/シミュレーター / 実機に対して PR をテストします。
ローカルテスト
最初に、ローカルベースラインを作成する必要があります。これは次を使用して実行できます。
// With the webdriver protocol
$ npm run test.local.init
このコマンドは、すべてのベースラインイメージを保持する localBaseline
という名前のフォルダーを作成します。
次に、次を実行します。
// With the webdriver protocol
npm run test.local.desktop
これにより、ローカルマシンで Chrome 上ですべてのテストが実行されます。
ローカル Storybook ランナーテスト (ベータ版)
最初に、ローカルベースラインを作成する必要があります。これは次を使用して実行できます。
npm run test.local.desktop.storybook
これにより、https://govuk-react.github.io/govuk-react/にあるデモ Storybook リポジトリに対して、ヘッドレスモードで Chrome を使用して Storybook テストが実行されます。
より多くのブラウザーでテストを実行するには、次を実行できます。
npm run test.local.desktop.storybook -- --browsers=chrome,firefox,edge,safari
[!NOTE] ローカルマシンに実行するブラウザーがインストールされていることを確認してください
Sauce Labs を使用した CI テスト (PR には不要)
以下のコマンドは、GitHub Actions でビルドをテストするために使用されます。ローカル開発には使用できません。
$ npm run test.saucelabs
こちらにある多くの構成に対してテストします。すべての PR は Sauce Labs に対して自動的にチェックされます。
リリース
上記にリストされているパッケージのバージョンをリリースするには、次の手順を実行します。
- リリースパイプラインをトリガーします。
- リリース PR が生成されます。これを別の WebdriverIO メンバーにレビューおよび承認してもらいます。
- PR をマージします。
- リリースパイプラインを再度トリガーします。
- 新しいバージョンがリリースされるはずです 🎉
クレジット
@wdio/visual-testing
は、Sauce Labs のオープンソースライセンスを使用しています。