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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.visual_regression_tracker.sdk_java;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class PathProvider {

private static final String BUILD_PATH = "/builds";
private static final String TEST_RUNS_PATH = "/test-runs";

private final String baseApiUrl;

public String getBuildPath() {
return baseApiUrl.concat(BUILD_PATH);
}

public String getBuildPathForBuild(String buildId) {
return getBuildPath().concat("/").concat(buildId);
}

public String getTestRunPath() {
return baseApiUrl.concat(TEST_RUNS_PATH);
}
}
Original file line number Diff line number Diff line change
@@ -1,80 +1,93 @@
package io.visual_regression_tracker.sdk_java;

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

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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


@Slf4j
public class VisualRegressionTracker {
protected static final String apiKeyHeaderName = "apiKey";

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");
private static final Logger LOGGER = LoggerFactory.getLogger(VisualRegressionTracker.class);
protected Gson gson;
protected VisualRegressionTrackerConfig visualRegressionTrackerConfig;
protected VisualRegressionTrackerConfig configuration;
protected PathProvider paths;
protected String buildId;
protected String projectId;
protected OkHttpClient client;

public VisualRegressionTracker(VisualRegressionTrackerConfig visualRegressionTrackerConfig) {
this.visualRegressionTrackerConfig = visualRegressionTrackerConfig;

this.client = new OkHttpClient();
this.gson = new Gson();
public VisualRegressionTracker(VisualRegressionTrackerConfig trackerConfig) {
configuration = trackerConfig;
paths = new PathProvider(trackerConfig.getApiUrl());
client = new OkHttpClient();
gson = new Gson();
}

public void start() throws IOException {
String projectName = configuration.getProject();
String branch = configuration.getBranchName();

BuildRequest newBuild = BuildRequest.builder()
.branchName(this.visualRegressionTrackerConfig.getBranchName())
.project(this.visualRegressionTrackerConfig.getProject())
.build();
.branchName(branch)
.project(projectName)
.build();

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

Request request = new Request.Builder()
.url(this.visualRegressionTrackerConfig.getApiUrl().concat("/builds"))
.addHeader(apiKeyHeaderName, this.visualRegressionTrackerConfig.getApiKey())
.post(body)
.build();
.url(paths.getBuildPath())
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.post(body)
.build();

try (Response response = client.newCall(request).execute()) {
log.info("Starting Visual Regression Tracker for project <{}> and branch <{}>", projectName, branch);

BuildResponse buildDTO = handleResponse(response, BuildResponse.class);
Response response = client.newCall(request).execute();

this.buildId = buildDTO.getId();
this.projectId = buildDTO.getProjectId();
}
BuildResponse buildResponse = handleResponse(response, BuildResponse.class);

buildId = buildResponse.getId();
projectId = buildResponse.getProjectId();

log.info("Visual Regression Tracker is started for project <{}>: buildId <{}>, projectId <{}>",
projectName, projectId, buildId);
}

public void stop() throws IOException {
if (!this.isStarted()) {
throw new TestRunException("Visual Regression Tracker has not been started");
if (!isStarted()) {
throw new TestRunException(TRACKER_NOT_STARTED);
}

Request request = new Request.Builder()
.url(this.visualRegressionTrackerConfig.getApiUrl().concat("/builds/").concat(this.buildId))
.addHeader(apiKeyHeaderName, this.visualRegressionTrackerConfig.getApiKey())
.patch(RequestBody.create(JSON, ""))
.build();
.url(paths.getBuildPathForBuild(buildId))
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.patch(RequestBody.create(JSON, ""))
.build();

try (Response response = client.newCall(request).execute()) {
handleResponse(response, Object.class);
}
log.info("Stopping Visual Regression Tracker for buildId <{}>", buildId);

Response response = client.newCall(request).execute();
handleResponse(response, Object.class);

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

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

String errorMessage;
switch (testResultDTO.getStatus()) {
Expand All @@ -90,57 +103,57 @@ public void track(String name, String imageBase64, TestRunOptions testRunOptions
}

if (!errorMessage.isEmpty()) {
if (this.visualRegressionTrackerConfig.getEnableSoftAssert()) {
LOGGER.error(errorMessage);
if (configuration.getEnableSoftAssert()) {
log.error(errorMessage);
} else {
throw new TestRunException(errorMessage);
}
}
}

public void track(String name, String imageBase64) throws IOException {
this.track(name, imageBase64, TestRunOptions.builder().build());
track(name, imageBase64, TestRunOptions.builder().build());
}

protected boolean isStarted() {
return this.buildId != null && this.projectId != null;
return buildId != null && projectId != null;
}

protected TestRunResponse submitTestRun(String name, String imageBase64, TestRunOptions testRunOptions) throws IOException {
if (!this.isStarted()) {
throw new TestRunException("Visual Regression Tracker has not been started");
protected TestRunResponse submitTestRun(String name, String imageBase64,
TestRunOptions testRunOptions) throws IOException {
if (!isStarted()) {
throw new TestRunException(TRACKER_NOT_STARTED);
}

TestRunRequest newTestRun = TestRunRequest.builder()
.projectId(this.projectId)
.buildId(this.buildId)
.branchName(this.visualRegressionTrackerConfig.getBranchName())
.name(name)
.imageBase64(imageBase64)
.os(testRunOptions.getOs())
.browser(testRunOptions.getBrowser())
.viewport(testRunOptions.getViewport())
.device(testRunOptions.getDevice())
.diffTollerancePercent(testRunOptions.getDiffTollerancePercent())
.build();
.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())
.build();

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

Request request = new Request.Builder()
.url(this.visualRegressionTrackerConfig.getApiUrl().concat("/test-runs"))
.addHeader(apiKeyHeaderName, this.visualRegressionTrackerConfig.getApiKey())
.post(body)
.build();
.url(paths.getTestRunPath())
.addHeader(API_KEY_HEADER, configuration.getApiKey())
.post(body)
.build();

try (Response response = client.newCall(request).execute()) {
return handleResponse(response, TestRunResponse.class);
}
Response response = client.newCall(request).execute();
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();
.orElseThrow(() -> new TestRunException("Cannot get response body"))
.string();

if (!response.isSuccessful()) {
throw new TestRunException(responseBody);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void shouldStartBuild() throws IOException, InterruptedException {
vrt.start();

RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getHeader(VisualRegressionTracker.apiKeyHeaderName), is(config.getApiKey()));
assertThat(recordedRequest.getHeader(VisualRegressionTracker.API_KEY_HEADER), is(config.getApiKey()));
assertThat(recordedRequest.getBody().readUtf8(), is(gson.toJson(buildRequest)));
assertThat(vrt.buildId, is(BUILD_ID));
assertThat(vrt.projectId, is(PROJECT_ID));
Expand All @@ -114,7 +114,7 @@ public void shouldStopBuild() throws IOException, InterruptedException {

RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod(), is("PATCH"));
assertThat(recordedRequest.getHeader(VisualRegressionTracker.apiKeyHeaderName), is(config.getApiKey()));
assertThat(recordedRequest.getHeader(VisualRegressionTracker.API_KEY_HEADER), is(config.getApiKey()));
assertThat(Objects.requireNonNull(recordedRequest.getRequestUrl()).encodedPath(), containsString(BUILD_ID));
}

Expand Down Expand Up @@ -155,7 +155,7 @@ public void shouldSubmitTestRun() throws IOException, InterruptedException {
TestRunResponse result = vrt.submitTestRun(NAME, IMAGE_BASE_64, testRunOptions);

RecordedRequest request = server.takeRequest();
assertThat(request.getHeader(VisualRegressionTracker.apiKeyHeaderName), is(config.getApiKey()));
assertThat(request.getHeader(VisualRegressionTracker.API_KEY_HEADER), is(config.getApiKey()));
assertThat(request.getBody().readUtf8(), is(gson.toJson(testRunRequest)));
assertThat(gson.toJson(result), is(gson.toJson(testRunResponse)));
}
Expand Down Expand Up @@ -193,7 +193,7 @@ public Object[][] trackErrorCases() {
expectedExceptions = TestRunException.class,
expectedExceptionsMessageRegExp = "^(Difference found: https://someurl.com/test/123123|No baseline: https://someurl.com/test/123123)$")
public void trackShouldThrowException(TestRunResponse testRunResponse, String expectedExceptionMessage) throws IOException {
vrtMocked.visualRegressionTrackerConfig = new VisualRegressionTrackerConfig("", "", "", "", false);
vrtMocked.configuration = new VisualRegressionTrackerConfig("", "", "", "", false);
when(vrtMocked.submitTestRun(anyString(), anyString(), any())).thenReturn(testRunResponse);

doCallRealMethod().when(vrtMocked).track(anyString(), anyString(), any());
Expand All @@ -203,7 +203,7 @@ public void trackShouldThrowException(TestRunResponse testRunResponse, String ex
@Test(dataProvider = "trackErrorCases")
public void trackShouldLogSevere(TestRunResponse testRunResponse, String expectedExceptionMessage) throws IOException {
Logger loggerMock = LoggerMock.getLoggerMock(VisualRegressionTracker.class);
vrtMocked.visualRegressionTrackerConfig = new VisualRegressionTrackerConfig("", "", "", "", true);
vrtMocked.configuration = new VisualRegressionTrackerConfig("", "", "", "", true);
when(vrtMocked.submitTestRun(anyString(), anyString(), any())).thenReturn(testRunResponse);

doCallRealMethod().when(vrtMocked).track(anyString(), anyString(), any());
Expand Down