Use the callback argument passed to test.step(), capture the page with page.screenshot(), and attach the returned PNG buffer with step.attach(). This keeps the image on the specific report step instead of attaching it only to the test. Step-scoped attachments require Playwright 1.51 or newer, and the reporter you choose must support rendering them.
Attach a screenshot to the exact test.step()
test.step() can provide a TestStepInfo object to its callback. Its attach() method accepts either a file path or a byte buffer, but not both. For an in-memory PNG returned by page.screenshot(), pass the buffer as body and identify it with contentType: 'image/png'.
import { test, expect } from '@playwright/test';
test('checkout shows confirmation', async ({ page }) => {
await page.goto('https://example.com/checkout');
await test.step('verify confirmation page', async step => {
const screenshot = await page.screenshot();
await step.attach('confirmation screenshot', {
body: screenshot,
contentType: 'image/png',
});
await expect(
page.getByRole('heading', { name: 'Order confirmed' })
).toBeVisible();
});
});
The awaited attach() call copies the attachment to a location reporters can access. After it resolves, a temporary file used to create the buffer can be deleted safely. Keeping the assertion in the same callback makes the screenshot evidence and the check belong to one named step.
Playwright’s TestStepInfo API documents this step-level method and notes that it was added in version 1.51. Check the version installed in your project before using it.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
Choose the correct attachment scope
Step-level evidence with step.attach()
Use the callback’s step object when the image explains one operation: a login result, a submitted form, or a confirmation page. The attachment appears in that step wherever the selected reporter supports step attachments.
Whole-test evidence with testInfo.attach()
Use the test fixture’s testInfo when the screenshot is evidence for the entire test rather than one action.
import { test } from '@playwright/test';
test('dashboard loads', async ({ page }, testInfo) => {
await page.goto('https://example.com/dashboard');
const image = await page.screenshot({ fullPage: true });
await testInfo.attach('dashboard page', {
body: image,
contentType: 'image/png',
});
});
The distinction is intentional: step.attach() is nested under one test.step(), while testInfo.attach() is recorded at test scope. Do not use the latter when readers need to find the image beside a particular step.
Attach a saved screenshot instead of a buffer
A path is useful when another process already creates the image or when you want to inspect the file locally. Supply path and omit body.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
await test.step('capture payment form', async step => {
const file = 'artifacts/payment-form.png';
await page.screenshot({ path: file, fullPage: true });
await step.attach('payment form', {
path: file,
contentType: 'image/png',
});
});
The API requires exactly one input form. Passing both body and path is invalid. For PNG files, explicitly setting the content type helps supporting reporters interpret the attachment as an image.
Select the screenshot area that provides useful evidence
Viewport screenshot
await page.screenshot() captures the current viewport. It is usually the smallest and fastest image and is appropriate when the relevant control is already visible.
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
Full-page screenshot
const image = await page.screenshot({ fullPage: true });
fullPage: true captures the complete scrollable page. It can produce a large attachment, so use it for evidence that actually depends on content below the fold.
Element screenshot
const image = await page
.getByRole('region', { name: 'Order summary' })
.screenshot();
A locator screenshot isolates the component under test and avoids unrelated page content. This is often easier to read in a report than a full browser image.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Capture after the state is ready
Place the screenshot after navigation, an explicit wait, or an action that produces the state you want to document. A screenshot is evidence, not a synchronization mechanism. Use Playwright assertions such as toBeVisible() to wait for the expected UI, then capture or attach the image at the point that matters.
Generate and open the HTML report
To create Playwright’s built-in HTML report, run:
npx playwright test --reporter=html
The documented default output directory is playwright-report. Open it with:
npx playwright show-report
The report is a self-contained folder served as a web page. The HTML reporter’s opening behavior and output directory can be configured with options including PLAYWRIGHT_HTML_OPEN and PLAYWRIGHT_HTML_OUTPUT_DIR; consult the reporter documentation for the exact setting used by your project.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
Playwright’s API documentation says that “Some reporters show test step attachments.” Recording an attachment therefore does not guarantee identical presentation in every reporter. If the image is present in the test data but absent from a third-party UI, verify that reporter’s step-attachment support or use the HTML reporter to inspect the result.
Reusable helper for consistent step screenshots
When many tests need the same evidence format, wrap capture and attachment in a helper. The helper below keeps the screenshot scoped to the calling step and allows full-page or viewport images.
import { Page, TestStepInfo } from '@playwright/test';
export async function attachScreenshot(
page: Page,
step: TestStepInfo,
name: string,
options: { fullPage?: boolean } = {},
) {
const body = await page.screenshot({
fullPage: options.fullPage ?? false,
type: 'png',
});
await step.attach(name, {
body,
contentType: 'image/png',
});
}
await test.step('review order summary', async step => {
await attachScreenshot(page, step, 'order summary');
await expect(page.getByText('Total')).toBeVisible();
});
Keep names stable and descriptive. A name such as confirmation screenshot is more useful in a long report than a generated timestamp.
Common errors and fixes
step.attach is not a function
Your installed Playwright version may predate 1.51, or the callback is not receiving the step argument. Update Playwright in the project and use async step => as the test.step() callback signature.
The image appears at test level, not under the step
The code probably calls testInfo.attach(). Move the call inside the relevant test.step() callback and call step.attach() instead.
Attachment validation fails
Pass either body or path, never both. A buffer from page.screenshot() belongs in body; a saved filename belongs in path. Set contentType: 'image/png' for PNG data.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
The report shows a download or no image preview
Check that the content type matches the bytes and that the reporter supports image attachments at step scope. The official documentation only promises rendering for some reporters. Try the HTML reporter to separate an attachment problem from a presentation limitation.
The screenshot is blank or shows the previous state
Capture after the page has reached the intended state. Wait for a meaningful locator or assertion, and ensure an action such as a click or navigation has been awaited before taking the screenshot.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsThe report becomes too large
Prefer a locator screenshot or viewport capture when full-page evidence is unnecessary. PNG preserves detail but can be large; capture only the state needed to explain the step, and avoid attaching duplicate images at both step and test scope.
Performance, reliability, and maintenance considerations
- Capture only decision points. Screenshots add browser work and report storage. Attach one after a state-changing action or failed assertion rather than after every low-level operation.
- Use deterministic dimensions. Configure a consistent browser project viewport so visual evidence is comparable between runs.
- Keep the assertion separate from the capture. The screenshot documents what the page looked like; the assertion determines pass or fail. This also lets you attach evidence before an assertion throws.
- Use PNG when readability matters. It is lossless and matches the documented
image/pngcontent type. Element captures can reduce file size without hiding unrelated UI. - Check version changes. Step attachment support depends on the Playwright version installed by the project, not merely the version used by a global command.
- Validate the reporter in CI. A reporter may record attachments without displaying them in its web interface. Download or open the generated report as part of a pipeline check if screenshots are required evidence.
Or skip the browser setup: ScreenshotNeo
If you need a screenshot of a public URL rather than evidence from an already-running Playwright test, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a single GET request and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled.
Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response reports the result through X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots. Every feature is available on every plan. Use the ScreenshotNeo API documentation for authentication and option details.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.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://example.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 also supports full-page capture with lazy images loaded, CSS-selector element capture, device presets and custom viewports, retina scale, PDF paper settings, custom CSS or JavaScript, pre-capture clicks, hidden selectors, waits, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Create a free ScreenshotNeo account to get 1,000 screenshots each month without a card.
FAQ
Can I attach a screenshot after the step callback finishes?
No. The step-scoped API is available through the TestStepInfo callback argument, so capture and attach the image inside that callback. Use testInfo.attach() for evidence that belongs to the complete test.
Does toHaveScreenshot() attach an image to a report step?
Visual assertions and report attachments solve different problems. toHaveScreenshot() compares an image with an expected snapshot; step.attach() explicitly records evidence for the report. You can use both when a test needs comparison and a human-readable artifact.
Can a step attachment be a non-image file?
Yes. The attachment API accepts a body or path with a content type. For screenshots, use PNG bytes or a PNG path and set image/png so compatible reporters can render it correctly.
Frequently Asked Questions
Can I attach a screenshot after the step callback finishes?
No. Use the TestStepInfo callback argument inside test.step(); use testInfo.attach() for test-level evidence.
Does toHaveScreenshot() attach an image to a report step?
No. It compares against an expected snapshot. Use step.attach() when you need an explicit report artifact.
Can a step attachment be a non-image file?
Yes. Provide a body or path and the appropriate content type; PNG screenshots should use image/png.
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.

