PDF出力をテストする
ユースケース: アプリがPDF — 請求書、レポート、チケット、契約書 — を生成する。「請求書をダウンロードをクリックすると正しいPDFが得られる」は立派なユーザージャーニーであり、ここでのリグレッションは紙になって顧客の手元に届いてしまう。
なぜ難しいのか
Section titled “なぜ難しいのか”ブラウザはファイルをダウンロードする(=画面に何もレンダリングされずアサート対象がない)か、Playwrightが中を覗けないネイティブPDFビューアで表示するかのどちらかです。いずれにせよ、コンテンツはDOMではなくバイナリファイルの中にあります。
ダウンロードを捕まえる
Section titled “ダウンロードを捕まえる”import { test, expect } from '@playwright/test';
test('invoice PDF contains the right data', async ({ page }) => { await page.goto('/orders/1042');
const downloadPromise = page.waitForEvent('download'); await page.getByRole('button', { name: 'Download invoice' }).click(); const download = await downloadPromise;
const path = await download.path(); // …以下で `path` に対してアサートする});PDFがダウンロードではなくインライン表示される場合は、ページのCookieを使って直接取得します: const pdf = await page.request.get(url).then(r => r.body())。
テキストをアサートする
Section titled “テキストをアサートする”npm install -D pdf-parseimport pdf from 'pdf-parse';import { readFileSync } from 'node:fs';
const { text, numpages } = await pdf(readFileSync(path));
expect(numpages).toBe(1);expect(text).toContain('Invoice #1042');expect(text).toContain('Total: $1,337.00');expect(text).not.toContain('undefined'); // テンプレートバグの定番pdf-parse はレイアウトを平坦化します。段組や表は読み上げ順で出てくるので、文書全体の完全一致ではなく断片でアサートしてください。
レイアウトをアサートする(ビジュアル)
Section titled “レイアウトをアサートする(ビジュアル)”テキストのチェックではレイアウト崩れ — 重なった段組、2ページ目に押し出されたロゴ — を見逃します。ラスタライズしてPlaywrightのビジュアル比較を再利用しましょう:
# poppler-utils。CIでは: apt-get install poppler-utils / brew install popplerpdftoppm -png -r 100 invoice.pdf out/invoiceimport { execFileSync } from 'node:child_process';
execFileSync('pdftoppm', ['-png', '-r', '100', path, 'out/invoice']);expect(readFileSync('out/invoice-1.png')).toMatchSnapshot('invoice-page1.png', { maxDiffPixelRatio: 0.02,});先に動的な領域(日付、請求書番号)を安定させてください。テストデータを固定するか、比較前に sharp で既知の安定領域を切り出します。
必要ならさらに深く
Section titled “必要ならさらに深く”- 機械可読請求書(ZUGFeRD/Factur-X)はXMLを内包しています。抽出してスキーマ検証しましょう。
- アーカイブ用文書のPDF/A準拠: CIで
verapdfを実行。 - アクセシビリティ(タグ付きPDF): 最低限、テキストが抽出可能であることをアサートします(スキャン画像だけのPDFは
textが空になります —「PDFライブラリを乗り換えたら」の後に起きがちな、れっきとしたリグレッションです)。
- PDF生成はサーバー側で非同期のことが多いです。ボタンクリックと競争せず、ダウンロードエンドポイントをポーリングしてください。
- フォント置換のせいでラスタライズ結果はマシン間で異なります。ベースラインはCIと同じDockerイメージで生成しましょう。
- Playwrightの
page.pdf()はWebページのPDFを作る機能(Chromium限定)で、アプリが生成したPDFのアサーションとは無関係です。
- トランザクションメールをテストする — PDFはメール添付で届くことが多いです。