Playwrightのキャプチャでスクロールバーが消える原因と表示させる方法|ignoreDefaultArgsで--hide-scrollbarsを外す

概要

今回はPlaywrightでテスト中に撮ったキャプチャからスクロールバーが消えてしまう問題と、それを表示させる方法について紹介していきます。

Playwrightでキャプチャを取得していたら、画面のレイアウト崩れが発生していました。
なんでかな?と通常のブラウザで同じ画面を確認したところ、Playwrightのキャプチャにだけスクロールバーが表示されていないことに気づきました(- -;

調べてみると、これはPlaywrightがChromiumをheadlessで起動するときに、デフォルトで--hide-scrollbarsという引数を付けているのが原因でした。
この記事では、Playwrightのソースと公式ドキュメントで確認できた範囲で、原因とignoreDefaultArgsを使った設定方法、使うときの注意点を確認していきます。

それではやっていきましょう!

目次

結論:ignoreDefaultArgsで–hide-scrollbarsを外す

先に結論です。
playwright.configのuse.launchOptionsに次の設定を入れたところ、キャプチャにスクロールバーが表示されるようになりました。

playwright.config.ts

1
2
3
launchOptions: {
ignoreDefaultArgs: ['--hide-scrollbars'],
},

Playwrightが自動で付けるブラウザ起動引数のうち、--hide-scrollbarsだけを除外するという設定です。
対象になるのはheadlessで起動したChromium系ブラウザで、ignoreDefaultArgsはtrueではなく配列で指定します(理由は後半の「使うときの注意点」で触れます)。
ここからは、なぜこれで直るのかを順番に見ていきます。

スクロールバーが消える原因

headless起動時にPlaywrightが–hide-scrollbarsを付けている

Playwrightはブラウザを起動するとき、自分で指定したargsとは別に、Playwright側で用意しているデフォルトの起動引数をブラウザに渡しています。

以下はGitHubで公開されているPlaywrightのソースです。

chromium.ts(抜粋)

1
2
3
4
5
6
7
8
if (options.headless) {
chromeArguments.push('--headless');
chromeArguments.push(
'--hide-scrollbars',
'--mute-audio',
'--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4',
);
}

つまり、headlessで起動したときだけ、--hide-scrollbarsがデフォルトで追加されるということですね。
Playwright Testのheadlessはデフォルトでtrueなので、特に何も設定せずにテストを回していると、この引数が付いた状態でブラウザが起動します。

普段使っているブラウザではスクロールバーが出ているのに、Playwrightのキャプチャにだけ出てこない、というズレはここれが原因でした。

ちなみにheadless: falseで画面を出して起動した場合は、この分岐に入らないので--hide-scrollbarsは付きません。
「画面を出して見たときとheadlessのキャプチャで見た目が違う」という場合は、まずこの引数を疑ってみるとよさそうです。

ignoreDefaultArgsの動き

Playwrightには、このデフォルト引数を制御するためのignoreDefaultArgsというオプションが用意されています。
公式ドキュメントのbrowserType.launch()の説明では、次のように書かれています。

  • trueを指定した場合
    • Playwright独自のデフォルト引数を渡さず、argsで指定したものだけを使う
  • 配列を指定した場合
    • デフォルト引数の中から、指定した引数だけを除外する
  • デフォルト値
    • false(デフォルト引数をすべて使う)

ソース側(packages/playwright-core/src/server/browserType.ts)でも、配列で指定した場合はデフォルト引数をfilterして、一致したものだけを取り除く実装になっていました。

browserType.ts(抜粋)

1
2
3
4
5
6
if (ignoreAllDefaultArgs)
browserArguments.push(...args);
else if (ignoreDefaultArgs)
browserArguments.push(...(await this.defaultArgs(options, isPersistent, userDataDir)).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));
else
browserArguments.push(...await this.defaultArgs(options, isPersistent, userDataDir));

indexOfで完全一致を見ているので、除外したい引数は'--hide-scrollbars'のように先頭の--まで含めて正確に書く必要があります。

設定方法

書く場所は、ブラウザの起動方法によって次の2通りです。

  • Playwright Testで動かしている場合:playwright.configのuse.launchOptions
  • スクリプトから直接ブラウザを起動している場合:chromium.launch()の引数

playwright.configのuse.launchOptionsで指定する

Playwright Testを使っている場合は、playwright.configのuseの中にあるlaunchOptionsに書くのが手軽です。
公式ドキュメントでも、browserType.launch()が受け付けるオプションはuse.launchOptionsに書けると説明されています。

playwright.config.ts

1
2
3
4
5
6
7
8
9
import { defineConfig } from '@playwright/test';

export default defineConfig({
use: {
launchOptions: {
ignoreDefaultArgs: ['--hide-scrollbars'],
},
},
});

projectsでブラウザごとに設定を分けている場合は、スクロールバーを出したいプロジェクトのuseにだけ書くこともできます。

launch()で直接指定する

Playwright Testを使わず、スクリプトからブラウザを起動している場合は、chromium.launch()の引数に直接渡します。

screenshot.ts

1
2
3
4
5
6
7
8
9
10
11
import { chromium } from 'playwright';

(async () => {
const browser = await chromium.launch({
ignoreDefaultArgs: ['--hide-scrollbars'],
});
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();

どちらの書き方でも、中身としては同じlaunch()のオプションを指定しているだけです。

使うときの注意点

ignoreDefaultArgsは公式ドキュメントで「Dangerous option; use with care.」と書かれているオプションです。
配列で必要な引数だけを外すのは問題ありませんが、trueを指定するとPlaywrightのデフォルト引数がすべて渡されなくなるので、動作が変わる可能性があります。
スクロールバーを出したいだけなら、['--hide-scrollbars']のように配列で指定するのがお勧めです。

他にも、次の点は頭に入れておくとよさそうです。

  • この引数はChromium系のheadless起動時に付くもの
    • 今回確認したのはChromiumのデフォルト引数の処理で、FirefoxやWebKitは別の実装になっています
  • スクリーンショット比較をしている場合は見た目が変わる
    • スクロールバーが写るようになるので、既存の比較用画像を作り直す必要が出てくるはずです
  • CIとローカルで設定を揃える
    • 片方だけ設定を入れると、スクロールバーの有無で見た目がズレます

締め

いかがでしたか?

始まりは、キャプチャなんでずれてるんだ?
あっスクロールバーないじゃん!
から始まりましたが、
結構沼にはまりました…

同じ悩みの方いて参考になりましたら嬉しいです!

以上となります。
デフォルト設定による謎事象は解決が大変(- -
それではお疲れさまでした。