Skip to main content
SeleniumDecoded

Screenshots and Videos

Capture screenshots and record videos during test execution for debugging and reporting.

Selenium 3 & 4 Stable

Screenshots and videos are invaluable for debugging test failures, documenting issues, and generating test reports.

Basic Screenshot

Take a Screenshot
Selenium 3 & 4 Stable
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import java.io.File;
import java.nio.file.Files;
// Take screenshot
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
// Save to file
Files.copy(screenshot.toPath(), new File("screenshot.png").toPath());
// Or get as bytes for embedding in reports
byte[] screenshotBytes = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
// Or get as Base64 string
String screenshotBase64 = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64);
# Take screenshot and save to file
driver.save_screenshot("screenshot.png")
# Or get as Base64 string
screenshot_base64 = driver.get_screenshot_as_base64()
# Or get as bytes
screenshot_bytes = driver.get_screenshot_as_png()
const fs = require('fs');
// Take screenshot as Base64
const screenshot = await driver.takeScreenshot();
// Save to file
fs.writeFileSync('screenshot.png', screenshot, 'base64');
// Or use directly as Base64 string for reports
using OpenQA.Selenium;
using System.IO;
// Take screenshot
Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot();
// Save to file
screenshot.SaveAsFile("screenshot.png");
// Or get as Base64 string
string screenshotBase64 = screenshot.AsBase64EncodedString;
// Or get as bytes
byte[] screenshotBytes = screenshot.AsByteArray;

Screenshot of Specific Element

Capture just a specific element instead of the full page:

Element Screenshot
Selenium 4 Stable
import org.openqa.selenium.WebElement;
// Find the element
WebElement element = driver.findElement(By.id("target-element"));
// Take screenshot of just this element
File elementScreenshot = element.getScreenshotAs(OutputType.FILE);
Files.copy(elementScreenshot.toPath(), new File("element.png").toPath());
# Find the element
element = driver.find_element(By.ID, "target-element")
# Take screenshot of just this element
element.screenshot("element.png")
// Find the element
const element = await driver.findElement(By.id('target-element'));
// Take screenshot of just this element (Selenium 4)
const elementScreenshot = await element.takeScreenshot();
fs.writeFileSync('element.png', elementScreenshot, 'base64');
// Find the element
IWebElement element = driver.FindElement(By.Id("target-element"));
// Take screenshot of just this element
Screenshot elementScreenshot = ((ITakesScreenshot)element).GetScreenshot();
elementScreenshot.SaveAsFile("element.png");

Full Page Screenshot

Capture the entire scrollable page, not just the viewport:

Full Page Screenshot (Firefox)
Selenium 4 Medium
import org.openqa.selenium.firefox.FirefoxDriver;
// Firefox supports full page screenshots natively
FirefoxDriver firefoxDriver = (FirefoxDriver) driver;
File fullPageScreenshot = firefoxDriver.getFullPageScreenshotAs(OutputType.FILE);
Files.copy(fullPageScreenshot.toPath(), new File("fullpage.png").toPath());
# Firefox supports full page screenshots natively
# driver must be Firefox
# Using Firefox's full page screenshot
full_screenshot = driver.get_full_page_screenshot_as_png()
with open("fullpage.png", "wb") as f:
f.write(full_screenshot)
// For Chrome, use CDP (Chrome DevTools Protocol)
// Firefox has native support
// Chrome full page via CDP
const cdp = await driver.createCDPConnection('page');
const screenshot = await driver.executeScript(`
return await new Promise((resolve) => {
chrome.runtime.sendMessage({action: 'captureFullPage'}, resolve);
});
`);
// Firefox supports full page screenshots
var firefoxDriver = (FirefoxDriver)driver;
Screenshot fullPage = firefoxDriver.GetFullPageScreenshot();
fullPage.SaveAsFile("fullpage.png");

Screenshot on Test Failure

Automatically capture screenshots when tests fail:

