Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture the browser page inside your test’s failure hook, save the image before the browser closes, and attach that file to an email sent through SMTP. This keeps the screenshot tied to the failed test and gives the recipient useful context such as the test name, URL, timestamp, and error. Cypress can take screenshots automatically during cypress run; with Playwright, add an explicit capture in the failure-handling code.

Choose where to capture the failure

The reliable point to take a screenshot is while the failed test’s page and browser context are still open. Capture first; then upload the image to your CI artifacts and send the notification. If you close the page or browser before the capture finishes, the screenshot may be missing or fail to represent the page that caused the error.

  • Use a test-runner failure hook when you need the failed page itself, its URL, or browser-specific state.
  • Use CI email notifications for a simple failed-workflow alert. GitHub Actions offers email notifications with an “Only notify for failed workflows” option, but that alert does not attach a browser screenshot.
  • Keep an artifact as well as emailing when the team may need to investigate after the email is buried or deleted.

Screenshots can expose personal data, tokens, and internal URLs. Mask sensitive page regions before capture when possible, limit recipients, and apply the same access controls and retention expectations you use for other test artifacts.

Capture and email a screenshot with Playwright and Node.js

Playwright supports viewport, element, and full-page screenshots. The example below uses a test failure hook, writes a uniquely named PNG, and sends it as a Nodemailer attachment. It assumes a Playwright test project using JavaScript, plus Nodemailer installed in the project with npm install nodemailer. Set the SMTP settings and recipient as environment variables in your local environment or CI secret store; do not commit credentials.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Logitech Brio 101 Full HD 1080p Webcam for Streaming and Meetings - Black
  • Compatible with Nintendo Switch 2’s new GameChat mode
  • Auto-Light Balance: RightLight boosts brightness by up to 50%, reducing shadows so you look your best—compared to previous-generation Logitech webcams (1)
  • Privacy with a Slide: The integrated webcam cover makes it easy to get total, reliable privacy when you're not on a video call
  • Built-In Mic: The built-in microphone lets others hear you clearly during video calls
  • Easy Plug-And-Play: The Brio 101 works with most video calling platforms, including Microsoft Teams, Zoom and Google Meet—no hassle; it just works

Set SMTP environment variables

Provide SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM, and MAIL_TO. Use the port and TLS mode required by your SMTP provider. For port 465, the example enables secure SMTP; for other ports, it uses the server’s upgrade-to-TLS behavior when offered.

Add a failure hook to the Playwright test

Save this as a test file such as tests/checkout.spec.js. Replace the URL, selectors, and test steps with the application and failure scenario you need to diagnose.

const { test, expect } = require('@playwright/test');
const nodemailer = require('nodemailer');
const path = require('node:path');
const fs = require('node:fs/promises');

function required(name) {
  const value = process.env[name];
  if (!value) throw new Error(`Missing required environment variable: ${name}`);
  return value;
}

const mailer = nodemailer.createTransport({
  host: required('SMTP_HOST'),
  port: Number(process.env.SMTP_PORT || 587),
  secure: Number(process.env.SMTP_PORT || 587) === 465,
  auth: {
    user: required('SMTP_USER'),
    pass: required('SMTP_PASS'),
  },
});

test('checkout completes', async ({ page }, testInfo) => {
  let failedScreenshot;

  try {
    await page.goto('https://example.com/checkout');
    await page.getByRole('button', { name: 'Place order' }).click();
    await expect(page.getByText('Order confirmed')).toBeVisible();
  } catch (error) {
    // Capture while this page is still open. Unique paths avoid collisions in parallel runs.
    const safeTitle = testInfo.title.replace(/[^a-z0-9_-]+/gi, '_').slice(0, 80);
    failedScreenshot = path.resolve(
      testInfo.outputDir,
      `${safeTitle}-${testInfo.workerIndex}-${Date.now()}.png`
    );
    await fs.mkdir(path.dirname(failedScreenshot), { recursive: true });
    await page.screenshot({ path: failedScreenshot, fullPage: true, animations: 'disabled' });
    throw error;
  }
});

