ScreenshotNeo

BlogHow-to

How to integrate Applitools Eyes with Puppeteer

Add Applitools Eyes visual checks to a Puppeteer test: install the SDK, configure a runner, capture checkpoints, close tests safely, and review baselines.

By the ScreenshotNeo team4 October 20267 min read

To integrate Applitools Eyes with Puppeteer, install @applitools/eyes-puppeteer, open an Eyes test for a Puppeteer page, capture one or more visual checkpoints, close the test, and collect the runner results. Applitools documents this integration and its Puppeteer SDK; the example API below follows its February 2024 tutorial, so confirm names against the version you install.

1. Install the Puppeteer integration

In an existing Node.js project, install the integration as a development dependency:

npm install --save-dev @applitools/eyes-puppeteer puppeteer

The Applitools tutorial installs @applitools/eyes-puppeteer with npm i -D. Check the current package documentation and your installed SDK version before relying on version-specific method names or browser options.

2. Configure the API key

Create or retrieve an Applitools API key in your Applitools account, then set it in the test process environment. Do not commit the key to source control.

export APPLITOOLS_API_KEY="YOUR_API_KEY"

For CI, store the value in the CI provider’s secret store and expose it to the job as APPLITOOLS_API_KEY. The tutorial’s setup helper calls eyes.setApiKey(apiKey).

3. Create a visual test

This ES module example shows the documented lifecycle: configure Eyes and an optional Visual Grid runner, launch Puppeteer, open Eyes on a page, check a stable application state, close the browser and Eyes, then collect results. It targets a local app at http://localhost:3000; replace that address and the selectors with those for your application.

import puppeteer from 'puppeteer';
import {
  Eyes,
  Target,
  VisualGridRunner,
} from '@applitools/eyes-puppeteer';

const apiKey = process.env.APPLITOOLS_API_KEY;
if (!apiKey) {
  throw new Error('Set APPLITOOLS_API_KEY before running this test.');
}

const runner = new VisualGridRunner({ testConcurrency: 1 });
const eyes = new Eyes(runner);

let browser;
let eyesTestOpened = false;
let testError;

try {
  eyes.setApiKey(apiKey);

  const configuration = eyes.getConfiguration();
  configuration.setBatch({ name: 'Puppeteer visual checks' });
  eyes.setConfiguration(configuration);

  browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800 });
  await page.goto('http://localhost:3000', { waitUntil: 'networkidle0' });

  await eyes.open(page, {
    appName: 'Example application',
    testName: 'Home page',
  });
  eyesTestOpened = true;

  // Prefer a meaningful, stable state over a transient loading state.
  await page.waitForSelector('[data-testid="home-ready"]');
  await eyes.check('Home page', Target.window());

  // For a long page, the tutorial demonstrates a full-page target:
  // await eyes.check('Home page - full page', Target.window().fully());
} catch (error) {
  testError = error;
} finally {
  if (browser) {
    await browser.close();
  }

  if (eyesTestOpened) {
    try {
      await eyes.closeAsync();
    } catch (error) {
      testError ??= error;
    }
  }

  // Abort is a safeguard so a failed test does not leave an Eyes test open.
  try {
    await eyes.abortAsync();
  } catch (error) {
    testError ??= error;
  }
}

if (testError) {
  throw testError;
}

const results = await runner.getAllTestResults();
console.log(results);

The exact runner constructor options can vary by SDK version. The tutorial creates a VisualGridRunner, passes it to new Eyes(...), configures a batch and browser or device targets, and retrieves summaries with getAllTestResults(). If using an SDK version that requires different runner configuration, follow that version’s package documentation.

4. Place checkpoints at useful states

eyes.open(page, ...) begins a visual test for the Puppeteer page. Each eyes.check(...) captures a checkpoint. Use names that explain the state or component being reviewed, such as “Signed-in dashboard” or “Checkout validation error.”

  • Viewport capture: Target.window() checks the visible browser window.
  • Full-page capture: Target.window().fully() requests a full-page checkpoint, as shown by the tutorial. Verify the method with your installed SDK.
  • Checkpoint timing: wait for the application to reach a stable state before checking. A selector, completed navigation, or explicit test state can be a better synchronization point than an arbitrary delay.
  • Checkpoint count: capture the states that represent meaningful user-visible behavior. A check after every low-level interaction may create noisy reviews; the tutorial’s hook-based example demonstrates capturing after replay steps, while your suite can choose its own meaningful boundaries.

