Skip to content

Commit

Permalink
Java: Implement visual regions and refactor diffing options (#44)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Karran <benjamin.karran@saucelabs.com>
  • Loading branch information
ebekebe and ebekebe authored May 8, 2024
1 parent 917ec76 commit cae3407
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 149 deletions.
80 changes: 48 additions & 32 deletions visual-java/src/main/java/com/saucelabs/visual/CheckOptions.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.saucelabs.visual;

import com.saucelabs.visual.graphql.type.DiffingOptionsIn;
import com.saucelabs.visual.model.DiffingFlag;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
import com.saucelabs.visual.model.IgnoreRegion;
import com.saucelabs.visual.model.VisualRegion;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.openqa.selenium.WebElement;

Expand All @@ -19,50 +23,50 @@ public CheckOptions() {}
public CheckOptions(
List<WebElement> ignoreElements,
List<IgnoreRegion> ignoreRegions,
List<VisualRegion> regions,
String testName,
String suiteName,
DiffingMethod diffingMethod,
DiffingOptionsIn diffingOptions,
Boolean captureDom,
String clipSelector,
FullPageScreenshotConfig fullPageScreenshotConfig,
List<String> enableOnly,
List<String> disableOnly) {
FullPageScreenshotConfig fullPageScreenshotConfig) {
this.ignoreElements = ignoreElements;
this.ignoreRegions = ignoreRegions;
this.regions = regions;
this.testName = testName;
this.suiteName = suiteName;
this.diffingMethod = diffingMethod;
this.captureDom = captureDom;
this.clipSelector = clipSelector;
this.fullPageScreenshotConfig = fullPageScreenshotConfig;
this.enableOnly = enableOnly;
this.disableOnly = disableOnly;
this.diffingOptions = diffingOptions;
}

private List<WebElement> ignoreElements = new ArrayList<>();
private List<IgnoreRegion> ignoreRegions = new ArrayList<>();
private List<VisualRegion> regions = new ArrayList<>();

private String testName;
private String suiteName;
private DiffingMethod diffingMethod;
private DiffingOptionsIn diffingOptions;
private Boolean captureDom;
private String clipSelector;

private FullPageScreenshotConfig fullPageScreenshotConfig;
private List<String> enableOnly;
private List<String> disableOnly;

public static class Builder {
private List<WebElement> ignoreElements = new ArrayList<>();
private List<IgnoreRegion> ignoreRegions = new ArrayList<>();
private List<VisualRegion> regions = new ArrayList<>();
private String testName;
private String suiteName;
private DiffingMethod diffingMethod;
private DiffingOptionsIn diffingOptions;
private Boolean captureDom;
private String clipSelector;
private FullPageScreenshotConfig fullPageScreenshotConfig;
private List<String> enableOnly;
private List<String> disableOnly;

public Builder withIgnoreElements(List<WebElement> ignoreElements) {
this.ignoreElements = ignoreElements;
Expand Down Expand Up @@ -104,28 +108,44 @@ public Builder withFullPageConfig(FullPageScreenshotConfig fullPageScreenshotCon
return this;
}

public Builder enableOnly(List<String> enableOnly) {
this.enableOnly = enableOnly;
public Builder disableOnly(EnumSet<DiffingFlag> flags) {
this.diffingOptions = new DiffingOptionsIn();
DiffingFlag.setAll(this.diffingOptions, true);
for (DiffingFlag f : flags) f.apply(this.diffingOptions, false);
return this;
}

public Builder disableOnly(List<String> disableOnly) {
this.disableOnly = disableOnly;
public Builder enableOnly(EnumSet<DiffingFlag> flags) {
this.diffingOptions = new DiffingOptionsIn();
DiffingFlag.setAll(this.diffingOptions, false);
for (DiffingFlag f : flags) f.apply(this.diffingOptions, true);
return this;
}

public Builder enableOnly(EnumSet<DiffingFlag> flags, WebElement element) {

this.regions.add(VisualRegion.ignoreChangesFor(element).except(flags));
return this;
}

public Builder disableOnly(EnumSet<DiffingFlag> flags, WebElement element) {

this.regions.add(VisualRegion.detectChangesFor(element).except(flags));
return this;
}

public CheckOptions build() {
return new CheckOptions(
ignoreElements,
ignoreRegions,
regions,
testName,
suiteName,
diffingMethod,
diffingOptions,
captureDom,
clipSelector,
fullPageScreenshotConfig,
enableOnly,
disableOnly);
fullPageScreenshotConfig);
}
}

Expand Down Expand Up @@ -153,6 +173,14 @@ public void setTestName(String testName) {
this.testName = testName;
}

public List<VisualRegion> getRegions() {
return regions;
}

public void setRegions(List<VisualRegion> regions) {
this.regions = regions;
}

public String getSuiteName() {
return suiteName;
}
Expand All @@ -169,6 +197,10 @@ public DiffingMethod getDiffingMethod() {
return diffingMethod;
}

public DiffingOptionsIn getDiffingOptions() {
return diffingOptions;
}

public void setCaptureDom(Boolean captureDom) {
this.captureDom = captureDom;
}
Expand Down Expand Up @@ -196,20 +228,4 @@ public void enableFullPageScreenshots(FullPageScreenshotConfig fullPageScreensho
public void enableFullPageScreenshots() {
this.fullPageScreenshotConfig = new FullPageScreenshotConfig.Builder().build();
}

public void enableOnly(List<String> enableOnly) {
this.enableOnly = enableOnly;
}

public List<String> getEnableOnly() {
return enableOnly;
}

public void disableOnly(List<String> disableOnly) {
this.disableOnly = disableOnly;
}

public List<String> getDisableOnly() {
return disableOnly;
}
}
121 changes: 46 additions & 75 deletions visual-java/src/main/java/com/saucelabs/visual/VisualApi.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package com.saucelabs.visual;

import static com.saucelabs.visual.model.DiffingOption.*;
import static com.saucelabs.visual.utils.EnvironmentVariables.isNotBlank;
import static com.saucelabs.visual.utils.EnvironmentVariables.valueOrDefault;

import com.saucelabs.visual.exception.VisualApiException;
import com.saucelabs.visual.graphql.*;
import com.saucelabs.visual.graphql.type.*;
import com.saucelabs.visual.model.IgnoreRegion;
import com.saucelabs.visual.model.VisualRegion;
import com.saucelabs.visual.utils.ConsoleColors;
import com.saucelabs.visual.utils.EnvironmentVariables;
import dev.failsafe.Failsafe;
import dev.failsafe.RetryPolicy;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.slf4j.Logger;
Expand Down Expand Up @@ -330,6 +329,7 @@ public void sauceVisualCheck(String snapshotName, CheckOptions options) {
new CreateSnapshotFromWebDriverMutation.CreateSnapshotFromWebDriverIn(
this.build.getId(),
diffingMethod,
Optional.ofNullable(options.getDiffingOptions()),
extractIgnoreList(options),
this.jobId,
snapshotName,
Expand Down Expand Up @@ -360,12 +360,6 @@ public void sauceVisualCheck(String snapshotName, CheckOptions options) {

input.setFullPageConfig(options.getFullPageScreenshotConfig());

DiffingOptionsIn diffingOptionsIn =
generateDiffingOptions(options.getEnableOnly(), options.getDisableOnly());
if (diffingOptionsIn != null) {
input.diffingOptions = Optional.of(diffingOptionsIn);
}

CreateSnapshotFromWebDriverMutation mutation = new CreateSnapshotFromWebDriverMutation(input);
CreateSnapshotFromWebDriverMutation.Data check =
this.client.execute(mutation, CreateSnapshotFromWebDriverMutation.Data.class);
Expand All @@ -375,47 +369,6 @@ public void sauceVisualCheck(String snapshotName, CheckOptions options) {
}
}

private DiffingOptionsIn.Builder setDiffingOptionValue(
DiffingOptionsIn.Builder builder, String key, boolean value) {
switch (key) {
case Content:
return builder.withContent(value);
case Dimensions:
return builder.withDimensions(value);
case Position:
return builder.withPosition(value);
case Structure:
return builder.withStructure(value);
case Style:
return builder.withStyle(value);
case Visual:
return builder.withVisual(value);
}
return builder;
}

private DiffingOptionsIn generateDiffingOptions(
List<String> enableOnly, List<String> disableOnly) {
if (enableOnly != null && disableOnly != null) {
return null;
}

DiffingOptionsIn.Builder builder = DiffingOptionsIn.builder();

if (enableOnly != null) {
for (String option : DiffingOptionValues) {
setDiffingOptionValue(builder, option, enableOnly.contains(option));
}
}

if (disableOnly != null) {
for (String option : DiffingOptionValues) {
setDiffingOptionValue(builder, option, !disableOnly.contains(option));
}
}
return builder.build();
}

private static DiffingMethod toDiffingMethod(CheckOptions options) {
if (options == null || options.getDiffingMethod() == null) {
return null;
Expand Down Expand Up @@ -495,44 +448,48 @@ private List<RegionIn> extractIgnoreList(CheckOptions options) {
if (options == null) {
return Collections.emptyList();
}

List<WebElement> ignoredElements =
options.getIgnoreElements() == null ? Arrays.asList() : options.getIgnoreElements();

List<IgnoreRegion> ignoredRegions =
options.getIgnoreRegions() == null ? Arrays.asList() : options.getIgnoreRegions();

List<VisualRegion> visualRegions =
options.getIgnoreRegions() == null ? Arrays.asList() : options.getRegions();

List<RegionIn> result = new ArrayList<>();
for (int i = 0; i < options.getIgnoreElements().size(); i++) {
WebElement element = options.getIgnoreElements().get(i);
for (int i = 0; i < ignoredElements.size(); i++) {
WebElement element = ignoredElements.get(i);
if (validate(element) == null) {
throw new VisualApiException("options.ignoreElement[" + i + "] does not exist (yet)");
}
result.add(toIgnoreIn(element));
result.add(VisualRegion.ignoreChangesFor(element).toRegionIn());
}
for (int i = 0; i < options.getIgnoreRegions().size(); i++) {
IgnoreRegion ignoreRegion = options.getIgnoreRegions().get(i);
for (int i = 0; i < ignoredRegions.size(); i++) {
IgnoreRegion ignoreRegion = ignoredRegions.get(i);
if (validate(ignoreRegion) == null) {
throw new VisualApiException("options.ignoreRegion[" + i + "] is an invalid ignore region");
}
result.add(toIgnoreIn(ignoreRegion));
result.add(
VisualRegion.ignoreChangesFor(
ignoreRegion.getName(),
ignoreRegion.getX(),
ignoreRegion.getHeight(),
ignoreRegion.getWidth(),
ignoreRegion.getHeight())
.toRegionIn());
}
for (int i = 0; i < visualRegions.size(); i++) {
VisualRegion region = visualRegions.get(i);
if (validate(region) == null) {
throw new VisualApiException("options.region[" + i + "] is an invalid visual region");
}
result.add(region.toRegionIn());
}
return result;
}

private RegionIn toIgnoreIn(WebElement element) {
Rectangle r = element.getRect();
return RegionIn.builder()
.withX(r.getX())
.withY(r.getY())
.withWidth(r.getWidth())
.withHeight(r.getHeight())
.build();
}

private RegionIn toIgnoreIn(IgnoreRegion r) {
return RegionIn.builder()
.withX(r.getX())
.withY(r.getY())
.withWidth(r.getWidth())
.withHeight(r.getHeight())
.withDiffingOptions(generateDiffingOptions(r.getEnableOnly(), r.getDisableOnly()))
.build();
}

private WebElement validate(WebElement element) {
if (element == null || !element.isDisplayed() || element.getRect() == null) {
return null;
Expand All @@ -550,6 +507,20 @@ private IgnoreRegion validate(IgnoreRegion region) {
return region;
}

private VisualRegion validate(VisualRegion region) {
if (region == null) {
return null;
}
if (0 < region.getHeight() * region.getWidth()) {
return region;
}
WebElement ele = region.getElement();
if (ele != null && ele.isDisplayed() && ele.getRect() != null) {
return region;
}
return null;
}

public VisualBuild getBuild() {
return build;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ public static class CreateSnapshotFromWebDriverIn {
public CreateSnapshotFromWebDriverIn(
String buildUuid,
DiffingMethod diffingMethod,
Optional<DiffingOptionsIn> diffingOptions,
List<RegionIn> ignoreRegions,
String jobId,
String name,
String sessionId,
String sessionMetadata) {
this.buildUuid = buildUuid;
this.diffingMethod = diffingMethod;
this.diffingOptions = diffingOptions;
this.ignoreRegions = ignoreRegions;
this.jobId = jobId;
this.name = name;
Expand Down
Loading

0 comments on commit cae3407

Please sign in to comment.