ScreenshotNeo

BlogHTML to image & PDF

Puppeteer Screenshot Testing for PDFs: How to Check Printed Page Output

Test the pages users will read: generate a deterministic PDF with Puppeteer, render its pages to images, and compare them with approved baselines.

By the ScreenshotNeo team4 October 20269 min read

To test the printed page output of a Puppeteer-generated PDF, generate the PDF with explicit print settings, render each PDF page to an image, then compare those images with approved baseline images. A screenshot of the browser viewport is a different artifact: it does not show how the generated PDF paginates or renders. Puppeteer’s page.pdf() generates a PDF using print CSS by default; the PDF-to-image and comparison stages are separate parts of your test pipeline. Puppeteer Page.pdf() documentation · Puppeteer screenshots guide.

This guide builds a repeatable Node.js capture script and explains what to compare, how to handle readiness and print styling, and how to diagnose common differences.

1. Decide which output you are testing

Start with the user-visible contract. If your application produces a PDF for printing or download, test that PDF’s pages. A browser screenshot is useful for checking the web UI, but it cannot verify PDF page breaks, clipping at paper boundaries, or pagination.

Test artifact What it can tell you What it cannot establish by itself
Browser viewport screenshot How the page appears in a browser at a chosen viewport and media type. How page.pdf() paginates and renders the document.
Images rendered from the generated PDF Page appearance, pagination, clipping, blank pages, and visual changes in the PDF output. Identical output on every printer, driver, operating system, print dialog, or paper stock.
PDF structure checks Signals such as page count, extracted text, links, and metadata, when paired with suitable tooling. Visual correctness by themselves.

A useful suite can combine image comparisons with structural assertions. Treat those as complementary signals: a page can have the expected text and still have a broken layout.

2. Make the page state repeatable

Visual comparisons are meaningful only when the page begins in a known state. Use a stable route and test data, and make the application expose or otherwise provide a clear readiness condition. Where practical, control dynamic content and external dependencies that can change between runs.

Waiting for network idle can be one readiness strategy, but it is not proof that an arbitrary application is ready. Applications may update after requests finish, or keep requests open. Wait for the condition that means your own page is ready, such as a specific report element or a test-only readiness signal.

Puppeteer’s PDF generation waits for fonts by default. That does not establish that application data, images, charts, or other asynchronous content has finished rendering. Choose an application-specific readiness check for those. Puppeteer’s PDF generation guide.

3. Generate the PDF with explicit print settings

Install Puppeteer in a Node.js project, then save this as generate-pdf.mjs. It opens a supplied URL, waits for an example application readiness marker, and writes the PDF to disk. Change the URL and readiness selector to match your application.

import puppeteer from 'puppeteer';

const url = process.env.TEST_URL ?? 'http://localhost:3000/report';
const output = process.env.PDF_OUTPUT ?? 'artifacts/report.pdf';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 1 });
  await page.goto(url, { waitUntil: 'networkidle0' });

  // Replace this with a readiness condition owned by your application.
  await page.waitForSelector('[data-report-ready="true"]');

  // page.pdf() uses print media by default. Keep these values aligned
  // with the PDF settings your product is intended to produce.
  await page.pdf({
    path: output,
    format: 'A4',
    landscape: false,
    printBackground: true,
    preferCSSPageSize: true,
    scale: 1,
    margin: {
      top: '12mm',
      right: '12mm',
      bottom: '12mm',
      left: '12mm',
    },
  });
} finally {
  await browser.close();
}

Run it after starting the application:

TEST_URL=http://localhost:3000/report PDF_OUTPUT=artifacts/report.pdf node generate-pdf.mjs

The example uses networkidle0 and a readiness selector together. You can use another navigation wait condition if it better fits the page, but retain a check for the application state your PDF depends on.

Choose the PDF options deliberately

Set options to match the output contract and keep them stable between baseline creation and later runs. Puppeteer documents these PDF settings in its PDFOptions API; check the documentation for the Puppeteer version installed in your project.

Option What to decide
format Paper format, such as A4 or Letter. When provided, format takes priority over width and height.
width, height Use dimensions when the intended page size is custom. Do not expect them to override a supplied format.
landscape Set orientation to match the intended document. Verify tables and wide content at the selected orientation.
margin Set top, right, bottom, and left margins explicitly when they are part of your output contract.
scale Keep scaling consistent; unexpected scaling can affect line wraps and page breaks.
pageRanges Use a range when the intended output includes only selected pages. An incorrect range can omit pages.
printBackground The documented default is false. Set it to true if backgrounds are part of the expected PDF appearance.
preferCSSPageSize When true, a CSS @page size takes priority. Otherwise Puppeteer scales page content to fit the paper size.

For example, if the document defines its own paper dimensions, include print CSS such as:

@media print {
  @page {
    size: A4 portrait;
    margin: 12mm;
  }

  .screen-only {
    display: none !important;
  }
}

With preferCSSPageSize: true, the CSS page size has priority. Keep CSS margins and PDF margins intentional and consistent so the output does not depend on assumptions about which setting controls the page geometry.

4. Render the PDF pages and compare them

