Skip to content

Commit

Permalink
Merge pull request #2167 from jkeys089/image-comparison-example
Browse files Browse the repository at this point in the history
add image comparison example
  • Loading branch information
ptrthomas authored Nov 2, 2022
2 parents 6875b25 + 055c353 commit 9f41e69
Show file tree
Hide file tree
Showing 15 changed files with 379 additions and 0 deletions.
33 changes: 33 additions & 0 deletions examples/image-comparison/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Karate Image Comparison
This project is designed to demonstrate basic usage of the [Image Comparison](https://github.com/karatelabs/karate/#compare-image) feature.

## Overview
The [Image Comparison](https://github.com/karatelabs/karate/#compare-image) feature was introduced in [Karate 1.3.0](https://github.com/karatelabs/karate/wiki/1.3.0-Upgrade-Guide).
As a new feature with a number of options and a new UI component we wanted to provide a simple introduction to help users get started.

The included features are numbered 1 through 5 and build on each other.
They are intended to demonstrate how you might start from scratch without any baseline images on a new project:
* `1_establish_baseline.feature` establishes baseline images to use in future test runs
* `2_compare_baseline.feature` compares dynamic screenshots against our baseline images
* `3_custom_rebase.feature` demonstrates the use of the `onShowRebase` handler to customize the filename when rebasing
* `4_generic_rebase.feature` shows a slightly more advanced use of the `onShowRebase` handler that incorporates image comparison configuration options
* `5_custom_config.feature` shows the final scenario that is similar to what you might use in real tests

There is also a [screencast](https://www.youtube.com/watch?v=NIP3-njBR-Q) that demonstrates basic usage of the diff UI in the Karate HTML report.

## Running
The `5_custom_config.feature` is a complete [Karate UI test](https://github.com/karatelabs/karate/tree/master/karate-core) that can be executed by running `ImageComparisonRunner` as a JUnit test.
You will be able to open the HTML report (the file-name will appear at the end of the console log) and refresh it when re-running the test.

To manually run the test execute the following commands:
* Install maven artifacts from the latest [develop](https://github.com/karatelabs/karate/tree/develop) branch locally
```
mvn clean install -P pre-release
```
* Run the test from the `examples/image-comparison` directory
```
mvn clean test -Dtest=ImageComparisonRunner
```

## Debugging
You should be able to use the [Karate extension for Visual Studio Code](https://github.com/karatelabs/karate/wiki/IDE-Support#vs-code-karate-plugin) for stepping-through a test for troubleshooting.
55 changes: 55 additions & 0 deletions examples/image-comparison/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.intuit.karate.examples</groupId>
<artifactId>image-comparison-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.version>3.8.1</maven.compiler.version>
<karate.version>1.3.0-SNAPSHOT</karate.version>
</properties>

<dependencies>
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit5</artifactId>
<version>${karate.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<encoding>UTF-8</encoding>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Werror</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>

</project>
25 changes: 25 additions & 0 deletions examples/image-comparison/src/test/java/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/karate.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<logger name="com.intuit.karate" level="DEBUG"/>
<logger name="ui" level="DEBUG"/>

<root level="warn">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>

</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Image comparison demo

Background:
* configure driver = { type: 'chrome', screenshotOnFailure: false }
* driver karate.properties['web.url.base']
* driver.emulateDevice(375, 667, 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1')

Scenario: Landing page
* configure imageComparison = { mismatchShouldPass: true }

* def loadingScreenshot = screenshot()
* compareImage { latest: #(loadingScreenshot) }

* waitFor('.welcome')
* def loadedScreenshot = screenshot()
* compareImage { latest: #(loadedScreenshot) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Image comparison demo

Background:
* configure driver = { type: 'chrome', screenshotOnFailure: false }
* driver karate.properties['web.url.base']
* driver.emulateDevice(375, 667, 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1')

Scenario: Landing page
* configure imageComparison = { mismatchShouldPass: true }

* def loadingScreenshot = screenshot()
* compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadingScreenshot) }

* waitFor('.welcome')
* def loadedScreenshot = screenshot()
* compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadedScreenshot) }
16 changes: 16 additions & 0 deletions examples/image-comparison/src/test/java/ui/3_custom_rebase.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Image comparison demo

Background:
* configure driver = { type: 'chrome', screenshotOnFailure: false }
* driver karate.properties['web.url.base']
* driver.emulateDevice(375, 667, 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1')

Scenario: Landing page
* def loadingScreenshot = screenshot()
* configure imageComparison = { onShowRebase: "(cfg, saveAs) => saveAs('loading.png')", mismatchShouldPass: true }
* compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadingScreenshot) }

* waitFor('.welcome')
* def loadedScreenshot = screenshot()
* configure imageComparison = { onShowRebase: "(cfg, saveAs) => saveAs('loaded.png')", mismatchShouldPass: true }
* compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadedScreenshot) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Feature: Image comparison demo

Background:
* configure imageComparison = { onShowRebase: '(cfg, saveAs) => saveAs(cfg.name)', mismatchShouldPass: true }

* configure driver = { type: 'chrome', screenshotOnFailure: false }
* driver karate.properties['web.url.base']
* driver.emulateDevice(375, 667, 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1')

Scenario: Landing page
* def loadingScreenshot = screenshot()
* def loadingOpts =
"""
{
name: 'loading.png'
}
"""
* compareImage { baseline: 'this:screenshots/loading.png', latest: #(loadingScreenshot), options: #(loadingOpts) }

* waitFor('.welcome')

* def loadedScreenshot = screenshot()
* def loadedOpts =
"""
{
name: 'loaded.png'
}
"""
* compareImage { baseline: 'this:screenshots/loaded.png', latest: #(loadedScreenshot), options: #(loadedOpts) }
41 changes: 41 additions & 0 deletions examples/image-comparison/src/test/java/ui/5_custom_config.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Feature: Image comparison demo

Background:
* configure imageComparison = { onShowRebase: '(cfg, saveAs) => saveAs(cfg.name)' }

* configure driver = { type: 'chrome', screenshotOnFailure: false }
* driver karate.properties['web.url.base']
* driver.emulateDevice(375, 667, 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1')

Scenario: Landing page
* def loadingScreenshot = screenshot()
* def loadingOpts =
"""
{
name: 'loading.png',
"ignoredBoxes": [{
"top": 278,
"left": 131,
"bottom": 391,
"right": 246
}]
}
"""
* compareImage { baseline: 'this:screenshots/loading.png', latest: #(loadingScreenshot), options: #(loadingOpts) }

* waitFor('.welcome')

* def loadedScreenshot = screenshot()
* def loadedOpts =
"""
{
name: 'loaded.png',
"ignoredBoxes": [{
"top": 73,
"left": 17,
"bottom": 125,
"right": 188
}]
}
"""
* compareImage { baseline: 'this:screenshots/loaded.png', latest: #(loadedScreenshot), options: #(loadedOpts) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ui;

import com.intuit.karate.http.HttpServer;
import com.intuit.karate.junit5.Karate;
import org.junit.jupiter.api.BeforeAll;

class ImageComparisonRunner {

@BeforeAll
public static void beforeAll() {
HttpServer server = MockRunner.start(0);
System.setProperty("web.url.base", "http://localhost:" + server.getPort());
}

@Karate.Test
Karate testUi() {
return Karate.run("classpath:ui/5_custom_config.feature");
}

}
24 changes: 24 additions & 0 deletions examples/image-comparison/src/test/java/ui/MockRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ui;

import com.intuit.karate.http.HttpServer;
import com.intuit.karate.http.ServerConfig;
import org.junit.jupiter.api.Test;

/**
* run this as a junit test to start an http server at port 8080 the html page
* can be viewed at http://localhost:8080/ kill / stop this process when done
*/
class MockRunner {

@Test
public void testStart() {
start(8080).waitSync();
}

public static HttpServer start(int port) {
ServerConfig config = new ServerConfig("src/test/java/ui/html")
.autoCreateSession(true);
return HttpServer.config(config).http(port).build();
}

}
104 changes: 104 additions & 0 deletions examples/image-comparison/src/test/java/ui/html/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Karate Image Comparison</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400&display=swap" rel="stylesheet">
<style>
html, body {
font-family: 'Roboto Mono', monospace;
text-shadow: 0 0 2px #858585;
background-color: black;
color: #f9f9f9;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
article {
flex-direction: column;
align-items: stretch;
min-height: 100%;
display: flex;
}
main {
align-items: center;
text-align: center;
display: flex;
flex-grow: 1;
}
header, main, footer {
flex-shrink: 0;
}
header {
align-items: center;
flex-wrap: wrap;
display: flex;
}
header span {
text-shadow: 0 0 2px #858585;
font-size: 35px;
opacity: 0.95;
}
footer {
display: flex;
}
footer span {
opacity: 0.95;
margin: 20px;
}
a, a:visited {
color: #f9f9f9;
}
h2 {
font-size: 40px;
opacity: 0.95;
flex-grow: 1;
}
@media (max-width:768px) {
header span {
margin: -40px 0 20px 20px;
font-size: 23px;
}
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.1/spin.min.css" integrity="sha512-ssYEuK9Epo/48VIlBWTFosf1izrgGZqEMELJP+L7Clh0nvaOSTg87dM+Z8L+KKjrPdMbMvKYOOnzBOkNMhWFsg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script type="module">
import {Spinner} from 'https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.0/spin.min.js';
const loadingEl = document.getElementsByTagName('main')[0]
new Spinner({
lines: 13,
length: 38,
width: 17,
radius: 45,
scale: 0.5,
corners: 1,
speed: 1 + (Math.random() * 2),
rotate: 0,
animation: 'spinner-line-fade-quick',
direction: 1,
color: '#ffffff',
fadeColor: 'transparent',
top: '50%',
left: '50%',
shadow: '0 0 1px transparent',
zIndex: 2000000000,
className: 'spinner',
position: 'absolute',
}).spin(loadingEl);
setTimeout(() => loadingEl.innerHTML = '<h2 class="welcome">Welcome!</h2>', 1000 + (Math.random()*1000))
</script>
</head>
<body>
<article>
<header>
<img src="logo.gif" alt="Karate" id="logo">
<span>Image Comparison Demo</span>
</header>
<main></main>
<footer>
<span>&copy; <a href="https://github.com/karatelabs/karate">Karate 2022</a></span>
</footer>
</article>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9f41e69

Please sign in to comment.