Playwright Java SDK
The Evinced Playwright Java SDK integrates with new or existing Playwright tests to automatically detect accessibility issues. By adding a few lines of code to your Playwright project, you can begin analyzing all the web pages and DOM changes to provide a dynamic view of how your site can become more accessible. As a result of the test, a rich and comprehensive report is generated to easily track issues to resolution.
Interested in seeing this in action? Contact us to get started!
Prerequisites
- Playwright version 1.29 or higher
Get started
Installation
To install Playwright Java SDK you will need either a file provided by Evinced Support or access to a remote repository that provides it. If you have neither, contact us to get started.
Installation with a locally provided file
You can install Playwright Java SDK using a standalone .jar distribution. In this case, you need to do the following:
- Download the .jar file.
- Unpack the provided java-playwright-sdk.zip to any desirable location.
- Add the following dependencies entries pointing to java-playwright-sdk-version.jar
- Gradle:1 implementation files('/Users/<path-to-your-unpacked-folder>/java-playwright-sdk-<version>.jar')
- Maven:
- First, install the “all” jar into your local Maven repository by the following command:
Example:1mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file \2 -Dfile=java-playwright-sdk-<version>.jar \3 -DpomFile=java-playwright-sdk-<version>.pom1mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file \2 -Dfile=java-playwright-sdk-1.6.1.jar \3 -DpomFile=java-playwright-sdk-1.6.1.pom - Add the corresponding dependency into your pom.xml:1 <dependency>2 <groupId>com.evinced</groupId>3 <artifactId>java-playwright-sdk</artifactId>4 <version>1.6.1</version>5 </dependency>
- First, install the “all” jar into your local Maven repository by the following command:
- Gradle:
Installation from a remote repository
Evinced Customers have the option of accessing Playwright Java SDK from a remote repository — the Evinced Jfrog Artifactory — to keep their SDK version up-to-date and to share the SDK internally at their organization.
When access is enabled, Playwright Java SDK is available at
https://evinced.jfrog.io/artifactory/restricted-maven/com/evinced/java-playwright-sdk.
Authentication
To launch Playwright Java SDK, you need to have a Service ID and an API Key.
Where to find your Evinced SDK credentials
These credentials are available via the Evinced Product Hub in the “Automation for Web” or “Automation for Mobile” product areas. Click the “Get SDK” button to see the Service Account ID and API Key at the bottom of the page.
Authenticate for Offline Testing
There are two methods to provide the token: online mode and offline mode. Online mode contacts the Evinced Licensing Server. Offline mode assumes that an Evinced employee has supplied a JSON Web Token (JWT). If an offline token is required, please reach out to your account team or support@evinced.com.
Please set credentials in environment variables and reference the environment variables in code.
1# Online mode2export EVINCED_SERVICE_ID=<serviceId>3export EVINCED_API_KEY=<apiKey>45# Offline mode, when a JWT has been provided by Evinced6export EVINCED_SERVICE_ID=<serviceId>7export EVINCED_AUTH_TOKEN=<token>
Setting credentials, an example:
1using Evinced.SDK;23// Offline mode4EvincedSDK.SetOfflineCredentials(Environment.GetEnvironmentVariable("EVINCED_SERVICE_ID"), Environment.GetEnvironmentVariable("EVINCED_AUTH_TOKEN"));56// Online mode (not yet implemented)7EvincedSDK.SetCredentials(Environment.GetEnvironmentVariable("EVINCED_SERVICE_ID"), Environment.GetEnvironmentVariable("EVINCED_API_KEY"));
Your First Test
SDK Initialization
To use Playwright Java SDK, you first need to authenticate. Please refer to Authentication for details.
The only command you need to add is to wrap the existing WebDriver object.
1Playwright playwright = Playwright.create();2Browser browser = playwright.chromium().launch();3EvPage page = EvPageFactory.create(browser.newPage());
Add Evinced Accessibility Checks (Single Run Mode)
This is a simple example of how to add an Evinced accessibility scan to a test. Please note the inline comments that give detail on each test step.
1@Test2public void evAnalyzeTest() {3 Playwright playwright = Playwright.create();4 Browser browser = playwright.chromium().launch();5 EvPage page = EvPageFactory.create(browser.newPage());67 try{8 page.navigate("https://demo.evinced.com");9 Report report = page.evAnalyze();10 List<Issue> issues = report.getIssues();11 assertEquals(6, issues.size());12 } finally {13 playwright.close();14 }15}
Add Evinced Accessibility Checks (Continuous Mode)
This is an example of how to add a continuous Evinced accessibility scan to a test. Using the
evStart()
and evStop()
methods, the Evinced engine will continually scan in the background capturing all DOM changes and page navigation as the test is executed. This will capture all accessibility issues as clicking on drop-downs or similar interactions reveals more of the page. The advantage of continuous mode is that no interaction with the actual test code is needed.
1@Test2public void evAnalyzeTest() {3 Playwright playwright = Playwright.create();4 Browser browser = playwright.chromium().launch();5 EvPage page = EvPageFactory.create(browser.mewPage());67 try{8 page.navigate("https://demo.evinced.com");9 page.evStart();1011 // More test code to interact with the page1213 Report report = page.evStop();14 List<Issue> issues = report.getIssues();15 assertEquals(6, issues.size());16 } finally {17 playwright.close();18 }19}
API
EvPageFactory.create()
Prepares the Evinced object for use in the project.
1EvPage page = EvPageFactory.create(browser.newPage());
Refer to Configuration to see examples of initializing with options.
evAnalyze(options)
Scans the current page and returns a list of accessibility issues. This is the recommended method for static page analysis.
Note: This method is not supported if evStart()
is already running.
1EvPage page = EvPageFactory.create(browser.newPage());2Report report = page.evAnalyze();
Returns Report
.
The returned report object contains a list of accessibility issues.
For more information regarding reports as well as the report object itself, please refer to our detailed Web Reports page.
evStart(options)
Continually watches for DOM mutations and page navigation, recording accessibility issues
until the evStop()
method is called. This method is recommended for dynamic page flows.
1EvPage page = EvPageFactory.create(browser.newPage());2page.evStart();
Returns void
.
evStop(options)
Stops the issue-gathering process started by evStart()
.
1EvPage page = EvPageFactory.create(browser.newPage());2page.evStart();3Report report = page.evStop();
Returns Report
.
The returned report object includes all accessibility issues detected between
the evStart()
and evStop()
method calls.
For more information regarding reports as well as the report object itself, please refer to our detailed Web Reports page.
evSaveFile(destination, issues, format)
Saves issues in a file with the specified format and location.
Supported formats are json
, html
, sarif
, and csv
.
Find detailed information in the Web Reports page.
1Report report = page.evStop();2// OR3Report report = page.evAnalyze();45// create a JSON file named jsonReport.json6EvincedSDK.evSaveFile("jsonReport", report, FileFormat.JSON);78// create an HTML file named htmlReport.html9EvincedSDK.evSaveFile("htmlReport", report, FileFormat.HTML);1011// create an SARIF file named sarifReport.html12EvincedSDK.evSaveFile("sarifReport", report, FileFormat.SARIF);1314// create an CSV file named csvReport.csv15EvincedSDK.evSaveFile("csvReport", report, FileFormat.CSV);
FileFormat
Defines the file type of the report. Options are JSON
, HTML
, SARIF
and CSV
.
Returns Path
.
Aggregated Report
The aggregated report feature allows you to have a general aggregated report for
the whole run (not only for one test or suite). This report will contain all the
issues found by the tests where evStart()
and
evStop()
commands were called. It is still possible to use the
evSaveFile()
command in any place of your code along with this
Aggregated Report feature.
1Path htmlAggregatedReport = EvincedSDK.evSaveFile("evinced-html-report", FileFormat.HTML);
Configuration
The same configuration object can be used when initializing the Evinced object
using Global.config
and when calling the evStart()
and evAnalyze()
methods but with a bit different consequences.
Providing options when initializing defines a global configuration for all calls
of evAnalyze()
and evStart()
, while providing options to
either of those methods affect only the test in which they are called.
Options provided in either evAnalyze()
or evStart()
override
those set in Evinced engine initialization.
Engines Configuration
Evinced uses two separate engines when scanning for accessibility issues, one is the aXe engine and the other is the Evinced engine. By default, Evinced disables the aXe Needs Review and Best Practices issues based on customer request and due to the fact they are mostly false positives. Please note this setting when comparing issue counts directly. See an example of how to enable Needs Review and Best practices issues in the Toggles section.
Configuration Object
Global.config
is a global configuration of type EvConfig
that will be used with all commands by default.
1Global.config.setRootSelector("#some-selector");
The configurations can be passed to a specific command, such as evAnalyze()
or evStart()
, it will override the
global configuration for a single run.
1EvConfig configuration = new EvConfig();2configuration.setRootSelector(".block-dropdown");3evPage.evStart(configuration);
Root Selector
Sets a CSS selector to limit the Evinced Engine to scan only the selected element and its children. Must be a valid CSS selector. If not set, the Evinced Engine will scan the entire document.
Default: no value
1Global.config.setRootSelector("#some-selector");
Reports Screenshots
When true
, the Evinced SDK will include screenshots in its reports that
highlight elements with accessibility issues.
Default: false
.
Note: Enabling screenshots may affect test run performance.
1// On global level2Global.config.setEnableScreenshots(true);34// OR56// On command level7EvConfig localConf = new EvConfig();8localConf.setEnableScreenshots(false); // Override global value9page.evStart(localConf);10...11page.evStop();
Shadow DOM Support
Shadow DOM is now supported by default. No additional configuration is needed.
IFrames Support
When true
, accessibility analysis includes iframe that exist inside the page.
Default: false
.
1EvConfig configuration = new EvConfig();2configuration.setIFramesIncluded(true);
Proxy
Configures proxy server access settings. Needed to enable outbound communication to the Evinced Platform through a proxy server.
1...2BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions();3launchOptions.setProxy(new Proxy("ip:port").setUsername("user").setPassword("password"));4Browser browser = browserType.launch(launchOptions);5EvPage evPage = EvPageFactory.create(browser.newPage());6//if you want to use online authentication7EvincedSDK.setCredentials("serviceId", "secret");8...
Uploading Reports to Evinced Platform
Introduction
Evinced Platform allows you to seamlessly collect, organize, visualize and monitor Evinced accessibility reports in one place. In this section, we will guide you through the key functionalities of the upload methods of the accessibility reports from the Evinced SDK to the Evinced Platform, which was introduced in version 2.3.2. This upload method is fully compatible with the previous versions of the Evinced SDK API, and is disabled by default.
Enable Upload Report to Platform
To enable the uploading functionality of accessibility reports to
the Evinced Platform you will need to set the enableUploadToPlatform
method to "true":
1EvincedSDK.enableUploadToPlatform(true);
Automatic Report Upload
Once the enableUploadToPlatform
method is set to true
, then by default
all generated reports will be uploaded to the Platform upon calling the evStop()
command.
If you want to change this behavior, set the setUploadToPlatformDefault
feature flag to false
.
1EvincedSDK.setUploadToPlatformDefault(false);
If the setUploadToPlatformDefault
is disabled, you can still upload
selected reports to the platform.
For that, use the following parameter in the evStop()
command:
1...2evPage.evStop(PlatformUpload.ENABLED);
Test Names
To facilitate report management and be able to distinguish between different reports on the Platform, use the setTestInfo
method to inform the test name and test class.
It’s recommended to do that in the “beforeEach” hook.
TestNG:
1@BeforeEach2public void platformUploadingTestSetup(TestInfo testInfo){3 evPage.setTestInfo(testInfo.getDisplayName(), this.getClass().getCanonicalName());4}
Labels and Custom Fields
You can attach labels and custom fields to your report to enhance readability in the platform. The labels will be added to the uploaded reports. See the following code example of how to set that up:
1EvincedSDK.getTestRunInfo()2 .addLabel(Parameter.GIT_USER_NAME, "git")3 .addLabel(Parameter.GIT_BRANCH, "main")4 .addLabel(Parameter.USER_AGENT, "agent007")5 .addLabel(Parameter.ENVIRONMENT, "production")6 .addLabel(Parameter.FLOW, "standard")7 .customLabel("Product version", "1.2.3")8 .customLabel("OS Type", "Linux")9 .customLabel("OS Name", "openSuse");
Use of beforeEach and afterEach Hooks
We recommend using the beforeEach and afterEach hooks to control analysis sessions and upload reports to the platform. This way, each test will be uploaded separately with its own report.
In beforeEach hook use evStart()
to start Evinced analysis and set any labels
you want for the report to contain when uploading to the platform.
In the afterEach hook call evStop()
to stop analysis and upload reports to the platform.
See this code example:
1@BeforeMethod2protected void evStartBeforeMethod(ITestContext context, ITestResult result) {3 evPage.setTestInfo(testInfo.getDisplayName(), this.getClass().getCanonicalName());4 evPage.evStart();5}67@AfterMethod8protected void evStopAfterMethod(ITestContext context, ITestResult result) {9 evPage.evStop();10}
Putting All of This Together
Here is a complete code snippet of how to perform uploads to the platform on a per-test basis.
1package com.evinced.example;23import com.evinced.EvPage;4import com.evinced.EvPageFactory;5import com.evinced.EvincedSDK;6import com.evinced.impl.resultsUpload.TestRunInfo;7import com.microsoft.playwright.Browser;8import com.microsoft.playwright.Playwright;9import org.testng.annotations.*;10import org.testng.ITestContext;11import org.testng.ITestResult;121314public class TestExample {15 private static Playwright playwright;16 protected Browser browser;17 protected EvPage evPage;1819 @BeforeSuite20 protected void beforeSuite() {21 EvincedSDK.setOfflineCredentials(System.getenv("AUTH_SERVICE_ID"), System.getenv("AUTH_TOKEN"));22 EvincedSDK.enableUploadToPlatform(true);2324 EvincedSDK.getTestRunInfo()25 .addLabel(TestRunInfo.Parameter.GIT_USER_NAME, "git")26 .addLabel(TestRunInfo.Parameter.GIT_BRANCH, "master")27 .customLabel("Testing purpose", "Test platform uploading feature");28 }2930 @BeforeTest31 protected void beforeTest() {32 browser = getBrowser();33 evPage = EvPageFactory.create(browser.newPage());34 }3536 @AfterTest37 protected void afterTest() {38 playwright.close();39 }4041 @BeforeMethod42 protected void evStartBeforeMethod(ITestContext context, ITestResult result) {43 evPage.setTestInfo(result.getMethod().getMethodName(), result.getTestClass().getName());44 evPage.evStart();45 }4647 @AfterMethod48 protected void evStopAfterMethod(ITestContext context, ITestResult result) {49 evPage.evStop();50 }5152 public static Browser getBrowser() {53 playwright = Playwright.create();54 return playwright.chromium().launch();55 }565758 @Test(testName = "Test url")59 public void testUrl() {60 evPage.navigate("https://demo.evinced.com");61 }62}63
Tutorials
You can find fully functional example projects on our GitHub.
Step-by-step adding Evinced to existing test suite.
Preface: Existing test suite
1package tutorial;23import com.microsoft.playwright.Browser;4import com.microsoft.playwright.Page;5import com.microsoft.playwright.Playwright;6import org.junit.jupiter.api.*;78import static org.junit.jupiter.api.Assertions.assertTrue;91011public class TutorialTest1 {12 private static Playwright playwright;13 private static Browser browser;14 private Page page;1516 private interface Selectors {17 String HOUSE_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > div.dropdown.line";18 String TENT_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > ul > li:nth-child(4)";19 String LOCATION_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > div.dropdown.line";20 String CANADA_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > ul > li:nth-child(1)";21 String SEARCH_BUTTON = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > a";22 String SEARCH_RESULTS = "#gatsby-focus-wrapper > main > h1";23 }242526 @BeforeAll27 private static void launchBrowser() {28 playwright = Playwright.create();29 browser = playwright.chromium().launch();30 }3132 @AfterAll33 private static void closeBrowser() {34 playwright.close();35 }3637 @BeforeEach38 private void setUp(){39 page = browser.newPage();40 }4142 @AfterEach43 private void tearDown(){44 page.close();45 }4647 @Test48 public void tutorialTest1(){49 page.navigate("https://demo.evinced.com");50 assertTrue(page.isVisible(Selectors.SEARCH_BUTTON));51 }5253 @Test54 public void tutorialTest2(){55 page.navigate("https://demo.evinced.com");56 page.click(Selectors.HOUSE_DROPDOWN);57 page.click(Selectors.TENT_OPTION);58 page.click(Selectors.LOCATION_DROPDOWN);59 page.click(Selectors.CANADA_OPTION);60 page.click(Selectors.SEARCH_BUTTON);61 page.waitForLoadState();62 assertTrue(page.isVisible(Selectors.SEARCH_RESULTS));63 }64}
Step 1: Add Evinced, use snapshot scan and partial report
1package tutorial;23import com.evinced.*;4import com.microsoft.playwright.Browser;5import com.microsoft.playwright.Playwright;6import org.junit.jupiter.api.*;78import static org.junit.jupiter.api.Assertions.assertEquals;9import static org.junit.jupiter.api.Assertions.assertTrue;101112public class TutorialTest2 {13 private static Playwright playwright;14 private static Browser browser;15 // Change type from Page to EvPage16 private EvPage page;1718 private interface Selectors {19 String HOUSE_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > div.dropdown.line";20 String TENT_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > ul > li:nth-child(4)";21 String LOCATION_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > div.dropdown.line";22 String CANADA_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > ul > li:nth-child(1)";23 String SEARCH_BUTTON = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > a";24 String SEARCH_RESULTS = "#gatsby-focus-wrapper > main > h1";25 }262728 @BeforeAll29 private static void launchBrowser() {30 playwright = Playwright.create();31 browser = playwright.chromium().launch();32 // Add evinced licensing33 EvincedSDK.setOfflineCredentials(System.getenv("AUTH_SERVICE_ID"), System.getenv("AUTH_TOKEN"));34 }3536 @AfterAll37 private static void closeBrowser() {38 playwright.close();39 }4041 @BeforeEach42 private void setUp(){43 // Wrap Playwright's page44 // You can access the original page with page.getWrappedPage();45 page = EvPageFactory.create(browser.newPage());46 }4748 @AfterEach49 private void tearDown(){50 page.close();51 }5253 @Test54 public void tutorialTest1(){55 page.navigate("https://demo.evinced.com");56 assertTrue(page.isVisible(Selectors.SEARCH_BUTTON));57 // Get issues snapshot on the page58 Report report = page.evAnalyze();59 // Save partial report for the single scan60 EvincedSDK.evSaveFile("/tmp/ev-standalone-report.json", report, FileFormat.JSON);61 }6263 @Test64 public void tutorialTest2(){65 page.navigate("https://demo.evinced.com");66 page.click(Selectors.HOUSE_DROPDOWN);67 page.click(Selectors.TENT_OPTION);68 page.click(Selectors.LOCATION_DROPDOWN);69 page.click(Selectors.CANADA_OPTION);70 page.click(Selectors.SEARCH_BUTTON);71 page.waitForLoadState();72 assertTrue(page.isVisible(Selectors.SEARCH_RESULTS));73 // Get issues snapshot on the result page74 Report report = page.evAnalyze();75 // Assert page does not contain accessibility issues76 assertEquals(0, report.getIssues().size());77 }78}
Step 2: Use continuous mode and aggregated report
Let's do a step back to the original test and start modifying it again. Some steps will be similar.
1package tutorial;23import com.evinced.*;4import com.microsoft.playwright.Browser;5import com.microsoft.playwright.Playwright;6import org.junit.jupiter.api.*;78import static org.junit.jupiter.api.Assertions.assertTrue;91011public class TutorialTest3 {12 private static Playwright playwright;13 private static Browser browser;14 // Change type from Page to EvPage15 private EvPage page;1617 private interface Selectors {18 String HOUSE_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > div.dropdown.line";19 String TENT_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(1) > div > ul > li:nth-child(4)";20 String LOCATION_DROPDOWN = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > div.dropdown.line";21 String CANADA_OPTION = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > div:nth-child(2) > div > ul > li:nth-child(1)";22 String SEARCH_BUTTON = "#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container > a";23 String SEARCH_RESULTS = "#gatsby-focus-wrapper > main > h1";24 }252627 @BeforeAll28 private static void launchBrowser() {29 playwright = Playwright.create();30 browser = playwright.chromium().launch();31 // Add evinced licensing32 EvincedSDK.setOfflineCredentials(System.getenv("AUTH_SERVICE_ID"), System.getenv("AUTH_TOKEN"));33 }3435 @AfterAll36 private static void closeBrowser() {37 playwright.close();38 // Save the aggregated report for all the issues found during test run39 EvincedSDK.evSaveFile("/tmp/ev-aggregated-report.html", FileFormat.HTML);40 }4142 @BeforeEach43 private void setUp(){44 // Wrap Playwright's page45 // You can access the original page with page.getWrappedPage();46 page = EvPageFactory.create(browser.newPage());47 // Start gathering issues48 page.evStart();49 }5051 @AfterEach52 private void tearDown(){53 // Stop gathering issues54 // If you want you can work with returned set of issues gathered between evStart and evStop55 Report report = page.evStop();56 System.out.println("Found issues: " + report.getIssues().size());57 page.close();58 }5960 @Test61 public void tutorialTest1(){62 page.navigate("https://demo.evinced.com");63 assertTrue(page.isVisible(Selectors.SEARCH_BUTTON));64 }6566 @Test67 public void tutorialTest2(){68 page.navigate("https://demo.evinced.com");69 page.click(Selectors.HOUSE_DROPDOWN);70 page.click(Selectors.TENT_OPTION);71 page.click(Selectors.LOCATION_DROPDOWN);72 page.click(Selectors.CANADA_OPTION);73 page.click(Selectors.SEARCH_BUTTON);74 page.waitForLoadState();75 assertTrue(page.isVisible(Selectors.SEARCH_RESULTS));76 }77}
Fail the test if critical issues are found
Here you can see a way of failing your test if critical accessibility issues are found using the Playwright Java SDK.
Using evAnalyze:
1Report report = driver.evAnalyze();23List<Issue> criticalIssues = report.getIssues().stream()4 .filter(issue -> "Critical".equals(issue.getSeverity().getName()))5 .collect(Collectors.toList());67assertTrue(criticalIssues.isEmpty(), "Critical issues are found");
Using evStart/evStop:
1driver.evStart();2Report report = driver.evStop();34List<Issue> criticalIssues = DATA.getReport().getIssues().stream()5.filter(issue -> "Critical".equals(issue.getSeverity().getName()))6.collect(Collectors.toList());78assertTrue(criticalIssues.isEmpty(), "Critical issues are found");
The criticalIssues
array will contain all the critical issues found during the scan. If the array is not empty, the test will fail on the assertion.
Complete Test Suite Integration
The feature is not yet implemented in this SDK. If you’d like to increase the priority of it, please contact support@evinced.com.
Support
Please feel free to reach out to support@evinced.com with any questions.
FAQ
- Can I configure which validations to run?
Yes, see the Configuration section for details on how to configure Axe validations to your needs.
- Can I run tests with Evinced using cloud-based services like Sauce Labs, Perfecto, or BrowserStack?
Yes, we have tested the Evinced SDK on many of these types of cloud-based services and expect no issues.