test.afterEach(async ({ page }, testInfo) => {
  if (testInfo.status === testInfo.expectedStatus) return;

  // afterEach runs before Playwright disposes of the test's page/context.
  const safeTitle = testInfo.title.replace(/[^a-z0-9_-]+/gi, '_').slice(0, 80);
  const screenshotPath = path.resolve(
    testInfo.outputDir,
    `${safeTitle}-${testInfo.workerIndex}-${Date.now()}-failure.png`
  );

  try {
    await fs.mkdir(path.dirname(screenshotPath), { recursive: true });
    await page.screenshot({ path: screenshotPath, fullPage: true, animations: 'disabled' });

    const url = page.url();
    const errorText = testInfo.error?.message || 'Test failed without an error message';
    await mailer.sendMail({
      from: required('MAIL_FROM'),
      to: required('MAIL_TO'),
      subject: `[Playwright failure] ${testInfo.title}`,
      text: [
        `Test: ${testInfo.title}`,
        `URL: ${url}`,
        `Time: ${new Date().toISOString()}`,
        `Project: ${testInfo.project.name}`,
        `Worker: ${testInfo.workerIndex}`,
        `Result: ${testInfo.status} (expected ${testInfo.expectedStatus})`,
        `Error: ${errorText}`,
        `Screenshot: ${screenshotPath}`,
      ].join('n'),
      attachments: [{ filename: path.basename(screenshotPath), path: screenshotPath }],
    });
  } catch (notificationError) {
    // Preserve the test failure and make the notification problem visible in CI logs.
    console.error('Could not capture or email failure screenshot:', notificationError);
  }
});

Use one capture path, not both the catch block and afterEach, in a real test: the example’s catch block illustrates capturing exactly at the point of an assertion or navigation error, while afterEach demonstrates the notification pattern. To avoid duplicate captures, remove the catch block and capture in afterEach, or store the captured path in test-scoped state and have the hook attach that same file. The hook must run before page teardown; do not defer screenshot work to a process that no longer has access to the page.

A simpler and usually safer arrangement is to capture only in afterEach. Playwright’s built-in test fixtures close the page after the test hooks complete. For richer environments, add a CI artifact upload step for the output directory, and include the run’s artifact link in the email once the upload has completed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Logitech C270 720p Webcam Plug-and-Play Wide Screen Video Calling - Black
  • Compatible with Nintendo Switch 2’s new GameChat mode
  • Crisp HD 720p/30 fps video calls with diagonal 55° field of view and auto light correction. Compatible with popular platforms including Skype and Zoom.
  • The built-in noise-reducing mic makes sure your voice comes across clearly up to 1.5 meters away, even if you’re in busy surroundings.
  • C270’s RightLight 2 feature adjusts to lighting conditions, producing brighter, contrasted images to help you look good in all your conference calls.
  • The adjustable universal clip lets you attach the camera securely to your screen or laptop, or fold the clip and set the webcam on a shelf. You’re always ready for your next video call.

Choose the capture scope

  • await page.screenshot({ path: 'failure.png' }) captures the current viewport.
  • await page.screenshot({ path: 'failure.png', fullPage: true }) captures the full scrollable page.
  • await locator.screenshot({ path: 'element.png' }) captures a specific element. This can be useful for a chart, modal, or error panel.

Full-page capture may take longer and can expose content outside the visible viewport. Choose the smallest scope that preserves the evidence needed to debug the failure. When a page can change immediately after an error, capture as soon as practical: screenshot generation is asynchronous, and the resulting image may reflect a later state.

Use Cypress automatic or manual failure screenshots

Cypress says, “Cypress comes with the ability to take screenshots.” During cypress run, it automatically captures screenshots for test failures by default and saves them under cypress/screenshots. Automatic failure screenshots are not taken during cypress open. The screenshotOnRunFailure setting disables the automatic behavior.

For example, add a manual screenshot at a point where you want a specific name or capture mode:

cy.screenshot('checkout-failure', {
  capture: 'fullPage',
  blackout: ['[data-sensitive]', '.account-number'],
});