5. Configure browser and device coverage

You can run the Puppeteer test in its local browser context or configure Visual Grid targets through the Eyes configuration. The Applitools tutorial demonstrates adding browser and device configurations, including imports named BrowserType and DeviceName. Those enum values and configuration methods are SDK-version-specific; use the installed package’s current documentation for exact names.

Choose coverage based on the visual behavior you need to validate. A single viewport is simpler to diagnose. Multiple browser or device targets broaden coverage but add work to review when rendering differences are expected. The tutorial identifies operating system, viewport, browser, app name, and test name as factors that can distinguish baselines.

6. Understand baselines and review results

An initial run establishes expected images for a test and environment. Later runs compare their screenshots against those baselines. The Eyes server reports visual differences for review in Test Manager; a difference is a signal to inspect, not automatically a defect. Accept a changed baseline only after confirming the UI change is intentional.

The SDK flow is: the test triggers a checkpoint, the SDK captures and sends an image to the Eyes server, and the server compares it with stored baselines. Results are reviewed in Applitools tooling. Keep app and test names stable so runs map consistently to the intended test and baseline.

7. Common errors and fixes

Symptom Likely cause What to do
Missing API key or authorization failure APPLITOOLS_API_KEY is unset, misspelled, or unavailable in the CI process. Check the environment variable in the test job and confirm the key is valid. Keep it in a secret store, not in committed code.
Import or method not found The copied tutorial uses SDK APIs that do not match the installed package version. Check the installed version and current @applitools/eyes-puppeteer documentation; align imports, runner configuration, and target methods to that version.
Test hangs or results never return An Eyes test was opened but not closed, or cleanup was skipped after an earlier failure. Put browser and Eyes cleanup in finally. Call closeAsync() for an opened test and retain abortAsync() as a safeguard, following the SDK’s documented lifecycle.
Screenshot shows loading UI or missing content The checkpoint happened before the page reached the intended state. Wait for a meaningful selector or app-ready condition before calling eyes.check(). Confirm navigation and asynchronous content have completed.
Unexpected visual differences across runs The rendered environment or app state changed, or the baseline belongs to a different browser, viewport, OS, app, or test context. Compare the run configuration and test data with the baseline. Inspect the difference before accepting an intentional update.
Full-page checkpoint behaves differently than expected Full-page capture APIs or page layout behavior vary with SDK version and content. Verify the full-page target method for the installed version, and ensure the page has loaded the content you expect before capture.

8. Performance, reliability, and cost considerations

  • Keep runs deterministic: use stable test data and wait for explicit application states. Animation, rotating content, timestamps, and asynchronous updates can create differences unrelated to a code change.
  • Limit parallelism deliberately: Visual Grid runner concurrency affects how many visual tests run at once. Start with a modest setting and adjust to the throughput and review capacity your suite needs; the correct value depends on your suite and environment.
  • Always close tests: a test left open can continue running. Ensure cleanup runs after assertion failures, navigation problems, and process errors.
  • Review environment identity: browser, viewport, OS, application name, and test name can affect which baseline is used. Keep those choices intentional and consistent.
  • Check current plan terms: the provided research does not specify Applitools pricing or limits, so consult Applitools directly for current cost and account details.

Or skip the browser setup

If you need a clean screenshot for documentation, monitoring, or an agent workflow rather than a baseline comparison, ScreenshotNeo can capture a URL with one API request. Its API returns PNG, JPEG, WebP, or PDF, and its documentation lists the request options.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does Eyes replace Puppeteer?

No. Puppeteer drives the browser and application interactions; Eyes adds visual checkpoints and comparison to baselines.

Does the first run fail because there is no baseline?

The tutorial describes the initial run as establishing the baseline. Review the resulting images in Applitools tooling, then use later runs to compare against them.

Can I use this integration without Visual Grid?

The tutorial uses a VisualGridRunner and demonstrates optional browser and device configuration. Check your installed SDK documentation for supported runner setups and choose the coverage your project needs.

Is a visual difference always a bug?

No. It may reflect an intentional UI change or a changed rendering environment. Inspect the comparison and test context before deciding whether to update the baseline.

Sources: Applitools Puppeteer tutorial; Applitools Eyes overview; Applitools SDK documentation.