Screenshot on Failure (pytest)
Selenium 3 & 4 Stable
// TestNG listener for screenshot on failure
import org.testng.ITestListener;
import org.testng.ITestResult;
public class ScreenshotListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
WebDriver driver = ((BaseTest) result.getInstance()).getDriver();
if (driver != null) {
String testName = result.getName();
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String filename = "screenshots/" + testName + "_" + timestamp + ".png";
try {
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.createDirectories(Paths.get("screenshots"));
Files.copy(screenshot.toPath(), Paths.get(filename));
System.out.println("Screenshot saved: " + filename);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
# conftest.py - pytest fixture for screenshot on failure
import pytest
from datetime import datetime
import os
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
setattr(item, f"rep_{report.when}", report)
@pytest.fixture(autouse=True)
def screenshot_on_failure(request, driver):
yield
# Check if test failed
if hasattr(request.node, "rep_call") and request.node.rep_call.failed:
os.makedirs("screenshots", exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"screenshots/{request.node.name}_{timestamp}.png"
driver.save_screenshot(filename)
print(f"Screenshot saved: {filename}")
// Mocha afterEach hook for screenshot on failure
const fs = require('fs');
const path = require('path');
afterEach(async function() {
if (this.currentTest.state === 'failed') {
const screenshotDir = 'screenshots';
if (!fs.existsSync(screenshotDir)) {
fs.mkdirSync(screenshotDir);
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = path.join(
screenshotDir,
`${this.currentTest.title}_${timestamp}.png`
);
const screenshot = await driver.takeScreenshot();
fs.writeFileSync(filename, screenshot, 'base64');
console.log(`Screenshot saved: ${filename}`);
}
});
// NUnit screenshot on failure using TearDown
[TearDown]
public void TakeScreenshotOnFailure()
{
if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed)
{
string screenshotDir = "screenshots";
Directory.CreateDirectory(screenshotDir);
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string testName = TestContext.CurrentContext.Test.Name;
string filename = Path.Combine(screenshotDir, $"{testName}_{timestamp}.png");
Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile(filename);
TestContext.WriteLine($"Screenshot saved: {filename}");
}
}

Video Recording

Using Docker Selenium with Video

docker-compose.yml
version: '3.8'
services:
chrome:
image: selenium/standalone-chrome:latest
shm_size: 2gb
ports:
- "4444:4444"
- "7900:7900" # VNC viewer
video:
image: selenium/video:latest
depends_on:
- chrome
environment:
- DISPLAY_CONTAINER_NAME=chrome
- FILE_NAME=test_recording.mp4
volumes:
- ./videos:/videos

Using FFmpeg for Recording

Record with FFmpeg
Selenium 3 & 4 Medium
import java.io.IOException;
public class VideoRecorder {
private Process ffmpegProcess;
public void startRecording(String filename) throws IOException {
// Linux/Mac - record X11 display
String[] command = {
"ffmpeg", "-y",
"-video_size", "1920x1080",
"-framerate", "25",
"-f", "x11grab",
"-i", ":0.0",
filename
};
ffmpegProcess = Runtime.getRuntime().exec(command);
}
public void stopRecording() {
if (ffmpegProcess != null) {
ffmpegProcess.destroy();
}
}
}
import subprocess
import signal
class VideoRecorder:
def __init__(self):
self.process = None
def start_recording(self, filename):
# Linux - record X11 display
command = [
"ffmpeg", "-y",
"-video_size", "1920x1080",
"-framerate", "25",
"-f", "x11grab",
"-i", ":0.0",
filename
]
self.process = subprocess.Popen(command)
def stop_recording(self):
if self.process:
self.process.send_signal(signal.SIGINT)
self.process.wait()
# Usage
recorder = VideoRecorder()
recorder.start_recording("test_video.mp4")
# ... run tests ...
recorder.stop_recording()
const { spawn } = require('child_process');
class VideoRecorder {
constructor() {
this.process = null;
}
startRecording(filename) {
// Linux - record X11 display
this.process = spawn('ffmpeg', [
'-y',
'-video_size', '1920x1080',
'-framerate', '25',
'-f', 'x11grab',
'-i', ':0.0',
filename
]);
}
stopRecording() {
if (this.process) {
this.process.stdin.write('q');
this.process.kill('SIGINT');
}
}
}
// Usage
const recorder = new VideoRecorder();
recorder.startRecording('test_video.mp4');
// ... run tests ...
recorder.stopRecording();
using System.Diagnostics;
public class VideoRecorder
{
private Process ffmpegProcess;
public void StartRecording(string filename)
{
// Windows - use GDI grab
var startInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-y -f gdigrab -framerate 25 -i desktop {filename}",
UseShellExecute = false,
CreateNoWindow = true
};
ffmpegProcess = Process.Start(startInfo);
}
public void StopRecording()
{
ffmpegProcess?.Kill();
}
}

Screenshot Comparison

Compare screenshots to detect visual changes:

Basic Screenshot Comparison
Selenium 3 & 4 Stable
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
public class ScreenshotComparison {
public static double compareImages(File baseline, File current) throws IOException {
BufferedImage img1 = ImageIO.read(baseline);
BufferedImage img2 = ImageIO.read(current);
if (img1.getWidth() != img2.getWidth() || img1.getHeight() != img2.getHeight()) {
return 0.0; // Different dimensions
}
long diff = 0;
for (int y = 0; y < img1.getHeight(); y++) {
for (int x = 0; x < img1.getWidth(); x++) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
}
}
long maxDiff = 3L * 255 * img1.getWidth() * img1.getHeight();
return 100.0 * (1.0 - (double) diff / maxDiff);
}
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
}
from PIL import Image
import math
def compare_images(baseline_path, current_path):
"""Compare two images and return similarity percentage."""
img1 = Image.open(baseline_path)
img2 = Image.open(current_path)
if img1.size != img2.size:
return 0.0 # Different dimensions
pixels1 = list(img1.getdata())
pixels2 = list(img2.getdata())
diff = sum(
abs(p1[i] - p2[i])
for p1, p2 in zip(pixels1, pixels2)
for i in range(3) # RGB channels
)
max_diff = 3 * 255 * len(pixels1)
similarity = 100.0 * (1.0 - diff / max_diff)
return similarity
# Usage
similarity = compare_images("baseline.png", "current.png")
print(f"Similarity: {similarity:.2f}%")
assert similarity > 99.0, "Visual regression detected!"
const { PNG } = require('pngjs');
const fs = require('fs');
function compareImages(baselinePath, currentPath) {
const img1 = PNG.sync.read(fs.readFileSync(baselinePath));
const img2 = PNG.sync.read(fs.readFileSync(currentPath));
if (img1.width !== img2.width || img1.height !== img2.height) {
return 0.0; // Different dimensions
}
let diff = 0;
for (let i = 0; i < img1.data.length; i += 4) {
diff += Math.abs(img1.data[i] - img2.data[i]); // R
diff += Math.abs(img1.data[i+1] - img2.data[i+1]); // G
diff += Math.abs(img1.data[i+2] - img2.data[i+2]); // B
}
const maxDiff = 3 * 255 * (img1.data.length / 4);
return 100.0 * (1.0 - diff / maxDiff);
}
// Usage
const similarity = compareImages('baseline.png', 'current.png');
console.log(`Similarity: ${similarity.toFixed(2)}%`);
using System.Drawing;
public static class ScreenshotComparison
{
public static double CompareImages(string baselinePath, string currentPath)
{
using var img1 = new Bitmap(baselinePath);
using var img2 = new Bitmap(currentPath);
if (img1.Width != img2.Width || img1.Height != img2.Height)
return 0.0;
long diff = 0;
for (int y = 0; y < img1.Height; y++)
{
for (int x = 0; x < img1.Width; x++)
{
Color c1 = img1.GetPixel(x, y);
Color c2 = img2.GetPixel(x, y);
diff += Math.Abs(c1.R - c2.R);
diff += Math.Abs(c1.G - c2.G);
diff += Math.Abs(c1.B - c2.B);
}
}
long maxDiff = 3L * 255 * img1.Width * img1.Height;
return 100.0 * (1.0 - (double)diff / maxDiff);
}
}

Organized Screenshot Storage

Organized Screenshot Naming
Selenium 3 & 4 Stable
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ScreenshotManager {
private static final String BASE_DIR = "screenshots";
public static String captureScreenshot(WebDriver driver, String testName, String step) {
LocalDateTime now = LocalDateTime.now();
String date = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String time = now.format(DateTimeFormatter.ofPattern("HH-mm-ss"));
// Create organized directory structure
String dir = String.format("%s/%s/%s", BASE_DIR, date, testName);
Files.createDirectories(Paths.get(dir));
// Descriptive filename
String filename = String.format("%s/%s_%s.png", dir, time, step);
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(screenshot.toPath(), Paths.get(filename));
return filename;
}
}
// Usage
ScreenshotManager.captureScreenshot(driver, "LoginTest", "01_homepage_loaded");
ScreenshotManager.captureScreenshot(driver, "LoginTest", "02_credentials_entered");
ScreenshotManager.captureScreenshot(driver, "LoginTest", "03_login_successful");
from datetime import datetime
import os
class ScreenshotManager:
BASE_DIR = "screenshots"
@classmethod
def capture(cls, driver, test_name, step):
now = datetime.now()
date = now.strftime("%Y-%m-%d")
time = now.strftime("%H-%M-%S")
# Create organized directory structure
dir_path = f"{cls.BASE_DIR}/{date}/{test_name}"
os.makedirs(dir_path, exist_ok=True)
# Descriptive filename
filename = f"{dir_path}/{time}_{step}.png"
driver.save_screenshot(filename)
return filename
# Usage
ScreenshotManager.capture(driver, "LoginTest", "01_homepage_loaded")
ScreenshotManager.capture(driver, "LoginTest", "02_credentials_entered")
ScreenshotManager.capture(driver, "LoginTest", "03_login_successful")
const fs = require('fs');
const path = require('path');
class ScreenshotManager {
static BASE_DIR = 'screenshots';
static async capture(driver, testName, step) {
const now = new Date();
const date = now.toISOString().split('T')[0];
const time = now.toTimeString().split(' ')[0].replace(/:/g, '-');
// Create organized directory structure
const dirPath = path.join(this.BASE_DIR, date, testName);
fs.mkdirSync(dirPath, { recursive: true });
// Descriptive filename
const filename = path.join(dirPath, `${time}_${step}.png`);
const screenshot = await driver.takeScreenshot();
fs.writeFileSync(filename, screenshot, 'base64');
return filename;
}
}
// Usage
await ScreenshotManager.capture(driver, 'LoginTest', '01_homepage_loaded');
await ScreenshotManager.capture(driver, 'LoginTest', '02_credentials_entered');
await ScreenshotManager.capture(driver, 'LoginTest', '03_login_successful');
using System;
using System.IO;
public class ScreenshotManager
{
private const string BaseDir = "screenshots";
public static string Capture(IWebDriver driver, string testName, string step)
{
DateTime now = DateTime.Now;
string date = now.ToString("yyyy-MM-dd");
string time = now.ToString("HH-mm-ss");
// Create organized directory structure
string dirPath = Path.Combine(BaseDir, date, testName);
Directory.CreateDirectory(dirPath);
// Descriptive filename
string filename = Path.Combine(dirPath, $"{time}_{step}.png");
Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile(filename);
return filename;
}
}
// Usage
ScreenshotManager.Capture(driver, "LoginTest", "01_homepage_loaded");
ScreenshotManager.Capture(driver, "LoginTest", "02_credentials_entered");
ScreenshotManager.Capture(driver, "LoginTest", "03_login_successful");

Best Practices

PracticeDescription
Capture on failureAutomatic screenshots when tests fail
Organized namingDate/test/step structure for easy navigation
Element screenshotsFocus on relevant elements
Video for complex flowsRecord full sessions for debugging
Clean up old filesRemove screenshots older than X days
Attach to reportsEmbed in HTML test reports

Next Steps