Cypress supports viewport, full-page, and runner capture, plus element screenshots, clipping, selector blackout, and onAfterScreenshot metadata. Failure screenshots use runner capture, which includes the Cypress command log. Full-page capture scrolls and stitches the application; fixed or sticky elements can consequently appear more than once. Cypress capture is asynchronous, so a page that changes immediately before the image is written may not be represented exactly as it looked at the instant of failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The generated file is evidence on disk; to email it, run a notification step after Cypress has finished writing the screenshot and attach the matching file path through your SMTP transport. If you need to capture a page state that automatic failure capture does not preserve, use the runner’s manual screenshot API at the relevant point and retain its resulting file location for the mail step.

Rank #3
Sale
NexiGo N60 1080P Webcam with Microphone, Software Control & Privacy Cover, USB HD Computer Web Camera, Plug and Play, for Zoom/Skype/Teams, Conferencing and Video Calling
  • 【Full HD 1080P Webcam】Powered by a 1080p FHD two-MP CMOS, the NexiGo N60 Webcam produces exceptionally sharp and clear videos at resolutions up to 1920 x 1080 with 30fps. The 3.6mm glass lens provides a crisp image at fixed distances and is optimized between 19.6 inches to 13 feet, making it ideal for almost any indoor use.
  • 【Wide Compatibility】Works with USB 2.0/3.0, no additional drivers required. Ready to use in approximately one minute or less on any compatible device. Compatible with Mac OS X 10.7 and higher / Windows 7, 8, 10 & 11 / Android 4.0 or higher / Linux 2.6.24 / Chrome OS 29.0.1547 / Ubuntu Version 10.04 or above. Not compatible with XBOX/PS4/PS5.
  • 【Built-in Noise-Cancelling Microphone】The built-in noise-canceling microphone reduces ambient noise to enhance the sound quality of your video. Great for Zoom / Facetime / Video Calling / OBS / Twitch / Facebook / YouTube / Conferencing / Gaming / Streaming / Recording / Online School.
  • 【USB Webcam with Privacy Protection Cover】The privacy cover blocks the lens when the webcam is not in use. It's perfect to help provide security and peace of mind to anyone, from individuals to large companies. 【Note:】Please contact our support for firmware update if you have noticed any audio delays.
  • 【Wide Compatibility】Works with USB 2.0/3.0, no additional drivers required. Ready to use in approximately one minute or less on any compatible device. Compatible with Mac OS X 10.7 and higher / Windows 7, 10 & 11, Pro / Android 4.0 or higher / Linux 2.6.24 / Chrome OS 29.0.1547 / Ubuntu Version 10.04 or above. Not compatible with XBOX/PS4/PS5.

Attach the screenshot with Nodemailer

Nodemailer accepts attachments as file paths, URLs, strings, Buffers, or readable streams. A path attachment is streamed from disk, so the file must remain present until sendMail has completed. A minimal mail step after a screenshot has been saved looks like this:

const nodemailer = require('nodemailer');

const transport = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: Number(process.env.SMTP_PORT || 587),
  secure: Number(process.env.SMTP_PORT || 587) === 465,
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});

await transport.sendMail({
  from: process.env.MAIL_FROM,
  to: process.env.MAIL_TO,
  subject: `Test failed: ${testName}`,
  text: `Test: ${testName}nURL: ${pageUrl}nError: ${errorMessage}`,
  attachments: [{ filename: 'failure.png', path: screenshotPath }],
});

For an HTML email that displays the image inline rather than as a conventional attachment, set a unique content ID and reference it in the body:

await transport.sendMail({
  from: process.env.MAIL_FROM,
  to: process.env.MAIL_TO,
  subject: `Test failed: ${testName}`,
  html: `<p>${testName}</p><img src="cid:failure-image" alt="Failed page screenshot">`,
  attachments: [{ filename: 'failure.png', path: screenshotPath, cid: 'failure-image' }],
});

