Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use PhantomJS’s webpage module to open the page, verify that page.open() returned success, and call page.render() before phantom.exit(). In a Mocha test run, the documented mocha-phantomjs bridge can request a screenshot through window.callPhantom; placing that request in afterEach and checking this.currentTest.state captures only failed tests.

The capture flow at a glance

There are three separate roles in this setup:

  • Mocha defines tests and hooks such as afterEach, and records whether a test passed or failed.
  • PhantomJS is the headless browser that loads a URL and renders pixels. It is not a test framework.
  • mocha-phantomjs (or another suitable runner) launches the browser and provides the bridge from browser-side test code to PhantomJS.

The official PhantomJS headless-testing documentation describes PhantomJS as a launcher used through a suitable test runner. Keep that distinction in your project: Mocha decides when to capture, while PhantomJS decides how to render.

Capture a page with a standalone PhantomJS script

Start with a small script to prove that PhantomJS can load and write an image independently of Mocha. Save this as capture.js:

var page = require('webpage').create();

page.open('http://example.com/', function (status) {
  if (status === 'success') {
    page.render('screenshots/example.png');
  } else {
    console.log('Page load failed: ' + status);
  }
  phantom.exit();
});

Run it with PhantomJS from the project directory, and create the screenshots directory first. The relative filename is resolved from the process working directory. The success check prevents a failed navigation from being mistaken for a valid screenshot. The explicit phantom.exit() is essential: PhantomJS does not terminate by itself after the callback.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Replace the URL and output path with values appropriate to your test. Use a path outside source control for failure artifacts, or clean the directory before a run so that an old image cannot be confused with a new one.

Choose the viewport and the part of the page to render

Set page.viewportSize to the browser viewport that your test is meant to represent. Set page.clipRect when you need only a rectangle rather than the entire viewport.

var page = require('webpage').create();

page.viewportSize = {
  width: 1024,
  height: 768
};
page.clipRect = {
  top: 0,
  left: 0,
  width: 1024,
  height: 768
};

page.open('http://example.com/', function (status) {
  if (status === 'success') {
    page.render('screenshots/viewport.png');
  }
  phantom.exit();
});

The 1024 × 768 values are illustrative API values, not a required default. Match them to the dimensions used by the test case. A clip rectangle larger than the viewport does not make the browser render content that was never laid out; increase the viewport when the page itself must reflow at a larger size.

Select an output format and quality

PhantomJS chooses the format from the filename extension. The render API lists PNG, JPEG, BMP, PPM, GIF and PDF where the installed Qt build supports them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Extension Use Important detail
.png Pixel-accurate test artifacts, text and UI comparisons PNG remains lossless. The quality value controls Deflate compression, so it can change file size without changing pixels.
.jpg or .jpeg Smaller photographic or visual-review files quality is an integer from 0 to 100 and controls JPEG quality.
.pdf Document-style output Use only when a PDF is the intended artifact; it is not a substitute for a pixel comparison image.
.gif GIF output where available Support depends on the Qt build bundled with PhantomJS.

Set quality before rendering when you need it:

page.settings = page.settings || {};
page.settings.quality = 90;
page.render('screenshots/review.jpg');

For PNG comparison images, leave the pixels lossless and adjust storage policy rather than introducing JPEG artifacts.

Capture only failed Mocha tests

The indexed mocha-phantomjs package documentation shows a takeScreenshot() helper that checks for window.callPhantom and then calls callPhantom({'screenshot': filename}). The failure-only pattern places that helper in Mocha’s afterEach hook and checks this.currentTest.state == 'failed'.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

A representative test-side arrangement is:

describe('checkout', function () {
  afterEach(function () {
    if (this.currentTest.state == 'failed') {
      takeScreenshot('screenshots/checkout-' + this.currentTest.title + '.png');
    }
  });

  it('shows an error for an expired card', function () {
    // browser test steps
  });
});

The helper must be available in the browser context supplied by your runner. A defensive implementation follows the documented bridge contract:

function takeScreenshot(filename) {
  if (window.callPhantom) {
    window.callPhantom({ screenshot: filename });
  } else {
    console.log('Screenshot bridge is unavailable');
  }
}

Use a filename that is unique for the test and, when parallel jobs are possible, for the worker or build identifier as well. Keep the hook focused on artifacts: the assertion failure should remain the primary test result even if the screenshot bridge is unavailable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture at a deliberate point instead of only on failure

Failure-only capture is economical and keeps successful runs quiet, but it is not the only useful timing choice.

Capture an explicit checkpoint

Call the helper immediately after the state you want to document: for example, after opening a dialog or submitting a form. This produces a known checkpoint even when the test eventually passes.

Capture in afterEach

Use the documented this.currentTest.state check when the objective is to inspect failures later. This avoids creating an image for every successful test.

Capture several states

Use distinct names such as 01-loaded.png, 02-dialog.png and 03-error.png. Do not overwrite a prior checkpoint with the same filename; otherwise the last render hides the sequence that led to the failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

