Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsTo capture a page during a Karma test in PhantomJS 2, launch a custom PhantomJS browser with an options.onCallback handler, then call window.top.callPhantom({type: 'render', fname: 'path/file.png'}) from the test page. The handler runs in PhantomJS context and invokes page.render(); a bare callPhantom('render') does nothing unless that bridge is configured.
What you need before adding screenshots
This recipe assumes a Karma project that already runs tests with the PhantomJS browser. Install the Karma launcher plugin and make an artifact directory before the test command runs:
npm install --save-dev karma karma-phantomjs-launcher
mkdir -p .tmp/screenshots
The directory can be any workspace-relative path. In continuous integration, create it in the job itself and publish it as a build artifact after Karma exits. Use a path that the PhantomJS process can write to; a browser sandbox or a read-only working directory will otherwise produce a render failure.
Configure a custom PhantomJS launcher
In karma.conf.js, define a launcher based on PhantomJS. The important part is options.onCallback: it receives the object sent by the test and calls page.render with the requested filename.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
module.exports = function (config) {
config.set({
frameworks: ['jasmine'],
files: [
'test/**/*.spec.js'
],
reporters: ['progress'],
customLaunchers: {
PhantomJSCustom: {
base: 'PhantomJS',
options: {
onCallback: function (data) {
if (data && data.type === 'render' && data.fname !== undefined) {
page.render(data.fname);
}
}
}
}
},
browsers: ['PhantomJSCustom'],
singleRun: true
});
};
Keep the data guard. A callback can receive values from other page code, and attempting to render when type or fname is absent can overwrite an unintended file or throw an exception. If you want a fixed destination, replace data.fname with a known path; passing the filename from the test is more useful when several specs capture different states.
Add a screenshot helper to the test bundle
Karma test code runs inside the page. page.render is available only to the PhantomJS script, so the test must send a callback request through window.top.callPhantom. This helper safely does nothing when the same test bundle is opened in a normal browser.
var renderId = 0;
function takeScreenshot(file) {
if (window.top.callPhantom === undefined) {
return;
}
var options = {
type: 'render',
fname: file || '.tmp/screenshots/' + (renderId++) + '.png'
};
window.top.callPhantom(options);
}
Call it after the UI has reached the state you want to inspect. For example:
describe('checkout form', function () {
it('shows the validation state', function () {
document.querySelector('#email').value = '';
document.querySelector('#submit').click();
takeScreenshot('.tmp/screenshots/checkout-validation.png');
expect(document.querySelector('.error').textContent)
.toContain('Email');
});
});
The filename is relative to the process working directory unless you provide an absolute path accepted by your CI environment. Keep names deterministic and include the suite or test identifier when tests can run in parallel; otherwise two workers can write the same file.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Why a callback object is required
window.top.callPhantom is a bridge from the page context to the PhantomJS script context. The launcher receives the object as data. Only the launcher knows the PhantomJS page object, so only its onCallback function can call page.render().
Rank #2
Consequently, window.top.callPhantom('render') is not a complete implementation. It sends a string, while the launcher above expects an object containing type: 'render' and a destination filename. If you prefer to generate names in the launcher, accept a render request without fname and substitute a counter there; passing the path from the helper keeps artifact naming close to the test that created it.
Control the captured area and file format
PhantomJS’s capture API renders the current page after it has loaded. Set the viewport before the page is opened when you need a reproducible layout:
page.viewportSize = {
width: 1280,
height: 900
};
When the whole viewport is not useful, assign a clipping rectangle before rendering:
page.clipRect = {
top: 0,
left: 0,
width: 640,
height: 480
};
page.render('.tmp/screenshots/region.png');
page.render supports PNG, JPEG, GIF and PDF output. Match the extension and your artifact handling to the format you select. PNG is generally the simplest choice for test diffs; PDF is useful when the artifact is intended for review rather than pixel comparison. The custom launcher can pass any filename supported by PhantomJS:
takeScreenshot('.tmp/screenshots/state.jpg');
takeScreenshot('.tmp/screenshots/state.pdf');
If your suite needs several viewport sizes, set page.viewportSize in the PhantomJS setup for each run rather than changing it from page JavaScript. A viewport change after layout-sensitive assertions can invalidate the state you meant to capture.
Make screenshots dependable in CI
Create and collect the directory
Make .tmp/screenshots/ before Karma starts, and configure the CI system to archive that directory even when a test fails. A failing assertion should not discard the screenshot that explains the failure.
Avoid filename collisions
The counter in the helper is process-local. It prevents collisions within one browser process, but parallel Karma workers can still produce the same names. Prefix files with a suite name, shard number or test identifier when parallel execution is enabled.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Keep rendering after the state change
Invoke the helper after the event, route change or DOM update you want to inspect. If the application updates asynchronously, wait for the same condition your assertion uses before sending the callback. The callback request is not a synchronization primitive for pending application work.
Use workspace-relative paths
A relative path such as .tmp/screenshots/ makes artifact collection predictable. Absolute paths can work, but they must exist and be writable inside the CI agent; paths from a developer workstation will not exist in a clean build container.
Troubleshooting common failures
Nothing is written
Check that Karma is actually using PhantomJSCustom, not the plain PhantomJS launcher, and that the callback is nested under customLaunchers.PhantomJSCustom.options. Then verify that the test calls window.top.callPhantom with an object and that data.type is exactly 'render'.
Rank #4
callPhantom is undefined
This is expected when the bundle runs outside PhantomJS. The helper’s guard avoids an exception in Chrome or a local browser. In Karma, confirm that the PhantomJS launcher started successfully and that the helper is called from the page context, not from the Node.js Karma configuration.
The callback runs but the file is missing
Most often the destination directory was never created or is not writable. Create it before Karma starts, use a workspace-relative path, and inspect the CI process’s current working directory. Also check for a filename collision that caused a later render to replace an earlier artifact.
The screenshot captures the wrong area
Set page.viewportSize for the intended browser dimensions and use page.clipRect when only a region is needed. A clipping rectangle is measured in page coordinates; values outside the rendered page can produce an empty or truncated image.
The image is blank or stale
Render only after the page has reached the state under test. If a test triggers asynchronous rendering, wait for a selector, class or other observable condition before calling the helper. A callback request cannot make unfinished application work complete.
PDF or JPEG handling fails downstream
PhantomJS can produce PNG, JPEG, GIF and PDF, but your artifact viewer or comparison step may accept only some of them. Confirm the extension and configure the CI collector for that file type; use PNG when the pipeline assumes raster images.
Best Value
Understand the maintenance trade-off
PhantomJS is headless command-line software commonly driven through a test runner; it is not itself a test framework. The PhantomJS project homepage states, “Important: PhantomJS development is suspended until further notice.” That makes this custom-launcher technique appropriate for maintaining an existing Karma/PhantomJS 2 suite, but a new system should evaluate a maintained browser runner against the same needs: screenshot API support, Karma integration, headless CI reliability, viewport and clipping controls, artifact paths, browser-engine currency and project maintenance.
Or skip the browser setup
If the goal is simply to capture a URL rather than render inside a Karma process, ScreenshotNeo provides a single HTTP request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
See the parameter reference in the ScreenshotNeo documentation. The following calls are runnable as written after replacing YOUR_API_KEY:
cURL
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,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, pre-capture clicks, selector or network-idle waits, request and resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API and an OpenAPI specification. Existing integrations can use the parameter names used by other screenshot APIs.
| Plan | Included screenshots | Price |
|---|---|---|
| Free | 1,000 per month | $0; no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots each month without adding a card.
Frequently Asked Questions
Is PhantomJS itself a test framework?
No. PhantomJS is headless command-line software used to launch tests through a suitable runner such as Karma; the assertions and test lifecycle remain Karma’s responsibility.
Can ScreenshotNeo run my Karma suite?
No. ScreenshotNeo captures a supplied URL or PDF through its API or MCP tools. Use the custom launcher above when the screenshot must be produced inside a Karma test; use ScreenshotNeo when an external URL capture is sufficient.
Quick Recap
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.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.