Escape untrusted values before placing them in HTML. If message data can be influenced by untrusted input, Nodemailer’s disableFileAccess and disableUrlAccess controls can prevent attachment definitions from reading arbitrary local paths or fetching arbitrary URLs. Prefer a known screenshot path created by the test rather than passing user-controlled paths into an attachment.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

ScreenshotNeo can capture a URL with one API request; it is useful when you need a clean screenshot of a publicly reachable page without setting up browser automation. It does not replace a failure hook when you need the exact transient browser state of a failed test. The test-runner method above remains the right choice for that evidence. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; its API accepts parameters for image or PDF capture, and its documentation is at ScreenshotNeo docs.

Rank #4
Sale
EMEET C960 1080P Webcam with Microphone, 2 Mics, 90° FOV, Computer Camera
  • 1080P Webcam with Cover for Video Calls - EMEET computer webcam provides design and Optimization for professional video streaming. Realistic 1920 x 1080p video, 5-layer anti-glare lens, providing smooth video. C960 computer camera delivers 1920x1080 video with fixed focus (11.8–118.1 inches), so as to provide a clearer image. C960 USB webcam has a cover and can be removed automatically to meet your needs for privacy. For optimal image performance, use the webcam in a well-lit environment.
  • Built-in 2 Omnidirectional Mics - EMEET webcam with microphone for desktop features 2 built-in omnidirectional microphones, picking up your voice to create clear audio for communication. When installing the webcam, select EMEET C960 as the default microphone input device in your computer and video applications and select C960 as the default device in Zoom/Teams and ensure microphone permissions are enabled for proper use. Please note that C960 does not include built-in speakers.
  • Automatic Light Adjustment - Automatic exposure adjustment is applied in EMEET HD webcam 1080p so that the streaming webcam can deliver stable image performance. EMEET C960 camera for computer also features color adjustment and exposure optimization to help you look your best. For optimal video quality, it is recommended to use the webcam in normal or well-lit environments and select suitable video settings in your application. Proper lighting helps achieve a clearer and more balanced image.
  • Plug-and-Play & Upgraded USB Connectivity - New C960 webcam features both USB Type-A & A-to-C adapter connections for wider compatibility. For stable performance, connect the webcam directly to the computer's main USB port and ensure the device is recognized correctly. If a hub or docking station is used, please ensure it provides sufficient power and stable data transmission, as limited ports may affect performance. 90° wide-angle lens captures more participants without frequent adjustments.
  • High Compatibility & Multi Application - C960 webcam for laptop is compatible with Windows 10/11, macOS 10.14+, and Android TV 7.0+. Not supported: Windows Hello, TVs, tablets, or game consoles. It works with Zoom, Teams, Facetime, Google Meet, YouTube and more. Please select C960 webcam as the default camera and microphone device in your application and ensure camera/microphone permissions are enabled, especially on macOS. (Tips: Incompatible with Windows Hello)
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

In this request, replace the URL with the page you want to capture and provide your API key. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers indicating the page verdict and billing outcome. Its MCP server includes take_screenshot, get_page_info, and capture_pdf for AI agents and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Make notifications useful and safe

Include diagnostic context

A screenshot alone may not identify which run failed. Include the test name, failing URL, timestamp, browser or project, environment, exception text, and CI run or artifact link when available. Use run- and test-specific filenames so parallel workers cannot overwrite each other’s images.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep artifacts and clean up in the right order

Upload the screenshot to CI artifact storage if you need it to survive longer than an email attachment. Wait for both the upload and the email send to finish before deleting a temporary file. If email fails, retain or upload the image and report the notification error in CI logs; do not silently convert a mail outage into a successful test run.

Control what leaves the test environment

Apply Cypress blackout selectors or an equivalent masking strategy before capture. Avoid emailing screenshots to broad lists, and consider whether the image should be stored only as a restricted CI artifact. Screenshots can contain secrets or customer data even when the test itself is synthetic.

