Implementation:DevExpress Testcafe ScreenshotCapturer
| Knowledge Sources | |
|---|---|
| Domains | Screenshot Capture, Image Processing |
| Last Updated | 2026-02-12 12:00 GMT |
Overview
ScreenshotCapturer handles taking browser screenshots, cropping them to the client area or element bounds with DPR scaling, and optionally generating thumbnails.
Description
The screenshot subsystem consists of two primary modules:
Capturer (capturer.js) is the main class that orchestrates the full screenshot pipeline. It delegates to the browser provider to take the raw screenshot, reads the PNG file, crops it using mark-seed positioning and/or explicit crop dimensions (scaled by device pixel ratio), writes the result back, and optionally generates a thumbnail via testcafe-browser-tools. It manages file paths through a configurable PathPattern with support for custom paths, error-specific paths, and sequential file indexing.
crop.js provides the image cropping functions. It locates a pixel-level "mark seed" embedded in the screenshot to determine the exact client area boundary, calculates clip regions based on mark position and/or explicit crop dimensions, validates the resulting dimensions, and copies the relevant image portion.
Usage
A Capturer instance is created per test run by Screenshots.createCapturerFor. During test execution, the test run calls captureAction(options) for on-demand screenshots and captureError(options) for failure screenshots. The cropping functions are called internally by Capturer._capture.
Code Reference
Source Location
- Repository: DevExpress_Testcafe
- File (Capturer): src/screenshots/capturer.js
- Lines: 1-207
- File (Crop): src/screenshots/crop.js
- Lines: 1-130
Signature (Capturer)
export default class Capturer {
constructor (baseScreenshotsPath, testEntry, connection, pathPattern, fullPage, thumbnails, warningLog) {
this.enabled = !!baseScreenshotsPath;
this.baseScreenshotsPath = baseScreenshotsPath;
this.testEntry = testEntry;
this.provider = connection.provider;
this.browserId = connection.id;
this.warningLog = warningLog;
this.pathPattern = pathPattern;
this.fullPage = fullPage;
this.thumbnails = thumbnails;
}
static _getDimensionWithoutScrollbar(fullDimension, documentDimension, bodyDimension);
static _getCropDimensions(cropDimensions, pageDimensions);
static _getClientAreaDimensions(pageDimensions);
static async _isScreenshotCaptured(screenshotPath);
async captureAction(options);
async captureError(options);
}
Signature (Crop functions)
export function markSeedToId(markSeed);
export function calculateMarkPosition(pngImage, markSeed);
export function getClipInfoByMarkPosition(markPosition, { width, height });
export function getClipInfoByCropDimensions(clipInfo, cropDimensions);
export function calculateClipInfo(pngImage, markSeedPosition, clientAreaDimensions, cropDimensions);
export async function cropScreenshot(image, { path, markSeedPosition, clientAreaDimensions, cropDimensions });
Import
import Capturer from '../screenshots/capturer';
import { cropScreenshot, calculateMarkPosition, markSeedToId } from '../screenshots/crop';
I/O Contract
Inputs (Capturer Constructor)
| Name | Type | Required | Description |
|---|---|---|---|
| baseScreenshotsPath | string |
Yes | Root directory for screenshot output; falsy disables capture |
| testEntry | object |
Yes | Test entry containing testRuns and screenshots arrays
|
| connection | BrowserConnection |
Yes | Browser connection providing provider and id
|
| pathPattern | PathPattern |
Yes | Pattern for generating screenshot file paths (with data like userAgent, fileIndex) |
| fullPage | boolean |
Yes | Whether to capture the full page or just the viewport |
| thumbnails | boolean |
Yes | Whether to generate thumbnail images |
| warningLog | WarningLog |
Yes | Warning log for non-fatal issues like path overwrites |
Inputs (captureAction / captureError options)
| Name | Type | Required | Description |
|---|---|---|---|
| actionId | string |
No | ID of the action that triggered the screenshot |
| failedActionId | string |
No | ID of the failed action (overrides actionId in the result) |
| pageDimensions | object |
No | Page dimension info including innerWidth, innerHeight, documentWidth, documentHeight, bodyWidth, bodyHeight, dpr
|
| cropDimensions | object |
No | Explicit crop bounds: { top, left, bottom, right }
|
| markSeed | Buffer |
No | Pixel mark seed for locating the client area boundary |
| customPath | string |
No | Custom file path (overrides pattern) |
| customPathPattern | string |
No | Custom path pattern for this specific screenshot |
| fullPage | boolean |
No | Override the instance-level fullPage setting |
| thumbnails | boolean |
No | Override the instance-level thumbnails setting |
Outputs
| Name | Type | Description |
|---|---|---|
| captureAction return | null> | Full path to the saved screenshot file, or null if capture is disabled
|
| captureError return | null> | Full path to the saved error screenshot file, or null if capture is disabled
|
| cropScreenshot return | null> | Cropped image data, or null if no cropping was needed
|
Usage Examples
// Creating a capturer (internal, called by Screenshots.createCapturerFor)
const capturer = new Capturer(
baseScreenshotsPath,
testEntry,
connection,
pathPattern,
fullPage,
thumbnails,
warningLog
);
// Capture a screenshot on action
const screenshotPath = await capturer.captureAction({
actionId: 'action-id-123',
pageDimensions: {
innerWidth: 1024, innerHeight: 768,
documentWidth: 1024, documentHeight: 2000,
bodyWidth: 1024, bodyHeight: 2000,
dpr: 2,
},
cropDimensions: { top: 100, left: 50, bottom: 300, right: 400 },
markSeed: markSeedBuffer,
fullPage: false,
thumbnails: true,
});
// Capture a screenshot on error
const errorScreenshotPath = await capturer.captureError({
failedActionId: 'failed-action-456',
pageDimensions: { /* ... */ },
});
Internal Mechanics
Mark Seed Detection
The browser injects a pixel-level "mark" (a binary pattern of black/white pixels) into the screenshot. calculateMarkPosition scans the image buffer for this pattern, tolerating color values within a threshold of 10 from pure black (0) or white (255). The mark position identifies the bottom-right corner of the client area, enabling precise cropping even when browser chrome or scrollbars are present.
DPR Scaling
All crop dimensions from the browser are in CSS pixels. _getCropDimensions multiplies them by the device pixel ratio (dpr) to convert to physical pixels before cropping the PNG image.
Async Queue
Screenshots to the same file path are serialized using addToQueue / isInQueue from utils/async-queue. If a screenshot is already being written to the same path, a warning is emitted about potential overwrites.
Thumbnail Generation
When thumbnails are enabled, generateThumbnail from testcafe-browser-tools creates a scaled-down copy in a thumbnails/ subdirectory alongside the original screenshot.