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
4 changes: 2 additions & 2 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Set up JDK 1.8
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 1.8
java-version: 11

- name: Grant execute permission for gradlew
run: chmod +x gradlew
Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ buildscript {
}

dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
testImplementation group: 'org.testng', name: 'testng', version: '7.3.0'
Expand Down
2 changes: 2 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
jdk:
- openjdk11
Original file line number Diff line number Diff line change
@@ -1,68 +1,55 @@
package io.visual_regression_tracker.sdk_java;

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import com.google.gson.Gson;
import io.visual_regression_tracker.sdk_java.request.BuildRequest;
import io.visual_regression_tracker.sdk_java.request.TestRunRequest;
import io.visual_regression_tracker.sdk_java.response.BuildResponse;
import io.visual_regression_tracker.sdk_java.response.TestRunResponse;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

enum METHOD {
GET,
POST,
PATCH
}

@Slf4j
public class VisualRegressionTracker {

private static final String TRACKER_NOT_STARTED = "Visual Regression Tracker has not been started";
protected static final String API_KEY_HEADER = "apiKey";
protected static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
protected Gson gson;
protected VisualRegressionTrackerConfig configuration;
protected PathProvider paths;
protected String buildId;
protected String projectId;
protected OkHttpClient client;

public VisualRegressionTracker(VisualRegressionTrackerConfig trackerConfig) {
configuration = trackerConfig;
paths = new PathProvider(trackerConfig.getApiUrl());
client = new OkHttpClient.Builder()
.connectTimeout(configuration.getHttpTimeoutInSeconds(), TimeUnit.SECONDS)
.readTimeout(configuration.getHttpTimeoutInSeconds(), TimeUnit.SECONDS)
.writeTimeout(configuration.getHttpTimeoutInSeconds(), TimeUnit.SECONDS)
.build();
gson = new Gson();
}

public BuildResponse start() throws IOException {
public BuildResponse start() throws IOException, InterruptedException {
String projectName = configuration.getProject();
String branch = configuration.getBranchName();
String ciBuildId = configuration.getCiBuildId();

BuildRequest newBuild = BuildRequest.builder()
.branchName(branch)
.project(projectName)
.ciBuildId(ciBuildId)
.build();

RequestBody body = RequestBody.create(JSON, gson.toJson(newBuild));

Request request = new Request.Builder()
.url(paths.getBuildPath())
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.post(body)
.build();

.branchName(branch)
.project(projectName)
.ciBuildId(ciBuildId)
.build();
log.info("Starting Visual Regression Tracker for project <{}> and branch <{}>", projectName, branch);

Response response = client.newCall(request).execute();

HttpRequest.BodyPublisher body = HttpRequest.BodyPublishers.ofString(gson.toJson(newBuild));
HttpResponse<String> response = getResponse(METHOD.POST, paths.getBuildPath(), body);
BuildResponse buildResponse = handleResponse(response, BuildResponse.class);

buildId = buildResponse.getId();
Expand All @@ -73,27 +60,22 @@ public BuildResponse start() throws IOException {
return buildResponse;
}

public void stop() throws IOException {
public void stop() throws IOException, InterruptedException {
if (!isStarted()) {
throw new TestRunException(TRACKER_NOT_STARTED);
}

Request request = new Request.Builder()
.url(paths.getBuildPathForBuild(buildId))
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.patch(RequestBody.create(JSON, ""))
.build();

log.info("Stopping Visual Regression Tracker for buildId <{}>", buildId);

Response response = client.newCall(request).execute();
HttpRequest.BodyPublisher body = HttpRequest.BodyPublishers.ofString("");
HttpResponse<String> response = getResponse(METHOD.PATCH, paths.getBuildPathForBuild(buildId), body);
handleResponse(response, Object.class);

log.info("Visual Regression Tracker is stopped for buildId <{}>", buildId);
}

public TestRunResult track(String name, String imageBase64, TestRunOptions testRunOptions)
throws IOException {
throws IOException, InterruptedException {
log.info("Tracking test run <{}> with options <{}> for buildId <{}>", name, testRunOptions, buildId);
TestRunResponse testResultDTO = submitTestRun(name, imageBase64, testRunOptions);

Expand Down Expand Up @@ -121,7 +103,7 @@ public TestRunResult track(String name, String imageBase64, TestRunOptions testR
return new TestRunResult(testResultDTO, this.paths);
}

public TestRunResult track(String name, String imageBase64) throws IOException {
public TestRunResult track(String name, String imageBase64) throws IOException, InterruptedException {
return track(name, imageBase64, TestRunOptions.builder().build());
}

Expand All @@ -130,46 +112,61 @@ protected boolean isStarted() {
}

protected TestRunResponse submitTestRun(String name, String imageBase64,
TestRunOptions testRunOptions) throws IOException {
TestRunOptions testRunOptions) throws IOException, InterruptedException {
if (!isStarted()) {
throw new TestRunException(TRACKER_NOT_STARTED);
}

TestRunRequest newTestRun = TestRunRequest.builder()
.projectId(projectId)
.buildId(buildId)
.branchName(configuration.getBranchName())
.name(name)
.imageBase64(imageBase64)
.os(testRunOptions.getOs())
.browser(testRunOptions.getBrowser())
.viewport(testRunOptions.getViewport())
.device(testRunOptions.getDevice())
.diffTollerancePercent(testRunOptions.getDiffTollerancePercent())
.ignoreAreas(testRunOptions.getIgnoreAreas())
.build();

RequestBody body = RequestBody.create(JSON, gson.toJson(newTestRun));

Request request = new Request.Builder()
.url(paths.getTestRunPath())
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.post(body)
.build();

Response response = client.newCall(request).execute();
.projectId(projectId)
.buildId(buildId)
.branchName(configuration.getBranchName())
.name(name)
.imageBase64(imageBase64)
.os(testRunOptions.getOs())
.browser(testRunOptions.getBrowser())
.viewport(testRunOptions.getViewport())
.device(testRunOptions.getDevice())
.diffTollerancePercent(testRunOptions.getDiffTollerancePercent())
.ignoreAreas(testRunOptions.getIgnoreAreas())
.build();

HttpRequest.BodyPublisher body = HttpRequest.BodyPublishers.ofString(gson.toJson(newTestRun));
HttpResponse<String> response = getResponse(METHOD.POST, paths.getTestRunPath(), body);
return handleResponse(response, TestRunResponse.class);
}

protected <T> T handleResponse(Response response, Class<T> classOfT) throws IOException {
String responseBody = Optional.ofNullable(response.body())
.orElseThrow(() -> new TestRunException("Cannot get response body"))
.string();
private HttpResponse<String> getResponse(METHOD method, String url, HttpRequest.BodyPublisher body) throws IOException, InterruptedException {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(configuration.getHttpTimeoutInSeconds()))
.header(API_KEY_HEADER, configuration.getApiKey())
.header("Content-Type", "application/json;charset=UTF-8")
.uri(URI.create(url));
HttpRequest request = getRequest(method, body, requestBuilder);
HttpResponse<String> response = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(configuration.getHttpTimeoutInSeconds()))
.build()
.send(request, HttpResponse.BodyHandlers.ofString());
return response;
}

protected HttpRequest getRequest(METHOD method, HttpRequest.BodyPublisher body, HttpRequest.Builder requestBuilder) {
switch (method) {
case PATCH:
return requestBuilder.method("PATCH", body).build();
case POST:
return requestBuilder.POST(body).build();
default:
throw new UnsupportedOperationException("This method is not yet supported.");
}
}

if (!response.isSuccessful()) {
protected <T> T handleResponse(HttpResponse<String> response, Class<T> classOfT) {
String responseBody = response.body();
if (!String.valueOf(response.statusCode()).startsWith("2")) {
throw new TestRunException(responseBody);
}

return gson.fromJson(responseBody, classOfT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package io.visual_regression_tracker.sdk_java;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

public class MockHttpClient extends HttpClient {

private final String body;
private int statusCode;

public MockHttpClient(int statusCode, String body) {
this.statusCode = statusCode;
this.body = body;
}

@Override
public Optional<CookieHandler> cookieHandler() {
return null;
}

@Override
public Optional<Duration> connectTimeout() {
return null;
}

@Override
public Redirect followRedirects() {
return null;
}

@Override
public Optional<ProxySelector> proxy() {
return null;
}

@Override
public SSLContext sslContext() {
return null;
}

@Override
public SSLParameters sslParameters() {
return null;
}

@Override
public Optional<Authenticator> authenticator() {
return null;
}

@Override
public Version version() {
return null;
}

@Override
public Optional<Executor> executor() {
return null;
}

@Override
public <T> HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
HttpResponse httpResponse = new HttpResponse() {
@Override
public int statusCode() {
return statusCode;
}

@Override
public HttpRequest request() {
return null;
}

@Override
public Optional<HttpResponse> previousResponse() {
return Optional.empty();
}

@Override
public HttpHeaders headers() {
return null;
}

@Override
public Object body() {
return body;
}

@Override
public Optional<SSLSession> sslSession() {
return Optional.empty();
}

@Override
public URI uri() {
return null;
}

@Override
public Version version() {
return null;
}
};
return httpResponse;
}

@Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
return null;
}

@Override
public <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest x, HttpResponse.BodyHandler<T> y, HttpResponse.PushPromiseHandler<T> z) {
return null;
}
}
Loading