Best Value
Logitech C920x HD Pro PC Webcam Full 1080p/30fps Video - Black
  • Compatible with Nintendo Switch 2’s new GameChat mode
  • HD lighting adjustment and autofocus: The Logitech webcam automatically fine-tunes the lighting, producing bright, razor-sharp images even in low-light settings. This makes it a great webcam for streaming and an ideal web camera for laptop use
  • Advanced capture software: Easily create and share video content with this Logitech camera that is suitable for use as a desktop computer camera or a monitor webcam
  • Stereo audio with dual mics: Capture natural sound during calls and recorded videos with this 1080p webcam, great as a video conference camera or a computer webcam
  • Full HD 1080p video calling and recording at 30 fps. You'll make a strong impression with this PC webcam that features crisp, clearly detailed, and vibrantly colored video

Troubleshoot missing or unhelpful emails

  • No screenshot exists: In Cypress, check that the run used cypress run and that screenshotOnRunFailure is enabled. In Playwright, make sure the hook calls page.screenshot before the page or context closes.
  • Attachment file not found: Resolve paths from a known working directory, create the output directory first, and do not delete the image before Nodemailer finishes sending. A missing file is a documented attachment failure case.
  • SMTP authentication or connection failure: Check the host, port, TLS mode, username, password, and outbound network access. Load credentials from the CI secret store and inspect the SMTP error details in the job log.
  • Message rejected or too large: SMTP servers may reject a message or impose attachment-size limits. Check the provider’s response and limits; keep the image as a CI artifact and email a link when an attachment cannot be delivered.
  • Screenshot shows the wrong state: Capture immediately in the relevant failure path, before navigation, cleanup, or teardown changes the page. For full-page capture, remember that scrolling and stitching can affect fixed or sticky elements.
  • Parallel tests overwrite images: Include a test identifier, worker or shard identifier, and run-specific value in the filename, then ensure artifact paths remain distinct.
  • Mail step masks the test result: Catch and log notification errors separately. Preserve the original test failure as the primary result while making the mail failure visible for follow-up.

Choose a delivery pattern for your team

Use the framework already running in CI, then choose delivery based on reliability and retention rather than convenience alone.

Need Practical choice Trade-off
Failure image with browser state Capture in Cypress or Playwright while the page is open. Requires the runner’s failure path to complete before teardown.
Email notification with image Attach the saved file with Nodemailer over your SMTP service. Subject to SMTP delivery and attachment-size limits.
Longer investigation window Upload an artifact and put its link in the notification. Access and retention depend on the CI artifact configuration.
Workflow-level alert only Enable failed-workflow email notifications in GitHub Actions. Useful as a fallback, but it does not attach browser screenshots.

Decide who can receive the message, how long images should remain available, and what data may appear in them before enabling automatic delivery. For failures that matter beyond the current mailbox, retain a restricted artifact and use email as the alert rather than the only copy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I email a screenshot from Cypress open?

Cypress does not automatically capture failure screenshots in `cypress open`; automatic failure capture is documented for `cypress run`. Use an explicit `cy.screenshot()` at the needed point if you want to capture manually.

Can Nodemailer show the image inside the email body?

Yes. Attach it with a unique `cid` and refer to that ID in the HTML body using a `cid:` image source.

Quick Recap

SaleBestseller No. 1
Logitech Brio 101 Full HD 1080p Webcam for Streaming and Meetings - Black
Logitech Brio 101 Full HD 1080p Webcam for Streaming and Meetings - Black
Compatible with Nintendo Switch 2’s new GameChat mode; Built-In Mic: The built-in microphone lets others hear you clearly during video calls
$35.90
SaleBestseller No. 2
Logitech C270 720p Webcam Plug-and-Play Wide Screen Video Calling - Black
Logitech C270 720p Webcam Plug-and-Play Wide Screen Video Calling - Black
Compatible with Nintendo Switch 2’s new GameChat mode
$16.89
Bestseller No. 5
Logitech C920x HD Pro PC Webcam Full 1080p/30fps Video - Black
Logitech C920x HD Pro PC Webcam Full 1080p/30fps Video - Black
Compatible with Nintendo Switch 2’s new GameChat mode; Fully compatible with Windows 11
$69.99

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.