From c9bbc2899eba4e8df66be4709086f9ee045543b0 Mon Sep 17 00:00:00 2001 From: chinmay-browserstack <92926953+chinmay-browserstack@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:41:44 +0530 Subject: [PATCH] Added support for responsive snapshot capture (#289) * Added support for responsive snapshot capture * Add null check * add null check * Make cookie collection fail safe * Fix null pointer while running tests * Fix for null pointer errors * CLI version bump * Addressed comments * Trigger Build * Update readme --- README.md | 4 +- package.json | 2 +- src/main/java/io/percy/selenium/Percy.java | 237 ++++++++++++++--- src/test/java/io/percy/selenium/SdkTest.java | 35 ++- yarn.lock | 256 +++++++++++-------- 5 files changed, 381 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index a08a835..d36d74b 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ $ percy exec -- [java test command] The snapshot method arguments: -`percy.snapshot(name, widths[], minHeight, enableJavaScript, percyCSS, scope)` +`percy.snapshot(name, widths[], minHeight, enableJavaScript, percyCSS, scope, sync, responsiveSnapshotCapture)` - `name` (**required**) - The snapshot name; must be unique to each snapshot - Additional snapshot options (overrides any project options): @@ -104,6 +104,8 @@ The snapshot method arguments: - `percyCSS` - Percy specific CSS only applied in Percy's rendering environment - `scope` - A CSS selector to scope the screenshot to + - `sync` - For getting syncronous results https://www.browserstack.com/docs/percy/advanced/sync-comparison-results + - `responsiveSnapshotCapture` - For capturing snapshot of responsive websites ## Upgrading diff --git a/package.json b/package.json index 1a93593..f33e97a 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,6 @@ "test": "npx percy exec --testing -- mvn test" }, "devDependencies": { - "@percy/cli": "1.28.0" + "@percy/cli": "1.29.5-beta.0" } } diff --git a/src/main/java/io/percy/selenium/Percy.java b/src/main/java/io/percy/selenium/Percy.java index 3c16e5d..7fe919b 100644 --- a/src/main/java/io/percy/selenium/Percy.java +++ b/src/main/java/io/percy/selenium/Percy.java @@ -1,7 +1,5 @@ package io.percy.selenium; -import org.apache.commons.exec.util.StringUtils; -import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.util.EntityUtils; @@ -14,19 +12,21 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.json.JSONObject; +import org.json.JSONArray; -import java.io.InputStream; +import java.time.Duration; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.Nullable; import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.remote.*; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; -import java.lang.reflect.Field; +import java.util.stream.Collectors; /** * Percy client for visual testing. @@ -39,16 +39,20 @@ public class Percy { private String domJs = ""; // Maybe get the CLI server address - private String PERCY_SERVER_ADDRESS = System.getenv().getOrDefault("PERCY_SERVER_ADDRESS", "http://localhost:5338"); + private static String PERCY_SERVER_ADDRESS = System.getenv().getOrDefault("PERCY_SERVER_ADDRESS", "http://localhost:5338"); // Determine if we're debug logging private static boolean PERCY_DEBUG = System.getenv().getOrDefault("PERCY_LOGLEVEL", "info").equals("debug"); + private static String RESONSIVE_CAPTURE_SLEEP_TIME = System.getenv().getOrDefault("RESONSIVE_CAPTURE_SLEEP_TIME", ""); + // for logging private static String LABEL = "[\u001b[35m" + (PERCY_DEBUG ? "percy:java" : "percy") + "\u001b[39m]"; // Type of session automate/web protected String sessionType = null; + protected JSONObject eligibleWidths; + private JSONObject cliConfig; // Is the Percy server running or not private boolean isPercyEnabled = healthcheck(); @@ -78,7 +82,7 @@ public Percy(WebDriver driver) { * */ public JSONObject snapshot(String name) { - return snapshot(name, null, null, false, null, null, null); + return snapshot(name, null, null, false, null, null, null, null); } /** @@ -89,7 +93,7 @@ public JSONObject snapshot(String name) { * pixels. */ public JSONObject snapshot(String name, List widths) { - return snapshot(name, widths, null, false, null, null, null); + return snapshot(name, widths, null, false, null, null, null, null); } /** @@ -101,7 +105,7 @@ public JSONObject snapshot(String name, List widths) { * @param minHeight The minimum height of the resulting snapshot. In pixels. */ public JSONObject snapshot(String name, List widths, Integer minHeight) { - return snapshot(name, widths, minHeight, false, null, null, null); + return snapshot(name, widths, minHeight, false, null, null, null, null); } /** @@ -114,7 +118,7 @@ public JSONObject snapshot(String name, List widths, Integer minHeight) * @param enableJavaScript Enable JavaScript in the Percy rendering environment */ public JSONObject snapshot(String name, List widths, Integer minHeight, boolean enableJavaScript) { - return snapshot(name, widths, minHeight, enableJavaScript, null, null, null); + return snapshot(name, widths, minHeight, enableJavaScript, null, null, null, null); } /** @@ -128,7 +132,7 @@ public JSONObject snapshot(String name, List widths, Integer minHeight, * @param percyCSS Percy specific CSS that is only applied in Percy's browsers */ public JSONObject snapshot(String name, @Nullable List widths, Integer minHeight, boolean enableJavaScript, String percyCSS) { - return snapshot(name, widths, minHeight, enableJavaScript, percyCSS, null, null); + return snapshot(name, widths, minHeight, enableJavaScript, percyCSS, null, null, null); } /** @@ -143,20 +147,14 @@ public JSONObject snapshot(String name, @Nullable List widths, Integer * @param scope A CSS selector to scope the screenshot to */ public JSONObject snapshot(String name, @Nullable List widths, Integer minHeight, boolean enableJavaScript, String percyCSS, String scope) { - if (!isPercyEnabled) { return null; } - - Map domSnapshot = null; - Map options = new HashMap(); - options.put("widths", widths); - options.put("minHeight", minHeight); - options.put("enableJavaScript", enableJavaScript); - options.put("percyCSS", percyCSS); - options.put("scope", scope); - - return snapshot(name, options); + return snapshot(name, widths, minHeight, enableJavaScript, percyCSS, scope, null, null); } public JSONObject snapshot(String name, @Nullable List widths, Integer minHeight, boolean enableJavaScript, String percyCSS, String scope, @Nullable Boolean sync) { + return snapshot(name, widths, minHeight, enableJavaScript, percyCSS, scope, sync, null); + } + + public JSONObject snapshot(String name, @Nullable List widths, Integer minHeight, boolean enableJavaScript, String percyCSS, String scope, @Nullable Boolean sync, Boolean responsiveSnapshotCapture) { if (!isPercyEnabled) { return null; } Map domSnapshot = null; @@ -167,23 +165,53 @@ public JSONObject snapshot(String name, @Nullable List widths, Integer options.put("percyCSS", percyCSS); options.put("scope", scope); options.put("sync", sync); + options.put("responsiveSnapshotCapture", responsiveSnapshotCapture); return snapshot(name, options); } + private boolean isCaptureResponsiveDOM(Map options) { + if (cliConfig.has("percy") && !cliConfig.isNull("percy")) { + JSONObject percyProperty = cliConfig.getJSONObject("percy"); + + if (percyProperty.has("deferUploads") && !percyProperty.isNull("deferUploads") && percyProperty.getBoolean("deferUploads")) { + return false; + } + } + + boolean responsiveSnapshotCaptureCLI = false; + if (eligibleWidths == null) { return false; } + if (cliConfig.getJSONObject("snapshot").has("responsiveSnapshotCapture")) { + responsiveSnapshotCaptureCLI = cliConfig.getJSONObject("snapshot").getBoolean("responsiveSnapshotCapture"); + } + Object responsiveSnapshotCaptureSDK = options.get("responsiveSnapshotCapture"); + + return (responsiveSnapshotCaptureSDK != null && (boolean) responsiveSnapshotCaptureSDK) || responsiveSnapshotCaptureCLI; + } + public JSONObject snapshot(String name, Map options) { if (!isPercyEnabled) { return null; } if ("automate".equals(sessionType)) { throw new RuntimeException("Invalid function call - snapshot(). Please use screenshot() function while using Percy with Automate. For more information on usage of PercyScreenshot, refer https://www.browserstack.com/docs/percy/integrate/functional-and-visual"); } - Map domSnapshot = null; + Object domSnapshot = null; try { JavascriptExecutor jse = (JavascriptExecutor) driver; jse.executeScript(fetchPercyDOM()); - domSnapshot = (Map) jse.executeScript(buildSnapshotJS(options)); + Set cookies = new HashSet<>(); + try { + cookies = driver.manage().getCookies(); + } catch(Exception e) { + log("Cookie collection failed " + e.getMessage(), "debug"); + } + if (isCaptureResponsiveDOM(options)) { + domSnapshot = captureResponsiveDom(driver, cookies, options); + } else { + domSnapshot = getSerializedDOM(jse, cookies, options); + } } catch (WebDriverException e) { // For some reason, the execution in the browser failed. - if (PERCY_DEBUG) { log(e.getMessage()); } + log(e.getMessage(), "debug"); } return postSnapshot(domSnapshot, name, driver.getCurrentUrl(), options); @@ -290,12 +318,14 @@ private boolean healthcheck() { String responseString = EntityUtils.toString(entity, "UTF-8"); JSONObject responseObject = new JSONObject(responseString); sessionType = (String) responseObject.optString("type", null); + eligibleWidths = responseObject.optJSONObject("widths"); + cliConfig = responseObject.optJSONObject("config"); return true; } catch (Exception ex) { log("Percy is not running, disabling snapshots"); // bike shed.. single line? - if (PERCY_DEBUG) { log(ex.toString()); } + log(ex.toString(), "debug"); return false; } @@ -326,7 +356,7 @@ private String fetchPercyDOM() { return domString; } catch (Exception ex) { isPercyEnabled = false; - if (PERCY_DEBUG) { log(ex.toString()); } + log(ex.toString(), "debug"); return ""; } @@ -344,7 +374,7 @@ private String fetchPercyDOM() { * @param percyCSS Percy specific CSS that is only applied in Percy's browsers */ private JSONObject postSnapshot( - Map domSnapshot, + Object domSnapshot, String name, String url, Map options @@ -390,7 +420,7 @@ protected JSONObject request(String url, JSONObject json, String name) { return jsonResponse.getJSONObject("data"); } } catch (Exception ex) { - if (PERCY_DEBUG) { log(ex.toString()); } + log(ex.toString(), "debug"); log("Could not post snapshot " + name); } return null; @@ -408,6 +438,14 @@ private String buildSnapshotJS(Map options) { return jsBuilder.toString(); } + private Map getSerializedDOM(JavascriptExecutor jse, Set cookies, Map options) { + Map domSnapshot = (Map) jse.executeScript(buildSnapshotJS(options)); + Map mutableSnapshot = new HashMap<>(domSnapshot); + mutableSnapshot.put("cookies", cookies); + + return mutableSnapshot; + } + private List getElementIdFromElement(List elements) { List ignoredElementsArray = new ArrayList<>(); for (int index = 0; index < elements.size(); index++) { @@ -417,7 +455,144 @@ private List getElementIdFromElement(List elements) { return ignoredElementsArray; } + // Get widths for multi DOM + private List getWidthsForMultiDom(Map options) { + List widths; + if (options.containsKey("widths") && options.get("widths") instanceof List) { + widths = (List) options.get("widths"); + } else { + widths = new ArrayList<>(); + } + // Create a Set to avoid duplicates + Set allWidths = new HashSet<>(); + + JSONArray mobileWidths = eligibleWidths.getJSONArray("mobile"); + for (int i = 0; i < mobileWidths.length(); i++) { + allWidths.add(mobileWidths.getInt(i)); + } + + // Add input widths if provided + if (widths.size() != 0) { + for (int width : widths) { + allWidths.add(width); + } + } else { + // Add config widths if no input widths are provided + JSONArray configWidths = eligibleWidths.getJSONArray("config"); + for (int i = 0; i < configWidths.length(); i++) { + allWidths.add(configWidths.getInt(i)); + } + } + + // Convert Set back to List + return allWidths.stream().collect(Collectors.toList()); + } + + // Method to check if ChromeDriver supports CDP by checking the existence of executeCdpCommand + private static boolean isCdpSupported(ChromeDriver chromeDriver) { + try { + chromeDriver.getClass().getMethod("executeCdpCommand", String.class, Map.class); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + // Change window dimensions and wait for the resize event + private static void changeWindowDimensionAndWait(WebDriver driver, int width, int height, int resizeCount) { + try { + if (driver instanceof ChromeDriver && isCdpSupported((ChromeDriver) driver)) { + Map commandParams = new HashMap<>(); + commandParams.put("width", width); + commandParams.put("height", height); + commandParams.put("deviceScaleFactor", 1); + commandParams.put("mobile", false); + + ((ChromeDriver) driver).executeCdpCommand("Emulation.setDeviceMetricsOverride", commandParams); + } else { + driver.manage().window().setSize(new Dimension(width, height)); + } + } catch (Exception e) { + log("Resizing using CDP failed, falling back to driver for width " + width + ": " + e.getMessage(), "debug"); + driver.manage().window().setSize(new Dimension(width, height)); + } + + // Wait for window resize event using WebDriverWait + try { + WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1)); + wait.until((ExpectedCondition) d -> + (long) ((JavascriptExecutor) d).executeScript("return window.resizeCount") == resizeCount); + } catch (WebDriverException e) { + log("Timed out waiting for window resize event for width " + width, "debug"); + } + } + + // Capture responsive DOM for different widths + public List> captureResponsiveDom(WebDriver driver, Set cookies, Map options) { + List widths = getWidthsForMultiDom(options); + + List> domSnapshots = new ArrayList<>(); + + Dimension windowSize = driver.manage().window().getSize(); + int currentWidth = windowSize.getWidth(); + int currentHeight = windowSize.getHeight(); + int lastWindowWidth = currentWidth; + int resizeCount = 0; + JavascriptExecutor jse = (JavascriptExecutor) driver; + + // Inject JS to count window resize events + jse.executeScript("PercyDOM.waitForResize()"); + + for (int width : widths) { + if (lastWindowWidth != width) { + resizeCount++; + changeWindowDimensionAndWait(driver, width, currentHeight, resizeCount); + lastWindowWidth = width; + } + + try { + int sleepTime = Integer.parseInt(RESONSIVE_CAPTURE_SLEEP_TIME); + Thread.sleep(sleepTime * 1000); // Sleep if needed + } catch (InterruptedException | NumberFormatException ignored) { + } + Map domSnapshot = getSerializedDOM(jse, cookies, options); + domSnapshot.put("width", width); + domSnapshots.add(domSnapshot); + } + + // Revert to the original window size + changeWindowDimensionAndWait(driver, currentWidth, currentHeight, resizeCount + 1); + + return domSnapshots; + } + protected static void log(String message) { - System.out.println(LABEL + " " + message); + log(message, "info"); + } + + protected static void log(String message, String level) { + message = LABEL + " " + message; + String logJsonString = "{\"message\": \"" + message + "\", \"level\": \"" + level + "\"}"; + StringEntity entity = new StringEntity(logJsonString, ContentType.APPLICATION_JSON); + int timeout = 1000; // 1 second + + // Create RequestConfig with timeout + RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(timeout) + .setConnectTimeout(timeout) + .build(); + + try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build()) { + HttpPost request = new HttpPost(PERCY_SERVER_ADDRESS + "/percy/log"); + request.setEntity(entity); + httpClient.execute(request); + } catch (Exception ex) { + if (PERCY_DEBUG) { System.out.println("Sending log to CLI Failed " + ex.toString()); } + } finally { + // Only log if level is not 'debug' or PERCY_DEBUG is true + if (!"debug".equals(level) || PERCY_DEBUG) { + System.out.println(message); + } + } } } diff --git a/src/test/java/io/percy/selenium/SdkTest.java b/src/test/java/io/percy/selenium/SdkTest.java index 68beced..9619c8d 100644 --- a/src/test/java/io/percy/selenium/SdkTest.java +++ b/src/test/java/io/percy/selenium/SdkTest.java @@ -134,6 +134,21 @@ public void takeSnapshotWithSyncCLI(){ assertEquals(data.get("screenshots").getClass().isAssignableFrom(JSONArray.class), true); } + @Test + public void snapshotWithResponsiveSnapshotCapture() { + // To run via test via chrome CDP uncomment below lines and replace chromedriver path +// System.setProperty("webdriver.chrome.driver", ""); +// ChromeOptions chromeOptions = new ChromeOptions(); +// chromeOptions.addArguments("--remote-allow-origins=*"); +// driver = new ChromeDriver(chromeOptions); + + driver.get("https://www.webfx.com/tools/whats-my-browser-size/"); + Map options = new HashMap(); + options.put("widths", Arrays.asList(768, 992, 1200)); + options.put("responsiveSnapshotCapture", true); + percy.snapshot("Site with snapshotWithResponsiveSnapshotCapture", options); + } + @Test public void takeScreenshot() { RemoteWebDriver mockedDriver = mock(RemoteWebDriver.class); @@ -142,15 +157,15 @@ public void takeScreenshot() { when(commandExecutor.getAddressOfRemoteServer()).thenReturn(new URL("https://hub-cloud.browserstack.com/wd/hub")); } catch (Exception e) { } - percy = spy(new Percy(mockedDriver)); - percy.sessionType = "automate"; + Percy mockedPercy = spy(new Percy(mockedDriver)); + mockedPercy.sessionType = "automate"; when(mockedDriver.getSessionId()).thenReturn(new SessionId("123")); when(mockedDriver.getCommandExecutor()).thenReturn(commandExecutor); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("browserName", "Chrome"); when(mockedDriver.getCapabilities()).thenReturn(capabilities); - percy.screenshot("Test"); - verify(percy).request(eq("/percy/automateScreenshot"), any(), eq("Test")); + mockedPercy.screenshot("Test"); + verify(mockedPercy).request(eq("/percy/automateScreenshot"), any(), eq("Test")); } @Test @@ -161,8 +176,8 @@ public void takeScreenshotWithOptions() { when(commandExecutor.getAddressOfRemoteServer()).thenReturn(new URL("https://hub-cloud.browserstack.com/wd/hub")); } catch (Exception e) { } - percy = spy(new Percy(mockedDriver)); - percy.sessionType = "automate"; + Percy mockedPercy = spy(new Percy(mockedDriver)); + mockedPercy.sessionType = "automate"; when(mockedDriver.getSessionId()).thenReturn(new SessionId("123")); when(mockedDriver.getCommandExecutor()).thenReturn(commandExecutor); DesiredCapabilities capabilities = new DesiredCapabilities(); @@ -174,8 +189,8 @@ public void takeScreenshotWithOptions() { when(mockedElement.getId()).thenReturn("1234"); when(mockedConsiderElement.getId()).thenReturn("5678"); options.put("ignore_region_selenium_elements", Arrays.asList(mockedElement)); - percy.screenshot("Test", options); - verify(percy).request(eq("/percy/automateScreenshot"), any() , eq("Test")); + mockedPercy.screenshot("Test", options); + verify(mockedPercy).request(eq("/percy/automateScreenshot"), any() , eq("Test")); } @Test @@ -188,8 +203,8 @@ public void takeSnapshotThrowErrorForPOA() { @Test public void takeScreenshotThrowErrorForWeb() { RemoteWebDriver mockedDriver = mock(RemoteWebDriver.class); - percy = spy(new Percy(mockedDriver)); - Throwable exception = assertThrows(RuntimeException.class, () -> percy.screenshot("Test")); + Percy mockedPercy = spy(new Percy(mockedDriver)); + Throwable exception = assertThrows(RuntimeException.class, () -> mockedPercy.screenshot("Test")); assertEquals("Invalid function call - screenshot(). Please use snapshot() function for taking screenshot. screenshot() should be used only while using Percy with Automate. For more information on usage of snapshot(), refer doc for your language https://www.browserstack.com/docs/percy/integrate/overview", exception.getMessage()); } } diff --git a/yarn.lock b/yarn.lock index ac5291c..24448e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,145 +44,149 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" -"@percy/cli-app@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-app/-/cli-app-1.27.7.tgz#7ee373674ab8e0a5aec33f624390097a76c53ad6" - integrity sha512-IQuTC5X5JM65cqOluFM9lnydkw3hvkUdf6LTsBRtzmS7/qURyiBDvuxkKt37XUCfXm2XFP8J6B6ADBQL4YShPA== +"@percy/cli-app@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-app/-/cli-app-1.29.5-beta.0.tgz#d018dc6c995b779c1596260013b3e2c673f2038e" + integrity sha512-nGfMHTH+7Zm8zhulhS7p/MbyNkMResy+wBalP2hxPGnFSWl5npxdepABYMmIDqMRJbmcWawj3xJQgmdWaBKuCw== dependencies: - "@percy/cli-command" "1.27.7" - "@percy/cli-exec" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" + "@percy/cli-exec" "1.29.5-beta.0" -"@percy/cli-build@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.27.7.tgz#7d8a200353ec7a3ec32131631e2e5145e53b357d" - integrity sha512-UmWaUFQAomlL4GpU/KPTq3hmJtXCMWQELwDnaQ1vnUf2rk17qSy5keSvCb/Ta8oPuObuRNoOHGrtUFPh1b7l2g== +"@percy/cli-build@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.29.5-beta.0.tgz#c9b331d8719ccb7762fe8f90848445db41d29cb6" + integrity sha512-fHbGAHKXx3HlboAX/fRumocxVQb7/cPxAbabAqRhT4cW0l8Ci9lspFkLpF4ow5KGAeU4y7/4S+DMuF+vK7uVIQ== dependencies: - "@percy/cli-command" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" -"@percy/cli-command@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.27.7.tgz#2341db97a7ddebda9587ba2e3efbcb21e0b06a43" - integrity sha512-nVHspbcTStwZOVYmcCowcFJsOLLJrhLfJbj2Rkvm3upf/E98x4+S9H+Tq9K4XcgznjFfLLRAHkkVFGnCdbO5qQ== +"@percy/cli-command@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.29.5-beta.0.tgz#b314aaf36ee660d3e0013d593249bde4c86da1ed" + integrity sha512-yHJCE6kbf2jslTx76eOu6yd+k5InQOR4bVh9MacMgxcfPPWqQf0MYAsNCB6lpvnHKfeD+doVDhM7tWMj1DjfuA== dependencies: - "@percy/config" "1.27.7" - "@percy/core" "1.27.7" - "@percy/logger" "1.27.7" + "@percy/config" "1.29.5-beta.0" + "@percy/core" "1.29.5-beta.0" + "@percy/logger" "1.29.5-beta.0" -"@percy/cli-config@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.27.7.tgz#55f1046807849925e8fc94dc94c08afae103a99e" - integrity sha512-SppI7w9xQAUk4nCqXq0KE3HoKZ+IwgkJ6biAQuzv3BJ8GWz42hUGnH1fLEoK92MZMLiOHByiEFUoBzUKXPscZw== +"@percy/cli-config@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.29.5-beta.0.tgz#24e7e7b1d9678450d1f510ff066ff59d3e956aba" + integrity sha512-+5gHOW3xjeVInKktn/y7TU1n4/Zfr8Kh9AemK17GDQy39M6mGvH/a49xsjmWoV21AmNttFzeBHXbkKl75RNH2g== dependencies: - "@percy/cli-command" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" -"@percy/cli-exec@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.27.7.tgz#6122111a59f888e37e3f0b465cb1f7f80f217b1b" - integrity sha512-ZYSuQYu7hMbl/KbGwS/bEdAEXEY7MygheCJrmM43AGUIj8LSHnVTi09/z7ZpcStLibPmsraJ8Cm7GXhTNdhuAQ== +"@percy/cli-exec@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.29.5-beta.0.tgz#c38e28cc09e01421aeab514143a9f0bf5eb9c268" + integrity sha512-JZk2tRtjKO0fgiN0m+YoD23DsX1OY7r1n1KfVrxojK2pFt4AD6RkLPLZVzUlze7pm+H1O5/8MbB4nXQEDgSsOQ== dependencies: - "@percy/cli-command" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" + "@percy/logger" "1.29.5-beta.0" cross-spawn "^7.0.3" which "^2.0.2" -"@percy/cli-snapshot@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.27.7.tgz#ac64af3b02bade251a0efc6e79663a7d3af245c7" - integrity sha512-FGmNhXfOvW2RkcdEzeWWXo2fUp8UoRIb8bT0Bg6xcw5UGPUAoDVaFA8i4M5Qvi/K54Ed5VYfBX3y5sLcg2XRaw== +"@percy/cli-snapshot@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.29.5-beta.0.tgz#a6e87305030be8fc0f5ed966f01da071b998f619" + integrity sha512-U61KMgzBzbFs06HoO+cbMRvbIHVzVxm+B2YHbWdQZOhZ8cwP+qPNv4SUZtTlH8z2YoYBXi7GXdFyOFCQ8r/2Aw== dependencies: - "@percy/cli-command" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" yaml "^2.0.0" -"@percy/cli-upload@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.27.7.tgz#9ef454e81e3a286a147c7841bd521c8459e99cff" - integrity sha512-kC9wfZ6TBnOTlB2ge32IlEQlxGh+fLXUIlmelCXCTDL6zutn8mI117XOHaJDtsI7qtovM0cTBxoSySaBY4XNqg== +"@percy/cli-upload@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.29.5-beta.0.tgz#ea356cf2945307e05651992e63628d016883c40f" + integrity sha512-v2DARkukue3rEhO0rApKAQDjnjpRX4bxJcmEkZo70r8oQBlvTlixX9O/5TLXAq3WI++H4szTPCqbO68TcTgr9g== dependencies: - "@percy/cli-command" "1.27.7" + "@percy/cli-command" "1.29.5-beta.0" fast-glob "^3.2.11" image-size "^1.0.0" -"@percy/cli@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.27.7.tgz#004de5827790c827eac9195dae6914e9868736b3" - integrity sha512-FKt032Of8IhzA/ZLGAWgox5y5N534lTXW+W6AEeeMPHDm06eHCPgbMuMjajuhD0nGFRtSO6rJIPw+gFzIgJU7g== - dependencies: - "@percy/cli-app" "1.27.7" - "@percy/cli-build" "1.27.7" - "@percy/cli-command" "1.27.7" - "@percy/cli-config" "1.27.7" - "@percy/cli-exec" "1.27.7" - "@percy/cli-snapshot" "1.27.7" - "@percy/cli-upload" "1.27.7" - "@percy/client" "1.27.7" - "@percy/logger" "1.27.7" - -"@percy/client@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.27.7.tgz#79513a55f56b2693b601f8cb55f6850f7dadfcf8" - integrity sha512-pMaimVhfJEWyta40PyanH/Sv8mp+CBeO4HuJ72zadDrJVAwNNBfaNE8OzT298y8u/DrMz/AlNSA6OhVojDVAuQ== - dependencies: - "@percy/env" "1.27.7" - "@percy/logger" "1.27.7" - -"@percy/config@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.27.7.tgz#1185344d00a4bcfc8f61eb5fa2ecc05e667b9667" - integrity sha512-7XbnrVWgvMgYAIngyWt0nd/l5lQiioOzIkqm9iH+nbfH/ka9pE8U/AWsCuiWGz2Z/1p03gBDFc01cyM+ORjglQ== - dependencies: - "@percy/logger" "1.27.7" +"@percy/cli@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.29.5-beta.0.tgz#ce834457f45312d5549d46fe4163b99a52e55251" + integrity sha512-5QUn29b4RSw+v5HLVYNmUMdKL04lJaii8wLG54sy0ey4dUG24FGU5OFPx+Ew2+TtpI+FuktkibXUBeh+r39ZiQ== + dependencies: + "@percy/cli-app" "1.29.5-beta.0" + "@percy/cli-build" "1.29.5-beta.0" + "@percy/cli-command" "1.29.5-beta.0" + "@percy/cli-config" "1.29.5-beta.0" + "@percy/cli-exec" "1.29.5-beta.0" + "@percy/cli-snapshot" "1.29.5-beta.0" + "@percy/cli-upload" "1.29.5-beta.0" + "@percy/client" "1.29.5-beta.0" + "@percy/logger" "1.29.5-beta.0" + +"@percy/client@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.29.5-beta.0.tgz#40284971db505523a662e26b36d61b3ee68a87e9" + integrity sha512-MWNwpckVG0WG5wxBT7gz7hcuXfqK16l0BXyyz4p5HTt1WEm2z5nKwHkiOEq6vPY/Sne2O/dEh+yzzdGjxfzQ2w== + dependencies: + "@percy/env" "1.29.5-beta.0" + "@percy/logger" "1.29.5-beta.0" + pako "^2.1.0" + +"@percy/config@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.29.5-beta.0.tgz#77be74016f375303f0202518eba36061d5754bb9" + integrity sha512-gMvQRirNKfo2ZzXo7Qx+CFPNT3gNZDMaTwwQnZ1YTVgdJql0OKWa/8VtT0bTCdeKhOBfEcPc7qgm8r+RDOtmnQ== + dependencies: + "@percy/logger" "1.29.5-beta.0" ajv "^8.6.2" cosmiconfig "^8.0.0" yaml "^2.0.0" -"@percy/core@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.27.7.tgz#daef80d280f117f8122264fae33be41ca8d62caa" - integrity sha512-lwweKGLbjsCWcAvaeHSK47IkaioBPKKjKd8fhhXFfv713aNL5CcokO9uUNMyBwrgqLY8jmPR/McMtROidUSowQ== +"@percy/core@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.29.5-beta.0.tgz#968087f2d3dd263e0c006595240b87fa18ecf40d" + integrity sha512-VgHnX0HGGOMW3022EbCmdn8yKq781QJ2ORB2JCTSVzuD2ObhZcVl8Ijl0sRBud2S6TXMwchcIycBb1XVgZeDXw== dependencies: - "@percy/client" "1.27.7" - "@percy/config" "1.27.7" - "@percy/dom" "1.27.7" - "@percy/logger" "1.27.7" - "@percy/webdriver-utils" "1.27.7" + "@percy/client" "1.29.5-beta.0" + "@percy/config" "1.29.5-beta.0" + "@percy/dom" "1.29.5-beta.0" + "@percy/logger" "1.29.5-beta.0" + "@percy/webdriver-utils" "1.29.5-beta.0" content-disposition "^0.5.4" cross-spawn "^7.0.3" extract-zip "^2.0.1" fast-glob "^3.2.11" - micromatch "^4.0.4" + micromatch "^4.0.8" mime-types "^2.1.34" - path-to-regexp "^6.2.0" + pako "^2.1.0" + path-to-regexp "^6.3.0" rimraf "^3.0.2" - ws "^8.0.0" + ws "^8.17.1" + yaml "^2.4.1" -"@percy/dom@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.27.7.tgz#ea4b790e72a86ae5b8ba0cc8ebc66adee2d04ee1" - integrity sha512-FLMaAp7LByGFqvTEV1UwiQd+Ww1SHU2UuGOvgMPBwPgAKt2BB+FfZEYgaeORt5YXLfDJsIVk/lfvhJXzmb5k8Q== +"@percy/dom@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.29.5-beta.0.tgz#999a9bdd8346c5beb941b0789793af6db563c0a2" + integrity sha512-nf5NDvcHRaXXwM6VyMhI8SZJNaSleM6gXiqqtjIO2DyEMCJvrVvBQTIn6eqmjjU7tZApSUPs4Dm+BQSelhmngA== -"@percy/env@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.27.7.tgz#bc465c2ea64661ab0f2731cb351595c99a296c42" - integrity sha512-TWgcxmRrcxwObc+kXfoh2eAywmB+6OGm9jxmf6wgdTf49zF70iS8mG6mJ7+r+PLz4MEmRCUTJ2S5I83iU6rLeQ== +"@percy/env@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.29.5-beta.0.tgz#60845e22624457da377f5b2ea771a1241e8b06de" + integrity sha512-5vtdmrQHdGISsjXC4mnvO9EviPy1oVcTF/IcP5XYdp+JYYttKabYw5U51dP4XnG87JW3EmT0lTYLhPU98lfguQ== dependencies: - "@percy/logger" "1.27.7" + "@percy/logger" "1.29.5-beta.0" -"@percy/logger@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.27.7.tgz#7d094c0a67defaeec5b0257cf6930e8a7a9adb04" - integrity sha512-w+r9tJPEUSflCPVajx15yfFtdaxjiqiWIu6i4sHKGwg0B24ivVb+6e1J5R2ry19F/YPjBi6llz5BRB9vasu+Iw== +"@percy/logger@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.29.5-beta.0.tgz#c446c7818c97644bd9d6155ce1b2dab5be3509fe" + integrity sha512-SNWLzYnYpEXsqkdzyqISEtkJi0wTbQmTzyXbcrfFVHCiQy8ZIQ4SoSqZkFjDBBa14DfAWKXJw935auZzKUSfUw== -"@percy/sdk-utils@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/sdk-utils/-/sdk-utils-1.27.7.tgz#b376c1ad653f3c70fea08a3db11eb63622ca6b53" - integrity sha512-E21dIEQ9wwGDno41FdMDYf6jJow5scbWGClqKE/ptB+950W4UF5C4hxhVVQoEJxDdLE/Gy/8ZJR7upvPHShWDg== +"@percy/sdk-utils@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/sdk-utils/-/sdk-utils-1.29.5-beta.0.tgz#671bac3c47f30294cc165b42c56030311d757573" + integrity sha512-SF4hpa8k+bjgEMpTu0WZIxY/fl3GXgGGbbAyWo9OXiHYMHu98TkP5zTFtjNFKaESIKuKNbutMnEAqJvh6ybnew== -"@percy/webdriver-utils@1.27.7": - version "1.27.7" - resolved "https://registry.yarnpkg.com/@percy/webdriver-utils/-/webdriver-utils-1.27.7.tgz#5c74452f6763b90aee4b713f21da2672bae1d28d" - integrity sha512-8nAQJOhphTsaTAqKfcDC/MDf/yJvJ0Hf8Owc90pisXakooEMNnAW4HzQlZIh4kyJQbw4iX9OFl6gVl+POTWhHQ== +"@percy/webdriver-utils@1.29.5-beta.0": + version "1.29.5-beta.0" + resolved "https://registry.yarnpkg.com/@percy/webdriver-utils/-/webdriver-utils-1.29.5-beta.0.tgz#fa0a1eee8a8ab9bfb9eb9729cbc6a7abc07b0eeb" + integrity sha512-1SHRUCbUn6RN9wgDScjrksRltGDK0QbkaJ0H91K5vKWP3AXdi+Nux7XOVfVhuIPj9cUU0Y9FLo5F1/wJCZ7ezA== dependencies: - "@percy/config" "1.27.7" - "@percy/sdk-utils" "1.27.7" + "@percy/config" "1.29.5-beta.0" + "@percy/sdk-utils" "1.29.5-beta.0" "@types/node@*": version "12.7.5" @@ -238,6 +242,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -374,6 +385,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -505,6 +523,14 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.51.0: version "1.51.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" @@ -536,6 +562,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +pako@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -563,10 +594,10 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-to-regexp@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" - integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== +path-to-regexp@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== path-type@^4.0.0: version "4.0.0" @@ -680,16 +711,21 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.0.0.tgz#550605d13dfc1437c9ec1396975709c6d7ffc57d" - integrity sha512-6AcSIXpBlS0QvCVKk+3cWnWElLsA6SzC0lkQ43ciEglgXJXiCWK3/CGFEJ+Ybgp006CMibamAsqOlxE9s4AvYA== +ws@^8.17.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== yaml@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.1.tgz#71886d6021f3da28169dbefde78d4dd0f8d83650" integrity sha512-1NpAYQ3wjzIlMs0mgdBmYzLkFgWBIWrzYVDYfrixhoFNNgJ444/jT2kUT2sicRbJES3oQYRZugjB6Ro8SjKeFg== +yaml@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130" + integrity sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"