Java WebDriver Integration
Introduction
This guide requires an existing Java JUnit / TestNG setup.
You can alternatively take a look to our example repository.
Sauce Visual provides a library allowing integration with WebDriver.
Sauce Visual plugin provides a library exposing a VisualApi
object that provides actions:
visual.sauceVisualCheck()
: Takes a screenshot and send it to Sauce Visual for comparison.visual.sauceVisualResults()
: Waits for all diff calculations to complete and returns a summary of results.
Quickstart
Step 1: Add Sauce Visual dependency
Add Sauce Visual dependency to your pom.xml
loading...
Note: You can find the latest versions available here.
Step 2: Configure Visual Testing integration
Declare a RemoteWebDriver and a VisualApi instance as class variables
import org.openqa.selenium.remote.RemoteWebDriver;
import com.saucelabs.visual.VisualApi;
private static VisualApi visual;
private static RemoteWebDriver driver;
Initialize RemoteWebDriver
and VisualApi
- JUnit
- TestNG
import org.junit.jupiter.api.BeforeAll;
@BeforeAll
public static void init() {
driver = new RemoteWebDriver(webDriverUrl, capabilities);
visual = new VisualApi.Builder(driver, sauceUsername, sauceAccessKey, DataCenter.US_WEST_1).build();
}
import org.testng.annotations.BeforeSuite;
@BeforeSuite
public static void init() {
driver = new RemoteWebDriver(webDriverUrl, capabilities);
visual = new VisualApi.Builder(driver, sauceUsername, sauceAccessKey, DataCenter.US_WEST_1).build();
}
To enhance efficiency in managing tests, it's important to provide a specific test name and suite name for each test. This practice allows Sauce Visual to effectively organize snapshots into coherent groups. As a result, it simplifies the review process, saving time and effort in navigating through test results and understanding the context of each snapshot.
Moreover, our Java Binding offers an automated solution to this process. By integrating the following code snippets into your tests, the Java Binding can automatically assign appropriate test names and suite names, streamlining your testing workflow.
- JUnit
- TestNG
import com.saucelabs.visual.junit5.TestMetaInfoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith({TestMetaInfoExtension.class})
public class MyJunitTestClass {
...
}
import com.saucelabs.visual.testng.TestMetaInfoListener;
import org.testng.annotations.Listeners;
@Listeners({TestMetaInfoListener.class})
public class MyTestNGTestClass {
...
}
Don't forget to quit the WebDriver
- JUnit
- TestNG
import org.junit.jupiter.api.AfterAll;
@AfterAll
public static void tearDown() {
if (driver != null) {
driver.quit();
}
}
import org.testng.annotations.AfterSuite;
@AfterSuite
public static void tearDown() {
if (driver != null) {
driver.quit();
}
}
Step 3: Add visual tests in your tests
Add a check to one of your tests:
- JUnit
- TestNG
import org.junit.jupiter.api.Test;
@Test
void checkLoginLooksTheSame() {
var loginPage = new LoginPage(driver);
loginPage.open();
visual.sauceVisualCheck("Before Login");
}
import org.testng.annotations.Test;
@Test
void checkLoginLooksTheSame() {
var loginPage = new LoginPage(driver);
loginPage.open();
visual.sauceVisualCheck("Before Login");
}
Step 4: Configure your Sauce Labs credentials
Sauce Visual relies on environment variables for authentications.
Both SAUCE_USERNAME
and SAUCE_ACCESS_KEY
need to be set prior starting your Java job.
Username and Access Key can be retrieved from https://app.saucelabs.com/user-settings.
export SAUCE_USERNAME=__YOUR_SAUCE_USER_NAME__
export SAUCE_ACCESS_KEY=__YOUR_SAUCE_ACCESS_KEY__
Step 5: Run the test
Upon executing your tests for the first time under this step, a visual baseline is automatically created in our system. This baseline serves as the standard for all subsequent WebDriver tests. As new tests are run, they are compared to this original baseline, with any deviations highlighted to signal visual changes. These comparisons are integral for detecting any unintended visual modifications early in your development cycle. All test builds, including the initial baseline and subsequent runs, can be monitored and managed through the Sauce Labs platform at Sauce Visual Builds.
Remember, the baseline is established during the initial run, and any subsequent visual differences detected will be marked for review.
Advanced usage
Customizing Your Builds (Environment Variables)
Below are the environment variables available in the Sauce Visual Java plugin. Keep in mind that the variables defined in CheckOptions
configuration have precedence over these.
Variable Name | Description | |
---|---|---|
SAUCE_USERNAME | required | Your Sauce Labs username. You can get this from the header of app.saucelabs.com |
SAUCE_ACCESS_KEY | required | Your Sauce Labs access key. You can get this from the header of app.saucelabs.com |
SAUCE_REGION | The region you'd like to run your Visual tests in. Defaults to us-west-1 if not supplied. Can be one of the following: 'eu-central-1' , 'us-west-1' or 'us-east-4' | |
SAUCE_VISUAL_BUILD_NAME | The name you would like to appear in the Sauce Visual dashboard. | |
SAUCE_VISUAL_BRANCH | The branch name you would like to associate this build with. We recommend using your current VCS branch in CI. | |
SAUCE_VISUAL_DEFAULT_BRANCH | The main branch name you would like to associate this build with. Usually main or master or alternatively the branch name your current branch was derived from. Follow me to learn more | |
SAUCE_VISUAL_PROJECT | The label / project you would like to associate this build with. | |
SAUCE_VISUAL_BUILD_ID | For advanced users, a user-supplied SauceLabs Visual build ID. Can be used to create builds in advance using the GraphQL API. This can be used to parallelize tests with multiple browsers, shard, or more. By default, this is not set and we create / finish a build during setup / teardown. | |
SAUCE_VISUAL_CUSTOM_ID | For advanced users, a user-supplied custom ID to identify this build. Can be used in CI to identify / check / re-check the status of a single build. Usage suggestions: CI pipeline ID. |
Test results summary
VisualApi#sauceVisualResults()
returns a summary of test results in Map<DiffStatus, Integer>
format where DiffStatus
is one of the following:
DiffStatus.QUEUED
: Diffs that are pending for processing. Should be 0 in case the test is completed without any timeoutsDiffStatus.EQUAL
: Diffs that have no changes detectedDiffStatus.UNAPPROVED
: Diffs that have detected changes and waiting for actionDiffStatus.APPROVED
: Diffs that have detected changes and have been approvedDiffStatus.REJECTED
: Diffs that have detected changes and have been rejected
Sample usage:
var EXPECTED_TOTAL_UNAPPROVED_DIFFS = 0;
assertEquals(visual.sauceVisualResults().get(DiffStatus.UNAPPROVED), EXPECTED_TOTAL_UNAPPROVED_DIFFS);
Build attributes
When creating the service in VisualApi
, extra fields can be set to define the context, thus acting on which baselines new snapshots will be compared to. (More info on baseline matching)
It needs to be defined through the VisualApi.Builder
object.
Available methods:
withBuild(String build)
: Sets the name of the buildwithProject(String project)
: Sets the name of the projectwithBranch(String branch)
: Sets the name of the branchwithDefaultBranch(String defaultBranch)
: Sets the name of the default branch
Example:
import com.saucelabs.visual.VisualApi;
import com.saucelabs.visual.DataCenter;
visual = new VisualApi.Builder(driver, username, accessKey, DataCenter.US_WEST_1)
.withBuild("Sauce Demo Test")
.withBranch("main")
.withProject("Java examples")
.build();
Ignored regions
Component-based ignored region
Sauce Visual provides a way to ignore a list of components.
An ignored component can be a specific element from the page.
Those ignored components are specified when requesting a new snapshot.
Example:
import com.saucelabs.visual.CheckOptions;
CheckOptions options = new CheckOptions();
options.setIgnoreElements(List.of(
// AddBackpackToCartButton will be ignored
inventoryPage.getAddBackpackToCartButton()
));
visual.sauceVisualCheck("Inventory Page", options);
User-specified ignored region
Alternatively, ignored regions can be user-specified areas. A region is defined by four elements.
x
,y
: The location of the top-left corner of the ignored regionwidth
: The width of the region to ignoreheight
: The height of the region to ignore
Note: all values are pixels
Example:
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.IgnoreRegion;
CheckOptions options = new CheckOptions();
IgnoreRegion ignoreRegion = new IgnoreRegion(
100, // x
100, // y
200, // width
200, // height
);
options.setIgnoreRegions(List.of(ignoreRegion));
visual.sauceVisualCheck("Before Login", options);
Selective Diffing
Sauce Visual allows selective diffing that permits to ignore changes from a certain kind (more information here).
Selective diffing is only available with Balanced
diffing method AND with DOM capture enabled.
Screenshot-wide configuration
Sauce Visual Binding allows to configure which kinds of changes should be effective on snapshot.
Example:
Ignoring only one kind:
visual.sauceVisualCheck(
"login-page",
new CheckOptions.Builder()
.withDiffingMethod(DiffingMethod.BALANCED)
.withCaptureDom(true)
// Every content change will be ignored
.disableOnly(EnumSet.of(DiffingFlag.Content))
.build());
Ignoring all kinds except one:
visual.sauceVisualCheck(
"login-page",
new CheckOptions.Builder()
.withDiffingMethod(DiffingMethod.BALANCED)
.withCaptureDom(true)
// Only style changes will be considered as a diff
.enableOnly(EnumSet.of(DiffingFlag.Style))
.build());
Area-specific configuration
Sauce Visual Binding allows to configure which kinds of changes should be effective specific regions of the snapshot.
Example:
WebElement usernameInput = driver.findElement(By.id("user-name"));
WebElement passwordInput = driver.findElement(By.id("password"));
visual.sauceVisualCheck(
"login-page",
new CheckOptions.Builder()
.withDiffingMethod(DiffingMethod.BALANCED)
.withCaptureDom(true)
// Ignore all kind of changes for element #user-name
.enableOnly(EnumSet.noneOf(DiffingFlag.class), usernameInput)
// Ignore only style changes for element #password
.enableOnly(EnumSet.of(DiffingFlag.Style), passwordInput)
.build());
Capturing the DOM snapshot
Sauce Visual does not capture dom snapshot by default. It can be changed in options.
Example:
import com.saucelabs.visual.CheckOptions;
CheckOptions options = new CheckOptions();
options.setCaptureDom(true);
visual.sauceVisualCheck("Inventory Page", options);
Full page screenshots
Full Page Screenshots capture the entire webpage, including content beyond the visible viewport, to ensure comprehensive visual testing. This feature helps teams identify layout or rendering issues across the full page and ensures consistency across devices and browsers.
By default, only the viewport is captured when .sauceVisualCheck
is used. You can opt in to capturing the entire page by using the enableFullPageScreenshots
option. It will capture everything by scrolling and stitching multiple screenshots together.
Configuration should be specified using the FullPageScreenshotConfig.Builder
object.
The maximum number of scrolls and stitches in a full page screenshot is 10.
Use full page screenshots only when necessary, as they slow down test execution.
Web
Available methods:
withDelayAfterScrollMs(int delayAfterScrollMs)
: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content.withDisableCSSAnimation(Boolean disableCSSAnimation)
: Disable CSS animations and the input caret in the app. The default value is true.withHideAfterFirstScroll(String... hideAfterFirstScroll)
: One or more CSS selectors that we should remove from the page after the first scroll. Useful for hiding fixed elements such as headers, cookie banners, etc.withHideScrollBars(Boolean hideScrollBars)
: Hide all scrollbars in the app. The default value is true.withScrollLimit(int scrollLimit)
: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10.
It's recommended to use the withHideAfterFirstScroll
method for elements with a fixed or sticky position, such as sticky headers or consent banners.
Examples:
import com.saucelabs.visual.CheckOptions;
CheckOptions options = new CheckOptions();
options.enableFullPageScreenshots();
visual.sauceVisualCheck("Long content page", options);
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
CheckOptions options = new CheckOptions();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withDelayAfterScrollMs(500)
.withDisableCSSAnimation(false)
.withHideAfterFirstScroll("#header")
.withHideScrollBars(false)
.withScrollLimit(5)
.build();
options.enableFullPageScreenshots(config);
visual.sauceVisualCheck("Long content page", options);
Mobile Native (beta)
Available methods:
withDelayAfterScrollMs(int delayAfterScrollMs)
: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content.withNativeClipSelector(SelectorIn nativeClipSelector)
: Selector used to identify the first element to which clipping will be applied.withScrollElement(WebElement scrollElement)
: Scrollable element used for scrolling. The default is root element.withScrollLimit(int scrollLimit)
: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10.
It is recommended to use the withScrollElement
method to set the appropriate scrollable container.
Examples:
- iOS
- Android
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView"));
CheckOptions options = new CheckOptions();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.withScrollLimit(5)
.build();
options.enableFullPageScreenshots(config);
visual.sauceVisualCheck("Long content page", options);
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView"));
CheckOptions options = new CheckOptions();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.withScrollLimit(5)
.build();
options.enableFullPageScreenshots(config);
visual.sauceVisualCheck("Long content page", options);
Only XPath selectors can be used for ignore regions and clipping to an element.
On iOS, selectors must be contained within the scrollElement
.
- iOS
- Android
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView"));
CheckOptions options = new CheckOptions();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.build();
options.enableFullPageScreenshots(config);
List<IgnoreSelectorIn> ignoreSelectors = List.of(
new IgnoreSelectorIn.Builder()
.withSelector(
new SelectorIn.Builder()
.withValue("//XCUIElementTypeStaticText[@name="Product Price"]")
.withType(SelectorType.XPATH)
.build())
.build());
options.setIgnoreSelectors(ignoreSelectors);
visual.sauceVisualCheck("Long content page", options);
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView"));
CheckOptions options = new CheckOptions();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.build();
options.enableFullPageScreenshots(config);
List<IgnoreSelectorIn> ignoreSelectors = List.of(
new IgnoreSelectorIn.Builder()
.withSelector(
new SelectorIn.Builder()
.withValue("//android.widget.TextView[@content-desc="Product Price"]")
.withType(SelectorType.XPATH)
.build())
.build());
options.setIgnoreSelectors(ignoreSelectors);
visual.sauceVisualCheck("Long content page", options);
- iOS
- Android
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView"));
CheckOptions options = new CheckOptions();
SelectorIn nativeClipSelector = new SelectorIn.Builder()
.withType(SelectorType.XPATH)
.withValue("//XCUIElementTypeCollectionView/XCUIElementTypeOther")
.build();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.withNativeClipSelector(nativeClipSelector)
.build();
options.enableFullPageScreenshots(config);
visual.sauceVisualCheck("Long content page", options);
import com.saucelabs.visual.CheckOptions;
import com.saucelabs.visual.model.FullPageScreenshotConfig;
RemoteWebDriver driver;
...
WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView"));
CheckOptions options = new CheckOptions();
SelectorIn nativeClipSelector = new SelectorIn.Builder()
.withType(SelectorType.XPATH)
.withValue("//androidx.recyclerview.widget.RecyclerView[@content-desc='Displays all products of catalog']")
.build();
FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder()
.withScrollElement(scrollElement)
.withNativeClipSelector(nativeClipSelector)
.build();
options.enableFullPageScreenshots(config);
visual.sauceVisualCheck("Long content page", options);
Full page screenshot for mobile native testing is in beta. Read more about mobile native limitation
Clip to an Element
You can clip to a specific element on the page by using the clipElement
option when calling Sauce Visual.
Notes:
- Clipping is done by taking a screenshot of the page then clipping it to the location of the requested element.
- We will attempt to scroll the element into view before taking the snapshot.
- We can only take a screenshot of what is visible in the current viewport, however, this can be combined with full page option to enable clipping large vertical elements.
Example:
import com.saucelabs.visual.CheckOptions;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
RemoteWebDriver driver;
...
WebElement element = driver.findElement(By.cssSelector(".your-css-selector"));
visual.sauceVisualCheck(
"Visible Sale Banner", new CheckOptions.Builder().withClipElement(element).build());
Examples
Two examples are available:
- An example project using Junit
- An example project using TestNG