Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pbassiner/d9c43e8279865dbc066a620e88560d8d to your computer and use it in GitHub Desktop.
Save pbassiner/d9c43e8279865dbc066a620e88560d8d to your computer and use it in GitHub Desktop.
Blog Post - Recording UI Tests Using Scalatest, Selenium and Akka: code snippets
//
// GifSequenceWriter.java
//
// Created by Elliot Kroo on 2009-04-25.
//
// This work is licensed under the Creative Commons Attribution 3.0 Unported
// License. To view a copy of this license, visit
// http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
// Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
/**
* Creates a new GifSequenceWriter
*
* @param outputStream the ImageOutputStream to be written to
* @param imageType one of the imageTypes specified in BufferedImage
* @param timeBetweenFramesMS the time between frames in miliseconds
* @param loopContinuously wether the gif should loop repeatedly
* @throws IIOException if no gif ImageWriters are found
*
* @author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier =
ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData =
gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode)
imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(
root,
"GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"FALSE");
graphicsControlExtensionNode.setAttribute(
"delayTime",
Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute(
"transparentColorIndex",
"0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
/* @pbassiner
* Changed original code introducing the conditional,
* otherwise it would loop twice when loopContinuously was false.
*/
if (loopContinuously) {
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(
new byte[] { 0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF) });
appEntensionsNode.appendChild(child);
}
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(
new IIOImage(
img,
null,
imageMetaData),
imageWriteParam);
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* @return a GIF ImageWriter object
* @throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if(!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child node (if
* the requested node does not exist).
*
* @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node.
* @param nodeName the name of the child node.
*
* @return the child node, if found or a new node created with the given name.
*/
private static IIOMetadataNode getNode(
IIOMetadataNode rootNode,
String nodeName) {
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
== 0) {
return((IIOMetadataNode) rootNode.item(i));
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return(node);
}
/**
public GifSequenceWriter(
BufferedOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) {
*/
public static void main(String[] args) throws Exception {
if (args.length > 1) {
// grab the output image type from the first image in the sequence
BufferedImage firstImage = ImageIO.read(new File(args[0]));
// create a new BufferedOutputStream with the last argument
ImageOutputStream output =
new FileImageOutputStream(new File(args[args.length - 1]));
// create a gif sequence with the type of the first image, 1 second
// between frames, which loops continuously
GifSequenceWriter writer =
new GifSequenceWriter(output, firstImage.getType(), 1, false);
// write out the first image to our sequence...
writer.writeToSequence(firstImage);
for(int i=1; i<args.length-1; i++) {
BufferedImage nextImage = ImageIO.read(new File(args[i]));
writer.writeToSequence(nextImage);
}
writer.close();
output.close();
} else {
System.out.println(
"Usage: java GifSequenceWriter [list of gif files] [output file]");
}
}
}
val (cancellable, future) =
Source
.tick(500 millis, 500 millis, NotUsed)
.map(_ => Try(webDriver.generateScreenshot(path)))
.collect { case Success(image) => new File(s"$path/${image}") }
.map(imageFile => {
writer.writeToSequence(ImageIO.read(imageFile))
imageFile.delete
})
.toMat(Sink.seq)(Keep.both)
.run
import java.awt.image.BufferedImage
import java.io.File
import java.util.UUID
import javax.imageio.ImageIO
import javax.imageio.stream.FileImageOutputStream
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{ Keep, Sink, Source }
import org.openqa.selenium.WebDriver
import org.scalatest.Outcome
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.{ Success, Try }
trait TestRecorder {
private[this] val recorderPath = "/recorder"
private[this] val imageType = BufferedImage.TYPE_4BYTE_ABGR
private[this] val frameDelayInMs = 250
def record(webDriver: WebDriver, basePath: String, alert: String => Unit)(test: => Outcome): Outcome = {
implicit val _system = ActorSystem()
implicit val _ec = _system.dispatcher
implicit val _materializer = ActorMaterializer()
val path = s"$basePath$recorderPath"
val outputFilename = s"$path/${UUID.randomUUID}.gif"
val outputFile = new File(outputFilename)
outputFile.getParentFile.mkdirs
val output = new FileImageOutputStream(outputFile)
val writer = new GifSequenceWriter(output, imageType, frameDelayInMs, false)
import WebDriverOps._
val (cancellable, future) =
Source
.tick(500 millis, 500 millis, NotUsed)
.map(_ => Try(webDriver.generateScreenshot(path)))
.collect { case Success(image) => new File(s"$path/${image}") }
.map(imageFile => {
writer.writeToSequence(ImageIO.read(imageFile))
imageFile.delete
})
.toMat(Sink.seq)(Keep.both)
.run
try test
finally {
cancellable.cancel
Await.ready(future, 30 seconds)
Await.ready(_system.terminate, 30 seconds)
writer.close
output.close
alert(s"Recording available at $outputFilename")
}
}
}
record(webDriver, "/tmp", println(_)) {
// Test code here
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment