Skip to content

Commit

Permalink
Espresso fps (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
kb-kerem authored Nov 14, 2024
1 parent 8b09a46 commit 739ab10
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 76 deletions.
44 changes: 14 additions & 30 deletions visual-espresso/visual/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"

buildConfigField "String", "SAUCE_USERNAME", getProperty("SAUCE_USERNAME", "\"${System.getenv('SAUCE_USERNAME')}\"")
buildConfigField "String", "SAUCE_ACCESS_KEY", getProperty("SAUCE_ACCESS_KEY", "\"${System.getenv('SAUCE_ACCESS_KEY')}\"")
buildConfigField "String", "SAUCE_USERNAME", "\"${getProperty("SAUCE_USERNAME", "")}\""
buildConfigField "String", "SAUCE_ACCESS_KEY", "\"${getProperty("SAUCE_ACCESS_KEY", "")}\""
buildConfigField "String", "VERSION_NAME", "\"${android.defaultConfig.versionName}\""
}

Expand All @@ -32,6 +32,8 @@ android {
}
publishing {
singleVariant("release") {
withSourcesJar()
withJavadocJar()
}
}
buildFeatures {
Expand All @@ -40,12 +42,17 @@ android {
}

String getProperty(String name, String defaultValue) {
if (!rootProject.file("./local.properties").exists()) {
return defaultValue
if (rootProject.file("./local.properties").exists()) {
def properties = new Properties()
properties.load(rootProject.file("./local.properties").newDataInputStream())
if (properties.containsKey(name)) {
return properties[name]
}
}
if (System.getenv(name) != null && !System.getenv(name).isBlank()) {
return System.getenv(name)
}
def properties = new Properties()
properties.load(rootProject.file("./local.properties").newDataInputStream())
return properties.containsKey(name) ? properties[name] : defaultValue
return defaultValue
}

dependencies {
Expand All @@ -72,8 +79,6 @@ publishing {
version = android.defaultConfig.versionName
afterEvaluate {
from components.release
artifact sourcesJar
artifact javadocJar
}
pom {
name = "${artifactName}"
Expand Down Expand Up @@ -106,27 +111,6 @@ publishing {
}
}

// Task for generating sources JAR
tasks.register('sourcesJar', Jar) {
archiveClassifier.set("sources")
from android.sourceSets.main.java.srcDirs
}

// Task for generating Javadoc JAR
tasks.register('javadocJar', Jar) {
archiveClassifier.set("javadoc")
dependsOn "androidJavadocs"
from androidJavadocs.destinationDir
}

// Android-specific Javadoc generation task
tasks.register('androidJavadocs', Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
destinationDir = file("../javadoc/")
failOnError false
}

apollo {
service("service") {
packageName.set("com.saucelabs.visual.graphql")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class SmokeTest {
BuildConfig.SAUCE_USERNAME,
BuildConfig.SAUCE_ACCESS_KEY)
.buildName("Smoke test")
.projectName("Espresso")
.projectName("Espresso SDK")
.branchName("main")
.build();

Expand All @@ -35,7 +35,7 @@ public void check() {
@Test
public void checkWithIgnoreRegions() {
Region region = Region.builder()
.x(100).y(100).width(100).height(100)
.x(200).y(200).width(100).height(100)
.build();
VisualCheckOptions options = new VisualCheckOptions.Builder()
.ignore(region).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,29 @@ VisualBuild createBuild(BuildAttributes buildAttributes) {
return new VisualBuild(d);
}

CreateSnapshotUploadMutation.Data uploadSnapshot(String buildId, boolean captureDom, View clipElement) {
CreateSnapshotUploadMutation.Data uploadSnapshot(String buildId, boolean captureDom, View clipElement, View scrollView) {
SnapshotUploadIn input = SnapshotUploadIn.builder().buildUuid(buildId).build();
CreateSnapshotUploadMutation m = CreateSnapshotUploadMutation.builder().input(input).build();
CreateSnapshotUploadMutation.Data d = graphQLClient.executeMutation(m);
CreateSnapshotUploadMutation.Data data = graphQLClient.executeMutation(m);

if (captureDom) {
byte[] dom = SnapshotHelper.getInstance().getDom();
SnapshotHelper.getInstance().uploadToUrl(d.result.domUploadUrl, dom, true);
byte[] dom = SnapshotHelper.getInstance().captureDom();
SnapshotHelper.getInstance().uploadDom(data.result.domUploadUrl, dom);
}

byte[] screenshot;
if (clipElement != null && clipElement == scrollView) { // Clip and fps views are same
screenshot = SnapshotHelper.getInstance().captureView(clipElement, true);
} else if (clipElement != null) { // Only clipping
screenshot = SnapshotHelper.getInstance().captureView(clipElement, false);
} else if (scrollView != null) { // Full page, no clipping
screenshot = SnapshotHelper.getInstance().captureView(scrollView, true);
} else { // No clipping, no full page
screenshot = SnapshotHelper.getInstance().captureScreen();
}
byte[] screenshot = clipElement != null
? SnapshotHelper.getInstance().getScreenshot(clipElement)
: SnapshotHelper.getInstance().getScreenshot();
SnapshotHelper.getInstance().uploadToUrl(d.result.imageUploadUrl, screenshot, false);
return d;
SnapshotHelper.getInstance().uploadScreenshot(data.result.imageUploadUrl, screenshot);

return data;
}

CreateSnapshotMutation.Data createSnapshot(SnapshotIn snapshotIn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
import static androidx.test.espresso.Espresso.onView;

import android.view.View;
import android.widget.ScrollView;

import androidx.core.widget.NestedScrollView;

import com.saucelabs.visual.espresso.GetViewAction;
import com.saucelabs.visual.exception.VisualApiException;
import com.saucelabs.visual.graphql.type.DiffingMethod;
import com.saucelabs.visual.graphql.type.DiffingOptionsIn;
import com.saucelabs.visual.graphql.type.RegionIn;
import com.saucelabs.visual.model.Region;
Expand All @@ -24,20 +29,26 @@ public class VisualCheckOptions {
private final Boolean captureDom;
private final DiffingOptionsIn diffingOptions;
private final View clipElement;
private final DiffingMethod diffingMethod;
private final View scrollView;

private VisualCheckOptions(
String testName,
String suiteName,
List<RegionIn> ignoreRegions,
Boolean captureDom,
DiffingOptionsIn diffingOptions,
View clipElement) {
View clipElement,
DiffingMethod diffingMethod,
View scrollView) {
this.testName = testName;
this.suiteName = suiteName;
this.ignoreRegions = ignoreRegions;
this.captureDom = captureDom;
this.diffingOptions = diffingOptions;
this.clipElement = clipElement;
this.diffingMethod = diffingMethod;
this.scrollView = scrollView;
}

public static final class Builder {
Expand All @@ -47,68 +58,173 @@ public static final class Builder {
private Boolean captureDom;
private DiffingOptionsIn diffingOptions;
private View clipElement;
private DiffingMethod diffingMethod;
private View scrollView;

/**
* Specify the test name for your tests
* <br>
* Use this method in case {@link com.saucelabs.visual.junit.TestMetaInfoRule}
* doesn't work for some reason.
*
* @param testName The name of your test
* @return Builder instance
*/
public Builder testName(String testName) {
this.testName = testName;
return this;
}

/**
* Specify the suite name for your tests
* <br>
* Use this method in case {@link com.saucelabs.visual.junit.TestMetaInfoRule}
* doesn't work for some reason.
*
* @param suiteName The name of your suite
* @return Builder instance
*/
public Builder suiteName(String suiteName) {
this.suiteName = suiteName;
return this;
}

/**
* Specify a set of {@link Region}s to be ignored by Sauce Visual
*
* @param regions Regions to be ignored
* @return Builder instance
*/
public Builder ignore(Region... regions) {
for (Region region : regions) {
this.ignoreRegions.add(RegionInFactory.fromRegion(region));
for (Region r : regions) {
RegionIn region = scrollView != null
? RegionInFactory.fromRegion(r, scrollView)
: RegionInFactory.fromRegion(r);
this.ignoreRegions.add(region);
}
return this;
}

/**
* Specify a set of {@link Matcher}s that resolve to {@link View}s to be ignored by Sauce Visual
*
* @param viewMatchers View matchers to be ignored
* @return Builder instance
*/
@SafeVarargs
public final Builder ignore(Matcher<View>... viewMatchers) {
List<RegionIn> result = new ArrayList<>();
for (Matcher<View> viewMatcher : viewMatchers) {
result.add(RegionInFactory.fromViewMatcher(viewMatcher));
RegionIn region = scrollView != null
? RegionInFactory.fromViewMatcher(viewMatcher, scrollView)
: RegionInFactory.fromViewMatcher(viewMatcher);
result.add(region);
}
this.ignoreRegions.addAll(result);
return this;
}

/**
* Specify a set of {@link View}s to be ignored by Sauce Visual
*
* @param views Views to be ignored
* @return Builder instance
*/
public Builder ignore(View... views) {
List<RegionIn> result = new ArrayList<>();
for (View view : views) {
result.add(RegionInFactory.fromView(view));
RegionIn region = scrollView != null
? RegionInFactory.fromView(view, scrollView)
: RegionInFactory.fromView(view);
result.add(region);
}
this.ignoreRegions.addAll(result);
return this;
}

/**
* Specify if the DOM (Android hierarchy) needs to be captured.
*
* @param captureDom True to capture the DOM
* @return Builder instance
*/
public Builder captureDom(boolean captureDom) {
this.captureDom = captureDom;
return this;
}

/**
* Specify a {@link Matcher} that will be resolved to a {@link View} that needs clipping
*
* @param viewMatcher View matchers to be clipped
* @return Builder instance
*/
public Builder clipElement(Matcher<View> viewMatcher) {
GetViewAction action = new GetViewAction();
onView(viewMatcher).perform(action);
this.clipElement = action.getView();
return this;
}

/**
* Specify a {@link View} that needs clipping
*
* @param view View to be clipped
* @return Builder instance
*/
public Builder clipElement(View view) {
this.clipElement = view;
return this;
}

/**
* Specify the {@link DiffingMethod} to be used
* <br/>
* Default is BALANCED
*
* @param diffingMethod DiffingMethod to be used
* @return Builder instance
*/
public Builder diffingMethod(DiffingMethod diffingMethod) {
this.diffingMethod = diffingMethod;
return this;
}

/**
* Specify a {@link Matcher} that resolves to a {@link NestedScrollView} or {@link ScrollView}
* that will be used for full page screenshots
*
* @param scrollViewMatcher View matchers to be used for full page screenshot
* @return Builder instance
*/
public Builder fullPageScreenshot(Matcher<View> scrollViewMatcher) {
GetViewAction action = new GetViewAction();
onView(scrollViewMatcher).perform(action);
this.scrollView = action.getView();
return this;
}

/**
* Specify a {@link NestedScrollView} or {@link ScrollView} that will be used for full page screenshots
*
* @param scrollView View to be used for full page screenshot
* @return Builder instance
*/
public Builder fullPageScreenshot(View scrollView) {
this.scrollView = scrollView;
return this;
}

public VisualCheckOptions build() {
return new VisualCheckOptions(
testName,
suiteName,
ignoreRegions,
captureDom,
diffingOptions,
clipElement);
clipElement,
diffingMethod,
scrollView);
}

}
Expand Down Expand Up @@ -138,7 +254,7 @@ public String resolveSuiteName() {
}

public List<RegionIn> getIgnoreRegions() {
if (clipElement == null) {
if (clipElement == null && scrollView == null) {
ignoreRegions.add(BarRegionHelper.getStatusBarRegion());
ignoreRegions.add(BarRegionHelper.getNavigationBarRegion());
}
Expand All @@ -157,6 +273,17 @@ public View getClipElement() {
return clipElement;
}

public DiffingMethod getDiffingMethod() {
return diffingMethod == null ? DiffingMethod.BALANCED : diffingMethod;
}

public View getScrollView() {
if (scrollView != null && !(scrollView instanceof ScrollView || scrollView instanceof NestedScrollView)) {
throw new VisualApiException("Full page screenshot only supports NestedScrollView or ScrollView instances");
}
return scrollView;
}

public static Builder builder() {
return new Builder();
}
Expand Down
Loading

0 comments on commit 739ab10

Please sign in to comment.