The page-open callback confirms navigation status, not that every application-controlled animation or asynchronous component has finished. If the image is consistently too early, move the render call to the test’s own ready checkpoint rather than treating a successful network load as proof that the interface is visually complete.

Make the artifacts reliable in CI

  • Check status before rendering. Log the returned status and skip the image when navigation failed.
  • Always exit. Put phantom.exit() on every path from the open callback so a failed navigation cannot leave the job hanging.
  • Use deterministic paths. Create the destination directory before the run and include the test title or an identifier in each filename.
  • Keep the viewport explicit. A different CI display is less likely to change layout when viewportSize is set in code.
  • Preserve the original failure. Screenshot creation should be diagnostic work; it should not replace the assertion error with an image-writing error.
  • Verify the runner bridge. A direct PhantomJS script can render successfully even when a Mocha runner has not injected callPhantom.

The PhantomJS documentation and the indexed package example are legacy material. The available information does not establish current compatibility between a particular PhantomJS binary, Mocha release and mocha-phantomjs package. Pin the versions used by your project and validate the bridge in the same environment as CI rather than assuming that a package snippet guarantees present-day support.

Troubleshoot common failures

No image is written

Confirm that page.open returned success, that the destination directory exists, and that the process has permission to write there. Remember that a relative path is relative to the directory from which PhantomJS was launched.

The process never finishes

Check that phantom.exit() runs after both successful and failed navigation. A callback that renders but never exits leaves PhantomJS alive.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The image is blank or shows an error page

Log the open status and capture only after the page reaches the test state you need. A successful callback does not prove that a client-rendered screen has completed its own asynchronous work.

callPhantom is undefined

The test is not running through a bridge that exposes window.callPhantom, or the helper is executing outside the browser page. First prove the standalone page.render script; then check the runner’s integration and the helper’s execution context.

The crop is wrong

Compare clipRect with viewportSize. The rectangle’s top, left, width and height are capture coordinates, while the viewport controls layout. Set both explicitly for repeatable output.

JPEG looks different between runs

Use PNG for pixel-sensitive assertions. JPEG is lossy, and its integer quality setting affects the encoded result. PNG quality changes compression, not image pixels.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

GIF or PDF rendering fails

Those formats depend on the capabilities of the PhantomJS Qt build. Try PNG first to separate a general rendering problem from an output-format limitation.

Or skip the browser setup

If you only need a URL rendered to an image or PDF, ScreenshotNeo provides a single HTTP request instead of maintaining a PhantomJS page and runner bridge. Its API accepts PNG, JPEG or WebP output, and it can also return a PDF.

cURL (see the ScreenshotNeo API documentation):

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

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie-consent banners, newsletter popups and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

Options relevant to test artifacts

  • Full-page capture with lazy images loaded, or one element selected by CSS selector.
  • Dark mode, 12 device presets, arbitrary viewport dimensions and retina scale.
  • PDF paper size, margins, landscape mode and page ranges.
  • HTML/CSS-to-image, custom CSS and JavaScript, pre-capture clicks, hidden selectors and waits for a selector, delay or network idle.
  • Blocking for ads, trackers, requests or resource types.
  • Custom headers, cookies, user agent and Authorization; timezone and geolocation.
  • Transparent backgrounds, image resizing, a user-selected cache TTL, signed links for public image tags, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification.
  • Parameter names used by other screenshot APIs also work, which can reduce changes when switching.

Plans include every feature: Free provides 1,000 shots per month with no card; Starter is $5 for 3,000; Growth $15 for 15,000; Pro $39 for 60,000; Scale $99 for 250,000; and Business $249 for 1,000,000. Yearly billing gives two months free. Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Frequently asked questions

Is PhantomJS itself a Mocha test runner?

No. PhantomJS supplies the headless browser; Mocha supplies test definitions and outcomes, and a runner such as the documented mocha-phantomjs arrangement connects them.

Can I treat the documented package example as a current support guarantee?

No. The package example is indexed legacy documentation, and current compatibility among the runner, PhantomJS and Mocha versions is not established here. Test the exact versions you intend to run.

Why keep a direct PhantomJS script if the tests already render screenshots?

It isolates navigation, filesystem permissions, viewport settings and output-format problems from Mocha hooks and the browser-to-runner bridge, making failures easier to diagnose.

Frequently Asked Questions

Is PhantomJS itself a Mocha test runner?

No. PhantomJS supplies the headless browser; Mocha supplies test definitions and outcomes, and a runner such as the documented mocha-phantomjs arrangement connects them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I treat the documented package example as a current support guarantee?

No. It is legacy indexed documentation, and compatibility among your specific PhantomJS, runner and Mocha versions must be verified in your own environment.

Why keep a direct PhantomJS script if the tests already render screenshots?

It separates navigation, file permissions, viewport and output-format problems from Mocha hooks and the browser bridge, simplifying diagnosis.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.