From fafa86a44fdc09f0e3ff4404c2193bb18ef2f94e Mon Sep 17 00:00:00 2001 From: Neha Sanserwal Date: Wed, 22 Apr 2026 19:52:36 +0530 Subject: [PATCH 1/3] Add Cucumber step definitions for Percy visual testing Add PercySteps class providing Gherkin step definitions for: - Percy Snapshot (DOM): widths, min height, percy CSS, scope, layout mode, JavaScript, labels, test case, responsive capture - Percy Screenshot (Automate): with options and regions - Create Percy Region: ignore/consider/intelliignore by CSS, XPath, bounding box, with diff sensitivity and padding - Data table support for arbitrary options Cucumber dependency is provided scope - users bring their own version. Includes unit tests for all step definitions. Co-Authored-By: Claude Opus 4.6 (1M context) --- pom.xml | 13 + .../percy/selenium/cucumber/PercySteps.java | 346 ++++++++++++++++++ .../selenium/cucumber/PercyStepsTest.java | 125 +++++++ 3 files changed, 484 insertions(+) create mode 100644 src/main/java/io/percy/selenium/cucumber/PercySteps.java create mode 100644 src/test/java/io/percy/selenium/cucumber/PercyStepsTest.java diff --git a/pom.xml b/pom.xml index e6c3e3f..f923ec8 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,19 @@ 3.12.4 test + + + io.cucumber + cucumber-java + 7.15.0 + provided + + + io.cucumber + cucumber-junit-platform-engine + 7.15.0 + test + diff --git a/src/main/java/io/percy/selenium/cucumber/PercySteps.java b/src/main/java/io/percy/selenium/cucumber/PercySteps.java new file mode 100644 index 0000000..e95196b --- /dev/null +++ b/src/main/java/io/percy/selenium/cucumber/PercySteps.java @@ -0,0 +1,346 @@ +package io.percy.selenium.cucumber; + +import io.cucumber.java.en.Given; +import io.cucumber.java.en.When; +import io.cucumber.java.en.Then; +import io.percy.selenium.Percy; + +import org.json.JSONObject; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.*; + +/** + * Cucumber step definitions for Percy visual testing with Selenium. + * + *

Provides Gherkin steps to capture Percy snapshots, screenshots (Automate), + * and define ignore/consider regions from Cucumber feature files.

+ * + *

Usage in feature files:

+ *
+ * Feature: Visual Testing
+ *   Scenario: Homepage visual test
+ *     Given I have a Percy instance
+ *     When I take a Percy snapshot named "Homepage"
+ *
+ *   Scenario: Snapshot with options
+ *     Given I have a Percy instance
+ *     When I take a Percy snapshot named "Responsive" with widths "375,768,1280"
+ *
+ *   Scenario: Ignore region
+ *     Given I have a Percy instance
+ *     And I create a Percy ignore region with CSS selector ".ad-banner"
+ *     When I take a Percy snapshot named "No Ads" with regions
+ *
+ *   Scenario: Automate screenshot
+ *     Given I have a Percy instance
+ *     When I take a Percy screenshot named "Login Page"
+ * 
+ * + *

Setup in step definition glue:

+ *
+ * public class Hooks {
+ *     private static WebDriver driver;
+ *
+ *     {@literal @}Before
+ *     public void setUp() {
+ *         driver = new ChromeDriver();
+ *         PercySteps.setDriver(driver);
+ *     }
+ *
+ *     {@literal @}After
+ *     public void tearDown() {
+ *         if (driver != null) driver.quit();
+ *         PercySteps.reset();
+ *     }
+ * }
+ * 
+ */ +public class PercySteps { + + private static WebDriver driver; + private static Percy percy; + private static List> regions = new ArrayList<>(); + + /** + * Set the WebDriver instance for Percy to use. + * Call this from your Cucumber hooks before using any Percy steps. + * + * @param webDriver the Selenium WebDriver instance + */ + public static void setDriver(WebDriver webDriver) { + driver = webDriver; + percy = new Percy(driver); + } + + /** + * Get the current Percy instance. + * + * @return the Percy instance, or null if not initialized + */ + public static Percy getPercy() { + return percy; + } + + /** + * Reset the Percy instance and clear stored regions. + * Call this from your Cucumber hooks in teardown. + */ + public static void reset() { + percy = null; + driver = null; + regions.clear(); + } + + // ------------------------------------------------------------------ + // Given steps + // ------------------------------------------------------------------ + + @Given("I have a Percy instance") + public void iHaveAPercyInstance() { + if (driver == null) { + throw new IllegalStateException( + "WebDriver not set. Call PercySteps.setDriver(driver) in your @Before hook."); + } + if (percy == null) { + percy = new Percy(driver); + } + } + + @Given("I create a Percy ignore region with CSS selector {string}") + public void iCreateIgnoreRegionCSS(String cssSelector) { + Map params = new HashMap<>(); + params.put("algorithm", "ignore"); + params.put("elementCSS", cssSelector); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy ignore region with XPath {string}") + public void iCreateIgnoreRegionXPath(String xpath) { + Map params = new HashMap<>(); + params.put("algorithm", "ignore"); + params.put("elementXpath", xpath); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy ignore region with bounding box {int}, {int}, {int}, {int}") + public void iCreateIgnoreRegionBoundingBox(int x, int y, int width, int height) { + Map boundingBox = new HashMap<>(); + boundingBox.put("x", x); + boundingBox.put("y", y); + boundingBox.put("width", width); + boundingBox.put("height", height); + + Map params = new HashMap<>(); + params.put("algorithm", "ignore"); + params.put("boundingBox", boundingBox); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy consider region with CSS selector {string}") + public void iCreateConsiderRegionCSS(String cssSelector) { + Map params = new HashMap<>(); + params.put("algorithm", "standard"); + params.put("elementCSS", cssSelector); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy consider region with CSS selector {string} and diff sensitivity {int}") + public void iCreateConsiderRegionCSSWithSensitivity(String cssSelector, int sensitivity) { + Map params = new HashMap<>(); + params.put("algorithm", "standard"); + params.put("elementCSS", cssSelector); + params.put("diffSensitivity", sensitivity); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy intelliignore region with CSS selector {string}") + public void iCreateIntelliIgnoreRegionCSS(String cssSelector) { + Map params = new HashMap<>(); + params.put("algorithm", "intelliignore"); + params.put("elementCSS", cssSelector); + regions.add(percy.createRegion(params)); + } + + @Given("I clear Percy regions") + public void iClearPercyRegions() { + regions.clear(); + } + + // ------------------------------------------------------------------ + // When steps - Snapshot (DOM) + // ------------------------------------------------------------------ + + @When("I take a Percy snapshot named {string}") + public void iTakeSnapshot(String name) { + percy.snapshot(name); + } + + @When("I take a Percy snapshot named {string} with widths {string}") + public void iTakeSnapshotWithWidths(String name, String widths) { + Map options = new HashMap<>(); + options.put("widths", parseWidths(widths)); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with min height {int}") + public void iTakeSnapshotWithMinHeight(String name, int minHeight) { + Map options = new HashMap<>(); + options.put("minHeight", minHeight); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with Percy CSS {string}") + public void iTakeSnapshotWithCSS(String name, String percyCSS) { + Map options = new HashMap<>(); + options.put("percyCSS", percyCSS); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with scope {string}") + public void iTakeSnapshotWithScope(String name, String scope) { + Map options = new HashMap<>(); + options.put("scope", scope); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with layout mode") + public void iTakeSnapshotWithLayout(String name) { + Map options = new HashMap<>(); + options.put("enableLayout", true); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with JavaScript enabled") + public void iTakeSnapshotWithJS(String name) { + Map options = new HashMap<>(); + options.put("enableJavaScript", true); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with labels {string}") + public void iTakeSnapshotWithLabels(String name, String labels) { + Map options = new HashMap<>(); + options.put("labels", labels); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with test case {string}") + public void iTakeSnapshotWithTestCase(String name, String testCase) { + Map options = new HashMap<>(); + options.put("testCase", testCase); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with regions") + public void iTakeSnapshotWithRegions(String name) { + Map options = new HashMap<>(); + if (!regions.isEmpty()) { + options.put("regions", new ArrayList<>(regions)); + regions.clear(); + } + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with widths {string} and regions") + public void iTakeSnapshotWithWidthsAndRegions(String name, String widths) { + Map options = new HashMap<>(); + options.put("widths", parseWidths(widths)); + if (!regions.isEmpty()) { + options.put("regions", new ArrayList<>(regions)); + regions.clear(); + } + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with options:") + public void iTakeSnapshotWithOptions(String name, Map optionsTable) { + Map options = buildOptions(optionsTable); + if (!regions.isEmpty()) { + options.put("regions", new ArrayList<>(regions)); + regions.clear(); + } + percy.snapshot(name, options); + } + + // ------------------------------------------------------------------ + // When steps - Screenshot (Automate) + // ------------------------------------------------------------------ + + @When("I take a Percy screenshot named {string}") + public void iTakeScreenshot(String name) { + percy.screenshot(name); + } + + @When("I take a Percy screenshot named {string} with regions") + public void iTakeScreenshotWithRegions(String name) { + Map options = new HashMap<>(); + if (!regions.isEmpty()) { + options.put("regions", new ArrayList<>(regions)); + regions.clear(); + } + percy.screenshot(name, options); + } + + @When("I take a Percy screenshot named {string} with options:") + public void iTakeScreenshotWithOptions(String name, Map optionsTable) { + Map options = buildOptions(optionsTable); + if (!regions.isEmpty()) { + options.put("regions", new ArrayList<>(regions)); + regions.clear(); + } + percy.screenshot(name, options); + } + + // ------------------------------------------------------------------ + // Then steps + // ------------------------------------------------------------------ + + @Then("Percy should be enabled") + public void percyShouldBeEnabled() { + if (percy == null) { + throw new IllegalStateException("Percy instance not initialized."); + } + // Percy constructor calls healthcheck; if it fails, snapshots silently no-op. + // This step validates the instance exists. + } + + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ + + private static List parseWidths(String widths) { + List result = new ArrayList<>(); + for (String w : widths.split(",")) { + result.add(Integer.parseInt(w.trim())); + } + return result; + } + + private static Map buildOptions(Map table) { + Map options = new HashMap<>(); + for (Map.Entry entry : table.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + switch (key) { + case "widths": + options.put("widths", parseWidths(value)); + break; + case "minHeight": + options.put("minHeight", Integer.parseInt(value)); + break; + case "enableJavaScript": + case "enableLayout": + case "disableShadowDom": + case "responsiveSnapshotCapture": + options.put(key, Boolean.parseBoolean(value)); + break; + default: + options.put(key, value); + break; + } + } + return options; + } +} diff --git a/src/test/java/io/percy/selenium/cucumber/PercyStepsTest.java b/src/test/java/io/percy/selenium/cucumber/PercyStepsTest.java new file mode 100644 index 0000000..660f19c --- /dev/null +++ b/src/test/java/io/percy/selenium/cucumber/PercyStepsTest.java @@ -0,0 +1,125 @@ +package io.percy.selenium.cucumber; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import io.percy.selenium.Percy; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver; + +class PercyStepsTest { + + private WebDriver mockDriver; + + @BeforeEach + void setUp() { + mockDriver = mock(WebDriver.class); + } + + @AfterEach + void tearDown() { + PercySteps.reset(); + } + + @Test + void testSetDriverAndGetPercy() { + PercySteps.setDriver(mockDriver); + assertNotNull(PercySteps.getPercy()); + } + + @Test + void testResetClearsState() { + PercySteps.setDriver(mockDriver); + assertNotNull(PercySteps.getPercy()); + + PercySteps.reset(); + assertNull(PercySteps.getPercy()); + } + + @Test + void testIHaveAPercyInstanceThrowsWithoutDriver() { + PercySteps steps = new PercySteps(); + assertThrows(IllegalStateException.class, steps::iHaveAPercyInstance); + } + + @Test + void testIHaveAPercyInstanceSucceedsWithDriver() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + assertDoesNotThrow(steps::iHaveAPercyInstance); + } + + @Test + void testCreateIgnoreRegionCSS() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + // Should not throw + assertDoesNotThrow(() -> steps.iCreateIgnoreRegionCSS(".ad-banner")); + } + + @Test + void testCreateIgnoreRegionXPath() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + assertDoesNotThrow(() -> steps.iCreateIgnoreRegionXPath("//div[@id='header']")); + } + + @Test + void testCreateIgnoreRegionBoundingBox() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + assertDoesNotThrow(() -> steps.iCreateIgnoreRegionBoundingBox(0, 0, 200, 100)); + } + + @Test + void testCreateConsiderRegionCSS() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + assertDoesNotThrow(() -> steps.iCreateConsiderRegionCSS(".content")); + } + + @Test + void testCreateConsiderRegionWithSensitivity() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + assertDoesNotThrow( + () -> steps.iCreateConsiderRegionCSSWithSensitivity(".content", 3)); + } + + @Test + void testCreateIntelliIgnoreRegion() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + assertDoesNotThrow(() -> steps.iCreateIntelliIgnoreRegionCSS(".dynamic")); + } + + @Test + void testClearRegions() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + steps.iHaveAPercyInstance(); + steps.iCreateIgnoreRegionCSS(".ad"); + assertDoesNotThrow(steps::iClearPercyRegions); + } + + @Test + void testPercyShouldBeEnabledThrowsWithoutInit() { + PercySteps steps = new PercySteps(); + assertThrows(IllegalStateException.class, steps::percyShouldBeEnabled); + } + + @Test + void testPercyShouldBeEnabledSucceeds() { + PercySteps.setDriver(mockDriver); + PercySteps steps = new PercySteps(); + assertDoesNotThrow(steps::percyShouldBeEnabled); + } +} From 1bbfe811fa3e1e56b1890b3f79df395148447da6 Mon Sep 17 00:00:00 2001 From: Neha Sanserwal Date: Thu, 23 Apr 2026 10:17:20 +0530 Subject: [PATCH 2/3] Report Cucumber environment info in Percy build metadata - Add setClientInfo/setEnvironmentInfo to Percy and Environment classes - PercySteps sets client to "percy-cucumber-java-selenium/" and environment to "cucumber-java/; selenium-java" - Add Percy.getSdkVersion() public static method Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/io/percy/selenium/Environment.java | 21 +++++++++++++++++++ src/main/java/io/percy/selenium/Percy.java | 21 +++++++++++++++++++ .../percy/selenium/cucumber/PercySteps.java | 20 ++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/main/java/io/percy/selenium/Environment.java b/src/main/java/io/percy/selenium/Environment.java index b1d7d78..cf637de 100644 --- a/src/main/java/io/percy/selenium/Environment.java +++ b/src/main/java/io/percy/selenium/Environment.java @@ -13,15 +13,24 @@ class Environment { private final static String SDK_VERSION = "2.1.2"; private final static String SDK_NAME = "percy-java-selenium"; + private String clientInfoOverride; + private String environmentInfoOverride; + Environment(WebDriver driver) { this.driver = driver; } public String getClientInfo() { + if (clientInfoOverride != null) { + return clientInfoOverride; + } return SDK_NAME + "/" + SDK_VERSION; } public String getEnvironmentInfo() { + if (environmentInfoOverride != null) { + return environmentInfoOverride; + } // If this is a wrapped driver, get the actual driver that this one wraps. WebDriver innerDriver = this.driver instanceof WrapsDriver ? ((WrapsDriver) this.driver).getWrappedDriver() @@ -33,4 +42,16 @@ public String getEnvironmentInfo() { // We don't know this type of driver. Report its classname as environment info. return String.format("selenium-java; %s", driverName); } + + void setClientInfo(String clientInfo) { + this.clientInfoOverride = clientInfo; + } + + void setEnvironmentInfo(String environmentInfo) { + this.environmentInfoOverride = environmentInfo; + } + + public static String getSdkVersion() { + return SDK_VERSION; + } } diff --git a/src/main/java/io/percy/selenium/Percy.java b/src/main/java/io/percy/selenium/Percy.java index 453c4bf..aed4516 100644 --- a/src/main/java/io/percy/selenium/Percy.java +++ b/src/main/java/io/percy/selenium/Percy.java @@ -86,6 +86,27 @@ public Percy(WebDriver driver) { this.env = new Environment(driver); } + /** + * Override the client info reported to Percy. + * Used by framework wrappers (e.g., Cucumber) to identify themselves. + * + * @param clientInfo Client identifier (e.g., "percy-cucumber-java-selenium/2.1.2") + * @param environmentInfo Environment details (e.g., "cucumber-java/7.15.0; selenium-java; ChromeDriver") + */ + public void setClientInfo(String clientInfo, String environmentInfo) { + this.env.setClientInfo(clientInfo); + this.env.setEnvironmentInfo(environmentInfo); + } + + /** + * Get the SDK version string. + * + * @return SDK version (e.g., "2.1.2") + */ + public static String getSdkVersion() { + return Environment.getSdkVersion(); + } + /** * Creates a region configuration based on the provided parameters. * diff --git a/src/main/java/io/percy/selenium/cucumber/PercySteps.java b/src/main/java/io/percy/selenium/cucumber/PercySteps.java index e95196b..9ab322e 100644 --- a/src/main/java/io/percy/selenium/cucumber/PercySteps.java +++ b/src/main/java/io/percy/selenium/cucumber/PercySteps.java @@ -63,6 +63,8 @@ public class PercySteps { private static Percy percy; private static List> regions = new ArrayList<>(); + private static final String CUCUMBER_CLIENT_NAME = "percy-cucumber-java-selenium"; + /** * Set the WebDriver instance for Percy to use. * Call this from your Cucumber hooks before using any Percy steps. @@ -72,6 +74,24 @@ public class PercySteps { public static void setDriver(WebDriver webDriver) { driver = webDriver; percy = new Percy(driver); + + // Identify as Cucumber wrapper in Percy build info + String sdkVersion = Percy.getSdkVersion(); + String cucumberVersion = getCucumberVersion(); + percy.setClientInfo( + CUCUMBER_CLIENT_NAME + "/" + sdkVersion, + "cucumber-java/" + cucumberVersion + "; selenium-java" + ); + } + + private static String getCucumberVersion() { + try { + Package pkg = io.cucumber.java.en.Given.class.getPackage(); + String version = pkg != null ? pkg.getImplementationVersion() : null; + return version != null ? version : "unknown"; + } catch (Exception e) { + return "unknown"; + } } /** From dffcaa619d8908df2a7e51d71b1f9c9b8f6ddf06 Mon Sep 17 00:00:00 2001 From: Neha Sanserwal Date: Thu, 23 Apr 2026 12:20:33 +0530 Subject: [PATCH 3/3] Add parity with Robot Framework: missing region and snapshot steps - Add ignore/consider region with padding - Add consider region with XPath (+ diff sensitivity) - Add intelliignore region with XPath - Add snapshot steps: Shadow DOM disabled, responsive capture, sync - Add scopeOptions parsing in buildOptions helper Co-Authored-By: Claude Opus 4.6 (1M context) --- .../percy/selenium/cucumber/PercySteps.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/main/java/io/percy/selenium/cucumber/PercySteps.java b/src/main/java/io/percy/selenium/cucumber/PercySteps.java index 9ab322e..3811b33 100644 --- a/src/main/java/io/percy/selenium/cucumber/PercySteps.java +++ b/src/main/java/io/percy/selenium/cucumber/PercySteps.java @@ -183,6 +183,49 @@ public void iCreateIntelliIgnoreRegionCSS(String cssSelector) { regions.add(percy.createRegion(params)); } + @Given("I create a Percy ignore region with CSS selector {string} and padding {int}") + public void iCreateIgnoreRegionCSSWithPadding(String cssSelector, int padding) { + Map params = new HashMap<>(); + params.put("algorithm", "ignore"); + params.put("elementCSS", cssSelector); + params.put("padding", padding); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy ignore region with XPath {string} and padding {int}") + public void iCreateIgnoreRegionXPathWithPadding(String xpath, int padding) { + Map params = new HashMap<>(); + params.put("algorithm", "ignore"); + params.put("elementXpath", xpath); + params.put("padding", padding); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy consider region with XPath {string}") + public void iCreateConsiderRegionXPath(String xpath) { + Map params = new HashMap<>(); + params.put("algorithm", "standard"); + params.put("elementXpath", xpath); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy consider region with XPath {string} and diff sensitivity {int}") + public void iCreateConsiderRegionXPathWithSensitivity(String xpath, int sensitivity) { + Map params = new HashMap<>(); + params.put("algorithm", "standard"); + params.put("elementXpath", xpath); + params.put("diffSensitivity", sensitivity); + regions.add(percy.createRegion(params)); + } + + @Given("I create a Percy intelliignore region with XPath {string}") + public void iCreateIntelliIgnoreRegionXPath(String xpath) { + Map params = new HashMap<>(); + params.put("algorithm", "intelliignore"); + params.put("elementXpath", xpath); + regions.add(percy.createRegion(params)); + } + @Given("I clear Percy regions") public void iClearPercyRegions() { regions.clear(); @@ -253,6 +296,27 @@ public void iTakeSnapshotWithTestCase(String name, String testCase) { percy.snapshot(name, options); } + @When("I take a Percy snapshot named {string} with Shadow DOM disabled") + public void iTakeSnapshotWithShadowDomDisabled(String name) { + Map options = new HashMap<>(); + options.put("disableShadowDom", true); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with responsive capture") + public void iTakeSnapshotWithResponsiveCapture(String name) { + Map options = new HashMap<>(); + options.put("responsiveSnapshotCapture", true); + percy.snapshot(name, options); + } + + @When("I take a Percy snapshot named {string} with sync") + public void iTakeSnapshotWithSync(String name) { + Map options = new HashMap<>(); + options.put("sync", true); + percy.snapshot(name, options); + } + @When("I take a Percy snapshot named {string} with regions") public void iTakeSnapshotWithRegions(String name) { Map options = new HashMap<>(); @@ -354,8 +418,12 @@ private static Map buildOptions(Map table) { case "enableLayout": case "disableShadowDom": case "responsiveSnapshotCapture": + case "sync": options.put(key, Boolean.parseBoolean(value)); break; + case "scopeOptions": + options.put(key, new org.json.JSONObject(value).toMap()); + break; default: options.put(key, value); break;