What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Stub the module at the import boundary, make it resolve to the smallest canvas-like object your caller needs, and assert the caller’s inputs and follow-up behavior. A focused unit test should prove that your code passes the right element and options to html2canvas, handles the returned promise, and uses the result correctly. It should not attempt to prove that CSS, images, iframes, or browser security rules render accurately; keep those checks in a real browser test.
What the stubbed test should prove
html2canvas accepts a DOM element and optional configuration, then returns a Promise resolving to a <canvas> element. Your unit test replaces the imported function with a test double and verifies your application’s contract with that function.
- The intended element is passed, rather than a parent, stale node, or
null. - Only the options your application deliberately sets are asserted.
- The asynchronous fulfillment path is awaited.
- The resolved canvas is handed to the next operation, such as
toDataURL(), a download helper, or an upload function. - If production code handles rejection, that failure path is tested too.
This isolates caller logic. It does not test html2canvas itself or visual fidelity.
A framework-neutral module-boundary pattern
Production code might look like this:
import html2canvas from 'html2canvas';
import { downloadImage } from './downloadImage.js';
export async function captureReport(element) {
const options = { scale: 2, useCORS: true };
const canvas = await html2canvas(element, options);
const dataUrl = canvas.toDataURL('image/png');
downloadImage(dataUrl, 'report.png');
return canvas;
}
The test must mock the same export and import path used by production. The exact API differs between Jest, Vitest, Mocha plus a loader, and other runners, so treat the following as pseudocode for the assertions, not a copy-and-paste command for one framework:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
const canvasStub = {
toDataURL: () => 'data:image/png;base64,test'
};
html2canvasMock.mockResolvedValue(canvasStub);
document.body.innerHTML = '<div id="report">Report</div>';
const targetElement = document.querySelector('#report');
await captureReport(targetElement);
expect(html2canvasMock).toHaveBeenCalledWith(targetElement, {
scale: 2,
useCORS: true
});
expect(downloadImage).toHaveBeenCalledWith(
'data:image/png;base64,test',
'report.png'
);
Use your runner’s supported module-mocking mechanism (for example, its hoisted factory or mocked-import API) before importing the module under test. If the application imports a named export, mock that named export; if it imports a default export, replace the default. Mocking a different path or export leaves the real function in place and makes the test slow, environment-dependent, or misleading.
Return only what the caller consumes
The mock does not need to reproduce a browser canvas. If the code calls only toDataURL, provide only toDataURL. If it reads width and height, add those properties. If it calls getContext('2d'), provide a minimal context with the methods your code invokes.
const canvasStub = {
width: 800,
height: 600,
getContext: () => ({ clearRect: () => {} }),
toDataURL: () => 'data:image/png;base64,test'
};
Do not build a fake renderer “just in case.” Every extra method is another unverified imitation that can conceal a change in your application’s actual requirements.
Testing asynchronous success and failure
Because html2canvas returns a Promise, make the test asynchronous and wait for the application action to finish. A synchronous assertion immediately after calling an async function can run before the mock is observed.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →html2canvasMock.mockResolvedValue(canvasStub);
await expect(captureReport(targetElement)).resolves.toBe(canvasStub);
expect(downloadImage).toHaveBeenCalled();
If your code catches errors and displays a message, reject the mock and assert that behavior:
html2canvasMock.mockRejectedValue(new Error('capture failed'));
await captureReport(targetElement);
expect(showCaptureError).toHaveBeenCalledWith('capture failed');
expect(downloadImage).not.toHaveBeenCalled();
If production intentionally lets the rejection propagate, assert the rejection instead:
Rank #2
html2canvasMock.mockRejectedValue(new Error('capture failed'));
await expect(captureReport(targetElement)).rejects.toThrow('capture failed');
Reset the mock between tests. Otherwise call counts, implementations, or a previous resolved value can leak into another case. Also restore any mocked download, notification, or upload module after each test.
Assert options that matter to your application
The documented configuration options include scaling, output dimensions, cross-origin loading, timeouts, element exclusion, and cloning behavior. Assert an option only when your code intentionally supplies it or when changing it would alter a user-visible decision.
| Option or input | What a unit assertion verifies | What it cannot verify |
|---|---|---|
scale |
Your caller requests the chosen rendering scale. | That the resulting pixels have the expected density or dimensions. |
useCORS |
Your caller asks html2canvas to attempt CORS image loading. | Whether the remote server sends a usable CORS header or the image is included. |
| Timeout or dimensions | Your code forwards the intended numeric values. | How a particular browser behaves when a resource is slow or a layout changes. |
| Ignore/exclusion and clone settings | The selector, predicate, or callback is supplied as designed. | Whether every matching node is excluded or the cloned DOM looks correct. |
Prefer an exact object assertion when the whole options object is part of your API. Use a partial assertion when unrelated defaults may legitimately be added by the caller. Avoid asserting html2canvas’s internal defaults; those belong to the library, not your unit.
DOM setup without pretending to have a browser renderer
Many test runners provide a simulated DOM such as jsdom. It is useful for creating an element and checking that the right node is selected, but it does not turn the test into a rendering test. Build only the structure your code queries:
document.body.innerHTML = `
<section data-testid="report">
<h1>Quarterly report</h1>
</section>`;
const target = document.querySelector('[data-testid="report"]');
await captureReport(target);
expect(html2canvasMock).toHaveBeenCalledWith(target, expect.any(Object));
Test selector and validation branches separately: missing elements, disabled capture buttons, duplicate IDs, and a user changing the target before the click. These are caller decisions and remain valuable even though no pixels are produced.
Why a stub cannot prove screenshot accuracy
html2canvas reconstructs an image from DOM information rather than taking a native browser screenshot. Its documentation warns that the result “may not be 100% accurate to the real representation” because it builds the image from information available on the page (about and limitations). CSS support is incomplete; cross-origin images can be restricted; and content inside cross-origin iframes is inaccessible.
A passing mocked test therefore says nothing about:
- font loading, layout, shadows, filters, pseudo-elements, or unsupported CSS;
- images that lack suitable CORS headers or are blocked by a policy;
- iframe contents from another origin;
- browser-specific computed styles and device-pixel behavior;
- the visual difference between a generated data URL and the expected design.
Keep those questions in a browser-level test with controlled fixtures and an explicit visual assertion. The package’s npm page describes fast unit tests separately from Playwright visual-regression tests against reference fixtures (package information). That separation is a useful model for application tests: mock the dependency for logic, and drive a real browser for rendering.
When Node.js is the wrong environment
The official FAQ states that html2canvas relies on window, document, and computed styles that do not exist in Node.js (FAQ). A unit test can run in a simulated DOM because html2canvas itself is replaced before it executes. Calling the real library in a plain Node process is a different problem and generally fails without a browser environment.
For integration or visual tests, launch a real browser through an automation tool such as Playwright or Puppeteer, load the page over the origin and assets you intend to support, then capture or compare the result. Do not “fix” a unit test by installing a large collection of canvas shims; that usually creates a brittle third test layer while still not exercising browser CSS and security behavior.
Free tools Windows power users keep installed
One-click scans. No signup required.
A practical test matrix
| Case | Mock behavior | Assertions |
|---|---|---|
| Normal capture | Resolve with a canvas stub | Target element, intentional options, and downstream call. |
| Target unavailable | Not called, or caller rejects before capture | Validation message and no download/upload. |
| html2canvas failure | Reject with an error | Error UI, logging, retry state, or propagated rejection. |
| Different option branch | Resolve with the same stub | Only the branch-specific option changes. |
| Real rendering fixture | Call real html2canvas in a browser | Visual or pixel-tolerance result, resource and browser behavior. |
Troubleshooting common stub failures
The real html2canvas function is still running
Cause: the mock was installed after the module under test was imported, or the mocked path does not match the production import. Fix: configure the mock before importing the subject and use the exact specifier and export shape.
“Cannot read properties of undefined” on the canvas
Cause: the resolved object lacks a method or property your caller uses. Fix: inspect the failing line and add only that member, such as toDataURL, width, or getContext.
Rank #4
The assertion runs before the call
Cause: the test does not await the function or the function starts work in a later callback. Fix: return or await the Promise, and await any public completion signal your application exposes.
Call counts differ between tests
Cause: shared mock state. Fix: clear calls and implementations in the runner’s per-test teardown, and recreate DOM fixtures for each test.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →The unit test passes but the image is wrong
Cause: a stub verifies caller behavior, not rendering fidelity. Fix: add a browser test with representative fonts, images, iframes, and CSS; investigate CORS and browser policies there.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is an automated website image or PDF rather than testing a function that calls html2canvas, ScreenshotNeo provides a browser-backed screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status.
One GET request returns PNG, JPEG, WebP, or PDF. The same service supports full-page lazy-image loading, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, easing migration.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for authentication, output and option details. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients, so an AI agent can request captures without you wiring browser automation.
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to try it without a card.
Best Value
FAQ
Does the mock need to be an actual HTMLCanvasElement?
No. It needs only the properties and methods the caller reads or invokes. Use a real canvas only in a browser-level test that is intentionally checking rendering behavior.
Should I mock html2canvas in every test?
Mock it in caller unit tests. Keep a smaller set of real-browser tests for the CSS, assets, iframe, and browser-policy behavior that a mock cannot observe.
Can I test html2canvas directly in Node.js?
Not in a plain Node process. The library depends on browser APIs; use a browser automation environment for an integration test.
Recommended Free Tools
What if my application uses a named import?
Mock the named export with the runner’s module-mocking API and preserve the same import shape used by production. A default-export mock will not replace a named import.
Frequently Asked Questions
Does the mock need to be an actual HTMLCanvasElement?
No. It needs only the properties and methods the caller reads or invokes. Use a real canvas only in a browser-level test that is intentionally checking rendering behavior.
Should I mock html2canvas in every test?
Mock it in caller unit tests. Keep a smaller set of real-browser tests for the CSS, assets, iframe, and browser-policy behavior that a mock cannot observe.
Can I test html2canvas directly in Node.js?
Not in a plain Node process. The library depends on browser APIs; use a browser automation environment for an integration test.
What if my application uses a named import?
Mock the named export with the runner’s module-mocking API and preserve the same import shape used by production. A default-export mock will not replace a named import.
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.

