Screenshots
craftdriver exposes a single options-bag screenshot() method on both Browser and ElementHandle. PNG bytes are always returned as a Buffer; pass path to also write the file in one step.
Page screenshots
browser.screenshot(opts?)
| Option | Type | Default | Notes |
|---|---|---|---|
path | string | — | Write the PNG to this file path. |
selector | string | By | full viewport | If set, capture only the matching element. |
fullPage | boolean | false | Capture the entire scrollable document. Requires BiDi. |
timeout | number (ms) | default | Wait timeout when locating selector. |
fullPage and selector are mutually exclusive.
// Viewport only (default), buffer
const buffer = await browser.screenshot();
// Viewport, written to disk
await browser.screenshot({ path: 'screenshots/homepage.png' });
// Whole scrollable document (BiDi `browsingContext.captureScreenshot`
// with `origin: "document"`).
await browser.screenshot({ fullPage: true, path: 'screenshots/full.png' });
// One element by CSS selector
const chart = await browser.screenshot({ selector: '#sales-chart' });
// One element, written to disk
await browser.screenshot({
selector: '#sales-chart',
path: 'screenshots/chart.png',
});Full-page vs viewport
Viewport capture (fullPage: false, the default) uses the W3C Classic Take Screenshot endpoint and is bounded by the visible viewport. Full-page capture uses BiDi browsingContext.captureScreenshot with origin: "document" and produces a PNG sized to the full scrollable content — useful for visual diffs of long pages and for archiving the complete rendered output. Full-page capture requires enableBiDi: true (the default); on Classic-only sessions it throws.
Element screenshots
element.screenshot(opts?)
| Option | Type | Default | Notes |
|---|---|---|---|
path | string | — | Write the PNG to this file path. |
timeout | number | default | Element resolution timeout. |
const buffer = await browser.find('#product-image').screenshot();
await browser.find('#logo').screenshot({ path: 'logo.png' });Use cases
Test failure documentation
afterEach(async (context) => {
if (context.task.result?.state === 'fail') {
const name = context.task.name.replace(/\s+/g, '-');
await browser.screenshot({ path: `screenshots/${name}.png` });
}
await browser.quit();
});Visual comparison baseline
await browser.navigateTo('https://example.com');
await browser.screenshot({ path: 'baseline/homepage.png' });
// Later, capture the candidate and diff with an image library:
await browser.navigateTo('https://example.com');
await browser.screenshot({ path: 'current/homepage.png' });Component screenshots
await browser.screenshot({ selector: 'header', path: 'components/header.png' });
await browser.screenshot({ selector: '.sidebar', path: 'components/sidebar.png' });
await browser.screenshot({ selector: 'footer', path: 'components/footer.png' });Before / after an action
await browser.screenshot({ selector: '#order-form', path: 'before-submit.png' });
await browser.find('#submit-order').click();
await browser.expect('#confirmation').toBeVisible();
await browser.screenshot({ selector: '#confirmation', path: 'after-submit.png' });Tips
Create the screenshot directory
screenshot({ path }) writes through fs.writeFile; the parent directory must already exist:
import { mkdir } from 'fs/promises';
await mkdir('screenshots', { recursive: true });
await browser.screenshot({ path: 'screenshots/test.png' });Timestamped filenames
const ts = new Date().toISOString().replace(/[:.]/g, '-');
await browser.screenshot({ path: `screenshots/page-${ts}.png` });Wait for stability
await browser.navigateTo('https://example.com');
await browser.expect('#main-content').toBeVisible();
await browser.screenshot({ path: 'stable-page.png' });Buffer usage
When you only need bytes (uploading, image diffing), omit path:
const buffer = await browser.screenshot();
import { writeFile } from 'fs/promises';
await writeFile('screenshot.png', buffer);
await uploadToS3(buffer, 'screenshots/latest.png');