Skip to main content
Automation - Granular Videos for Desktop

Enable granular videos for selenium

Doris Sooläte avatar
Written by Doris Sooläte
Updated over a year ago

Enable Granular Videos for Desktop [Java - Junit]

In order to enable granular videos for selenium, we are using a selenium driver to take screenshots after regular intervals. In the last using the FFMPEG library, we'll create a video from these screenshots.

  • Add dependency to pom.xml

  • Add VideoRecording class to the framework

  • Add startVideoRecording and stopVideoRecording in Before and After method

Please follow the following steps:

Add dependency to pom.xml

This dependency is used to call the command line execution from Java.

<dependency>
<groupId>org.buildobjects</groupId>
<artifactId>jproc</artifactId>
<version>2.8.2</version>
</dependency>

Add VideoRecording class to the framework

This is a utility class, which does all the dirty work. This class exposes two methods startVideoRecording and stopVideoRecording. This class takes screenshots after configurable intervals, and at the end, it creates a video using compiling all the screenshots.

package com.testlio.lib.utility.video;

import io.qameta.allure.Allure;
import org.apache.commons.io.FileUtils;
import org.buildobjects.process.ProcBuilder;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.testlio.lib.properties.Execution.properties;

public class VideoRecording {
private static AtomicInteger number = new AtomicInteger();
private static ScheduledExecutorService executor;

//Time it takes new screenshot
private static int SCREEN_SHOT_DELAY_MS = 700;

private static Path tempFilePath;

private static Runnable createScreenshot(final String testName, WebDriver driver) throws IOException {

System.out.println("Creating Directory if not exists");
tempFilePath = Files.createTempDirectory(testName);

return () -> {
if (driver != null) {
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
File dest = new File(tempFilePath.toAbsolutePath() + "/" + number + ".png");
number.getAndIncrement();
try {
FileUtils.copyFile(screenshot, dest);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
};
}


public static void startVideoRecording(String testName, WebDriver driver) throws IOException {
// Make sure we are in Selenium/Desktop
if (properties().isWebExecution()) {
number.set(0);
executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(createScreenshot(testName, driver), 0, SCREEN_SHOT_DELAY_MS, TimeUnit.MILLISECONDS);
}
}

public static void stopVideoRecording(String testName) {
try {
if (!properties().isWebExecution() || executor == null) {
return;
}

executor.shutdownNow();
Path tempDirectory = Files.createTempDirectory(testName);
String videoFileName = tempDirectory.toAbsolutePath() + "/" + testName + "-video.mp4";

ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();

// FFMPEG library command
new ProcBuilder("ffmpeg")
.withArgs("-y", "-i", tempFilePath.toAbsolutePath() + "/" + "%d.png", "-c:v", "libx264", "-r", "5", "-vf", "setpts=10*PTS", videoFileName)
.withOutputStream(output)
.withErrorStream(errorOutput)
.withTimeoutMillis(20000)
.run();

System.out.println(output);
System.out.println(errorOutput);

File initialFile = new File(videoFileName);
if (initialFile.exists()) {
System.out.println("File exists, attaching as allure report");
InputStream targetStream = Files.newInputStream(initialFile.toPath());
Allure.getLifecycle().addAttachment(testName, "video/mp4", "mp4", targetStream);
}
}
catch (Exception e) {
System.out.println(e);
}
}
}

Add startVideoRecording and stopVideoRecording in Before and After method

We need to start video recording before each method.

@Rule
public TestName name = new TestName();

@Before
public void setUp() throws IOException {
driverProvider = getDriverProvider();
// Check if Desktop/Selenium driver then start videoRecording
if (properties().isWebExecution()) {
// create web driver and pass it to startVideoRecording method
webDriver = driverProvider.createDriver();
startVideoRecording(testName.getMethodName(), getWebDriver());
}
}

@After
public void after(){
// Check if Desktop/Selenium driver then stop videoRecording
if (properties().isWebExecution()) {
stopVideoRecording(testName.getMethodName());
}
}

Enable Granular Videos for Desktop [Java - TestNG]

In order to enable granular videos for selenium, we are using a selenium driver to take screenshots after regular intervals. In the last using the FFMPEG library, we'll create a video from these screenshots.

  • Add dependency to pom.xml

  • Add VideoRecording class to the framework

  • Add ListenersTestNG class to framework

  • Add Listeners to BaseTest class

  • Add startVideoRecording in BeforeMethod method

Please follow the following steps:

Add dependency to pom.xml

This dependency is used to call the command line execution from Java.

<dependency>
<groupId>org.buildobjects</groupId>
<artifactId>jproc</artifactId>
<version>2.8.2</version>
</dependency>

Add VideoRecording class to the framework

This is a utility class, which does all the dirty work. This class exposes two methods startVideoRecording and stopVideoRecording. This class takes screenshots after configurable intervals, and at the end, it creates a video using compiling all the screenshots.

package com.testlio.lib.utility.video;

import io.qameta.allure.Allure;
import org.apache.commons.io.FileUtils;
import org.buildobjects.process.ProcBuilder;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.testlio.lib.properties.Execution.properties;

public class VideoRecording {
private static AtomicInteger number = new AtomicInteger();
private static ScheduledExecutorService executor;

//Time it takes new screenshot
private static int SCREEN_SHOT_DELAY_MS = 700;

private static Path tempFilePath;

private static Runnable createScreenshot(final String testName, WebDriver driver) throws IOException {

System.out.println("Creating Directory if not exists");
tempFilePath = Files.createTempDirectory(testName);

return () -> {
if (driver != null) {
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
File dest = new File(tempFilePath.toAbsolutePath() + "/" + number + ".png");
number.getAndIncrement();
try {
FileUtils.copyFile(screenshot, dest);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
};
}


public static void startVideoRecording(String testName, WebDriver driver) throws IOException {
// Make sure we are in Selenium/Desktop
if (properties().isWebExecution()) {
number.set(0);
executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(createScreenshot(testName, driver), 0, SCREEN_SHOT_DELAY_MS, TimeUnit.MILLISECONDS);
}
}

public static void stopVideoRecording(String testName) {
try {
if (!properties().isWebExecution() || executor == null) {
return;
}

executor.shutdownNow();
Path tempDirectory = Files.createTempDirectory(testName);
String videoFileName = tempDirectory.toAbsolutePath() + "/" + testName + "-video.mp4";

ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();

// FFMPEG library command
new ProcBuilder("ffmpeg")
.withArgs("-y", "-i", tempFilePath.toAbsolutePath() + "/" + "%d.png", "-c:v", "libx264", "-r", "5", "-vf", "setpts=10*PTS", videoFileName)
.withOutputStream(output)
.withErrorStream(errorOutput)
.withTimeoutMillis(20000)
.run();

System.out.println(output);
System.out.println(errorOutput);

File initialFile = new File(videoFileName);
if (initialFile.exists()) {
System.out.println("File exists, attaching as allure report");
InputStream targetStream = Files.newInputStream(initialFile.toPath());
Allure.getLifecycle().addAttachment(testName, "video/mp4", "mp4", targetStream);
}
}
catch (Exception e) {
System.out.println(e);
}
}
}

Add ListenersTestNG class to framework

afterInvocation method will be invoked after each test (success or failure)

package com.testlio.lib.utility.video;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;

import static com.testlio.lib.utility.video.VideoRecording.stopVideoRecording;

public class ListenersTestNG implements IInvokedMethodListener {
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
if (method.isTestMethod()) {
stopVideoRecording(method.getTestMethod().getMethodName());
}
}
}

Add Listeners to BaseTest class

@Listeners(value= ListenersTestNG.class)
public abstract class BaseTest {

Add startVideoRecording in BeforeMethod method

We need to start video recording before each method.

	@BeforeMethod(alwaysRun = true)
public void setUp(ITestContext context, Method method) throws IOException {
driverProvider = getDriverProvider();
// Check if Desktop/Selenium driver then start videoRecording
if (properties().isWebExecution()) {
webDriver = driverProvider.createDriver();
startVideoRecording(method.getName(), getWebDriver());
}
}

Did this answer your question?