👺

playwrightのsnapshotでmask以外の方法で要素を隠す

2025/01/31に公開

Playwrightのスクリーンショットのテストをする際、差分として考慮したくない部分に関してmaskを指定できる。

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  await expect(page).toHaveScreenshot({
    mask: [
      page.locator(".navbar__title.text--truncate"),
      page.locator(".getStarted_Sjon"),
    ]
  })
})

ある程度のランダムに発生する部分はこれで対処可能なのだが、トーストなどこれだけだと考慮しきれないケースがあり、消す方法を考えた

パターン1: removeで行う

要素を指定してremove()で消すことを考えた

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  const target = [
    ".navbar__title.text--truncate",
    ".getStarted_Sjon"
  ]
  await Promise.all(target.map(async (selector) => {
    await page.$eval(selector, (element) => {
      element.remove()
    })
  }))
  await expect(page).toHaveScreenshot()
})

下記のように要素を消すことはできる。

ただ見て分かる通り、他の要素の位置がずれるなどが発生してしまう。positon:fixedな要素などであればこれで十分対処できそうだったが、基本的にはあまり使えないケースが多いだろう

パターン2: style属性で非表示にする

次にstyle属性でopacity: 0にする方法。

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  const target = [
    ".navbar__title.text--truncate",
    ".getStarted_Sjon"
  ]
  await Promise.all(target.map(async (selector) => {
    await page.$eval(selector, (elements) => {
      elements.setAttribute("style", "opacity: 0")
    })
  }))
  await expect(page).toHaveScreenshot()
})

これはこれでうまく行ったのだが、$evalを実行してからsnapshotを取るまでの間に対象の要素が出てきてしまう場合があり、これもイマイチうまくいかなかった

パターン3: addStyleTagでCSSを追加する

上記で足りなかったので、addStyleTagで追加する手段を取った。

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  const target = [
    ".navbar__title.text--truncate",
    ".getStarted_Sjon"
  ]
  const hiddenCss = target.map(selector => `${selector} { opacity: 0 !important; }`).join("\n")
  await page.addStyleTag({ content: hiddenCss })
  await expect(page).toHaveScreenshot()
})

これであれば、後から要素が追加されても問題なかった。
スナップショット撮影後に戻す場合も下記のような形で行えた

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  const target = [
    ".navbar__title.text--truncate",
    ".getStarted_Sjon"
  ]
  const hiddenCss = target.map(selector => `${selector} { opacity: 0 !important; }`).join("\n")
  const styleHandle = await page.addStyleTag({ content: hiddenCss })
  await expect(page).toHaveScreenshot()
  // 追加した要素を消す
  await styleHandle.evaluateHandle((el) => el.childNodes.forEach(el => el.remove()))
})

おまけ: blurでぼかす

発展型として、maskの代わりにblurにする事もできる。

test('snapshot', async ({ page }) => {
  await page.goto('https://playwright.dev/')
  const target = [
    ".navbar__title.text--truncate",
    ".getStarted_Sjon"
  ]
  const hiddenCss = target.map(selector => `${selector} { filter: blur(5px);  }`).join("\n")
  await page.addStyleTag({ content: hiddenCss })
  await expect(page).toHaveScreenshot()
})

少し分かりづらいがうっすらボケているのがわかる

「全部隠したくはないけどmaxDiffPixelsを調整してそれなりに近いと嬉しい」みたいな場合に使えるかもしれない

GitHubで編集を提案

Discussion