Keep the generated PDF as a test artifact. Add a PDF renderer as a separate pipeline stage, render each page to an image, and compare those images against baselines reviewed and checked into your project. The cited Puppeteer APIs document PDF generation and browser page screenshots; they do not provide a built-in PDF visual-diff pipeline or prescribe a particular PDF renderer or comparison library.

  1. Generate the PDF with the same settings used when the baseline was approved.
  2. Render all PDF pages to images with your chosen PDF-to-image tool.
  3. Compare the rendered pages with the corresponding approved baseline images.
  4. Review differences before updating baselines. Look for changed pagination, clipping, missing images, font substitution, unexpected blank pages, and content that moved across a page boundary.
  5. Keep the PDF and rendered pages available as artifacts when a comparison fails so reviewers can see the actual output.

A page screenshot is still useful for diagnosing the underlying web layout. Puppeteer supports screenshots of a page and of a specific element, but use images rendered from the PDF when the question is how the PDF pages look. Puppeteer screenshots guide.

5. Control print media, colors, and backgrounds

page.pdf() generates output using print CSS. If your intended PDF is explicitly based on screen-media styles, call page.emulateMediaType('screen') before generating it. Puppeteer documents screen, print, and null as supported media types. Do not switch to screen media simply to make a visual test pass if the product is meant to generate a print-styled PDF. Puppeteer Page.emulateMediaType() API.

Color differences need deliberate handling. Puppeteer says PDF colors are modified for printing by default and points to -webkit-print-color-adjust when exact colors are needed. Also, printBackground defaults to false, so backgrounds may be absent unless you enable it.

@media print {
  .report {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

MDN explains that print-color-adjust defaults to economy, under which the user agent may alter or omit color and background treatment. The exact value asks for the authored appearance, but user-agent choices and user settings can override it; it is not a guarantee. Align the CSS and PDF options with the browser-generated output you intend to test, and avoid claiming that this proves what every physical printer will produce. MDN print-color-adjust reference.

6. Troubleshoot common failures

Symptom Likely cause What to check or change
PDF content looks different from a viewport screenshot The PDF uses print CSS; the screenshot may have used screen media and viewport dimensions. Compare images rendered from the generated PDF. Confirm the intended media type and print styles.
Missing colored backgrounds printBackground is false, or print color adjustment changes the output. Set printBackground: true if backgrounds are expected. Review print color CSS, while remembering it cannot guarantee every user agent or printer preserves exact colors.
Wrong paper size or unexpected scaling format, width/height, CSS @page, or preferCSSPageSize do not match the intended geometry. Make the intended paper size explicit. Check that format is not taking priority over dimensions you expected to use, and decide whether CSS page size should take priority.
Text or images are missing intermittently The PDF was generated before application content was ready. Font waiting does not wait for every application resource. Add an application-specific readiness condition and verify the data and image loading path. Do not treat network idle alone as proof of readiness.
Page breaks or page count changed Content, fonts, margins, scaling, paper size, or page-range settings changed. Inspect the PDF and rendered pages, then compare settings and inputs with the baseline run. Check print-specific CSS and dynamic content.
Unexpected blank page Print layout or page-break behavior may have shifted content onto another page. Inspect the rendered page sequence and the preceding page boundary. Check the relevant print styles, geometry, and input content.
Test times out waiting for network idle The page may keep network requests active or otherwise never reach that network condition. Choose a readiness condition that matches the application, such as a report-ready selector. A navigation wait condition is not a substitute for app readiness.
Baseline differs after a dependency update The browser or Puppeteer version may have changed rendering behavior. Record Puppeteer and browser versions with the test environment, and review changed output before accepting new baselines. Do not assume option defaults or output are invariant across releases.

7. Performance, reliability, and cost

PDF generation, PDF rasterization, and visual comparison are three pieces of work. Keep the pipeline focused on the pages and routes that protect important document output, and retain artifacts on failures so diagnosis does not require guessing from a pass/fail result alone. Avoid running overlapping operations on the same page unless your code coordinates them: Puppeteer’s screenshot API documents restrictions around concurrent screenshot operations. Page.screenshot() API.

For reliable comparisons, use stable inputs, consistent PDF options, and a controlled browser environment. Record the Puppeteer and browser versions. Review baseline updates rather than treating every pixel difference as a defect or automatically replacing the expected images.

Cost depends on your chosen execution environment and any separate PDF rendering or comparison software. The sources cited here do not establish prices or recommend a particular rasterizer. Account for the browser process and image-processing work in your own pipeline, and choose tooling that fits its runtime and storage constraints.

Or skip the browser setup

If your goal is a screenshot of a webpage rather than a visual test of generated PDF pages, ScreenshotNeo is a website screenshot API and MCP server for developers. It does not replace the PDF-page rendering stage in the workflow above.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, with no card required.

FAQ

How do I take a screenshot of a PDF generated with Puppeteer?

Generate the PDF with page.pdf(), render its pages to images with a separate PDF renderer, then inspect or compare those images. A Puppeteer viewport screenshot is not a screenshot of the generated PDF pages.

Does page.pdf() use print CSS?

Yes. Puppeteer documents print as the PDF media type. Use page.emulateMediaType('screen') first only when screen-media output is the intended contract.

Does Puppeteer wait for fonts before making the PDF?

Puppeteer’s PDF documentation says it waits for fonts by default. You still need an appropriate readiness strategy for your application data, images, and other asynchronous content.

Will print color settings guarantee what a physical printer produces?

No. Browser-generated PDF checks do not establish identical output across print dialogs, operating systems, printer drivers, printers, or paper. The CSS color-adjust property also cannot guarantee that a user agent preserves the authored appearance.