Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: 2
jobs:
build:
docker:
- image: circleci/openjdk:8-node-browsers
steps:
- checkout
- run: npm install
- run: ./run-snapshots.sh
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"private": true,
"devDependencies": {
"@percy/agent": "^0.1.0"
"@percy/agent": "^0.1.0",
"har-validator": "^5.1.3"
}
}
30 changes: 27 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<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/maven-v4_0_0.xsd">
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.percy</groupId>
Expand Down Expand Up @@ -37,6 +38,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.3.1</junit.jupiter.version>
</properties>

<dependencies>
Expand All @@ -46,6 +48,19 @@
<artifactId>selenium-java</artifactId>
<version>3.141.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<distributionManagement>
Expand Down Expand Up @@ -95,7 +110,10 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>8</release>
<!-- As of Dec 2018, the 'release' flag doesn't work in the CircleCI environment.
This is likely because of the specific JSDK version used in the CI docker image. Use 'source' and 'target' instead. -->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
Expand Down Expand Up @@ -171,8 +189,14 @@
</executions>
</plugin>
<plugin>
<!-- JUnit 5 requires Surefire version 2.22.0 or higher -->
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<version>2.22.0</version>
<configuration>
<!-- Workaround for issue encountered in CI. -->
<!-- https://stackoverflow.com/questions/53010200/maven-surefire-could-not-find-forkedbooter-class-->
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions run-snapshots.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

set -o pipefail
set -e

# If we don't have a local Percy agent binary, run npm install.
if [ ! -f ./node_modules/.bin/percy ]; then
echo "*** Percy agent not installed. Installing now. ***"
npm install
fi

# Run the tests with snapshots.
./node_modules/.bin/percy exec -- mvn test
85 changes: 85 additions & 0 deletions src/test/java/io/percy/selenium/SdkTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.percy.selenium;

import java.io.IOException;
import java.util.Arrays;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class SdkTest {
private static final String TEST_URL = "http://localhost:8000";
private static WebDriver driver;
private static Percy percy;

@BeforeAll
public static void openServerAndBrowser() throws IOException {
TestServer.startServer();
ChromeOptions options = new ChromeOptions();
options.addArguments(
"--headless",
"--disable-web-security",
"--allow-running-insecure-content",
"--ignore-certificate-errors");
driver = new ChromeDriver(options);
percy = new Percy(driver);
}

@AfterAll
public static void closeServerAndBrowser() {
// Close our test browser.
driver.quit();
// Shutdown our server and make sure the threadpool also terminates.
TestServer.shutdown();
}

@AfterEach
public void clearLocalStorage() {
((JavascriptExecutor) driver).executeScript("window.localStorage.clear()");
}

@Test
public void takesLocalAppSnapshotWithProvidedName() {
driver.get(TEST_URL);
percy.snapshot("Snapshot with provided name");
}

@Test
public void takesLocalAppSnapshotWithProvidedNameAndWidths() {
driver.get(TEST_URL);
percy.snapshot("Snapshot with provided name and widths", Arrays.asList(768, 992, 1200));
}

@Test
public void takesLocalAppSnapshotWithProvidedNameAndMinHeight() {
driver.get(TEST_URL);
percy.snapshot("Snapshot with provided name and min height", null, 2000);
}

@Test
public void takesMultipleSnapshotsInOneTestCase() {
driver.get(TEST_URL);

WebElement newTodoEl = driver.findElement(By.className("new-todo"));
newTodoEl.sendKeys("A new todo to check off");
newTodoEl.sendKeys(Keys.RETURN);
percy.snapshot("Multiple snapshots in one test case -- #1", Arrays.asList(768, 992, 1200));

driver.findElement(By.cssSelector("input.toggle")).click();
percy.snapshot("Multiple snapshots in one test case -- #2", Arrays.asList(768, 992, 1200));
}

@Test
public void snapshotsLiveHTTPSSite() {
driver.get("https://www.google.com");
percy.snapshot("Live HTTPS site", Arrays.asList(768, 992, 1200));
}
}
98 changes: 98 additions & 0 deletions src/test/java/io/percy/selenium/TestServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.percy.selenium;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

/**
* HTTP server that serves the static files that make our test app.
*/
class TestServer {
// Server port.
private static final Integer PORT = 8000;

// Location for static files in our test app.
private static final String TESTAPP_DIR = "src/test/resources/testapp/";
private static final String INDEX_FILE = "index.html";

// Recognized Mime type map (extension -> mimetype)
private static final Map<String, String> MIME_MAP = new HashMap<String, String>();
static {
MIME_MAP.put("html", "text/html");
MIME_MAP.put("js", "application/javascript");
MIME_MAP.put("css", "text/css");
}

private static ExecutorService executor;
private static HttpServer server;

public static HttpServer startServer() throws IOException {
executor = Executors.newFixedThreadPool(1);
server = HttpServer.create(new InetSocketAddress(PORT), 0);
HttpContext context = server.createContext("/");
context.setHandler(TestServer::handleRequest);
server.setExecutor(executor);
server.start();
return server;
}

public static void shutdown() {
if (server != null) {
server.stop(1);
}
if (executor != null) {
executor.shutdownNow();
}
}

private static void handleRequest(HttpExchange exchange) throws IOException {
String requestedPath = exchange.getRequestURI().getPath();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow ok, I guess this is a thing! Thanks for making your way through this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is almost identical to what we have in the example app (just lightly simplified here). I could not find a convenient library to do programmatic simple static file serving over HTTP.

if (requestedPath.equals("/")) {
serveStaticFile(exchange, INDEX_FILE);
} else {
if (requestedPath.startsWith("/")) {
requestedPath = requestedPath.substring(1);
}
serveStaticFile(exchange, requestedPath);
}
}

private static void serveStaticFile(HttpExchange exchange, String resourcePath) throws IOException {
byte[] response;
int responseCode;
File file = new File(String.format("%s/%s", TESTAPP_DIR, resourcePath));
if (!file.canRead()) {
response = "404 - File Not Found".getBytes();
responseCode = 404;
} else {
InputStream in = new FileInputStream(file);
response = new byte[in.available()];
in.read(response);
responseCode = 200;
in.close();
}

exchange.getResponseHeaders().add("Content-Type", getMimeType(resourcePath));
exchange.sendResponseHeaders(responseCode, response.length);
OutputStream os = exchange.getResponseBody();
os.write(response);
os.close();
}

private static String getMimeType(String resourcePath) {
int lastDotIndex = resourcePath.lastIndexOf('.');
String extension = lastDotIndex > 0 ? resourcePath.substring(lastDotIndex + 1) : "";
return MIME_MAP.getOrDefault(extension, "text/plain");
}
}
11 changes: 11 additions & 0 deletions src/test/resources/testapp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
App for testing the SDK. Based on the
[TodoMVC](https://github.com/tastejs/todomvc)
[Vanilla-ES6](https://github.com/tastejs/todomvc/tree/master/examples/vanilla-es6)
app, forked at commit
[c78ae12a1834a11da6236c64a0c0fb06b20b7c51](https://github.com/tastejs/todomvc/tree/c78ae12a1834a11da6236c64a0c0fb06b20b7c51).

To see the app in action:

```bash
$ open index.html
```
Loading