概要
Nuxt3 でウェブサイトのパフォーマンス改善のため、画像に loading="lazy"
属性を追加することは一般的です。しかし、これを行うと Playwright を使ったビジュアルリグレッションテスト (VRT) が失敗するという問題に直面することがあります。この記事では、その原因と具体的な解決策について解説します。
原因
この問題の原因は、Playwright がページのスクリーンショットを撮影するタイミングにあります。loading="lazy"
が設定された画像は、ビューポートに入るまで読み込みが遅延されます。Playwright がスクリーンショットを撮る際に、これらの画像がまだ読み込まれていない(表示されていない)状態であるため、期待される画像が表示された状態のスクリーンショットと比較すると差分が生じ、テストが失敗します。
解決策
この問題を解決するためのアプローチは、テスト実行時に画像を強制的に読み込ませ、それが完了するまで待機することです。
解決策のポイントは以下です。
- 遅延読み込みを無効化する (
loading = 'eager'
) - すべての画像の読み込みが完了するまで待機する
- この判定処理がないと一部のブラウザで失敗する場合がある
- 画像の読み込み処理は並列処理で対応する
以下は具体的な Playwright テストコードです。
test('VRTテスト', async ({ page }) => {
// ページ内の遅延読み込み画像を強制的に読み込み、完了を待つ
await page.evaluate(async () => {
// loading="lazy" 属性を持つ要素をすべて取得
const elementsWithLazyLoad = document.querySelectorAll('[loading="lazy"]')
// Promise を格納する配列
const elementLoadPromises: Promise<void>[] = []
// 画像の読み込み処理
for (const element of Array.from(elementsWithLazyLoad)) {
// 型チェック
if (element instanceof HTMLImageElement) {
// loading="lazy" を "eager" に変更して強制的に読み込みを開始
element.loading = 'eager'
// 画像の読み込み完了またはエラー発生を待つPromiseを作成
elementLoadPromises.push(
new Promise((resolve) => {
// 読み込み完了またはエラー発生時のハンドラ
const handleLoadOrError = () => {
// イベントリスナーを解除
element.removeEventListener('load', handleLoadOrError)
element.removeEventListener('error', handleLoadOrError)
// 待機Promiseを解決
resolve()
}
// load/error イベントリスナーを追加
element.addEventListener('load', handleLoadOrError)
element.addEventListener('error', handleLoadOrError)
// 既に読み込みが完了している場合は即座にハンドラを実行
if (element.complete) {
handleLoadOrError()
}
})
)
}
}
// すべての画像の読み込みPromiseが完了するまで待機
if (elementLoadPromises.length > 0) {
await Promise.all(elementLoadPromises)
}
})
// 画像の読み込みが完了した状態でスクリーンショットを撮影し、VRTを実行
await expect(page).toHaveScreenshot({
fullPage: true
})
})
その他の検討事項
Playwright issue (#19277) では、画像を読み込ませる方法として「画面をスクロールさせる」方法が議論されているようです。しかし、今回はその方法は採用しませんでした。その理由は、スクロールによって意図しない要素(例: CTAボタン)が表示されてしまい、想定しているスクリーンショットとの間に不要な差分が発生し、テスト結果が不安定になる可能性があるためです。前述の loading="eager" に変更して待機する方法の方が、テスト対象の要素に直接作用し、安定した結果を得やすいと考えられます。