diff --git a/bellatrix.addons.visual-regression-tracker/pom.xml b/bellatrix.addons.visual-regression-tracker/pom.xml new file mode 100644 index 00000000..d8516471 --- /dev/null +++ b/bellatrix.addons.visual-regression-tracker/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + solutions.bellatrix + bellatrix + 1.0-SNAPSHOT + + + bellatrix.addons.visual-regression-tracker + + + + jitpack.io + https://jitpack.io + + + + + + solutions.bellatrix + bellatrix.core + 1.0 + + + solutions.bellatrix + bellatrix.plugins.screenshots + 1.0 + + + com.github.Visual-Regression-Tracker + sdk-java + 5.2.1 + + + + + 19 + 19 + UTF-8 + + + \ No newline at end of file diff --git a/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTracker.java b/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTracker.java new file mode 100644 index 00000000..b4a9142f --- /dev/null +++ b/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTracker.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Automate The Planet Ltd. + * Author: Miriam Kyoseva + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package solutions.bellatrix.plugins.vrt; + +import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import plugins.screenshots.ScreenshotPlugin; +import solutions.bellatrix.core.configuration.ConfigurationService; +import solutions.bellatrix.core.utilities.SingletonFactory; + +import java.util.Objects; + +@UtilityClass +public class VisualRegressionTracker { + private static final VisualRegressionTrackerSettings settings = ConfigurationService.get(VisualRegressionTrackerSettings.class); + private static final VisualRegressionTrackerConfig config = new VisualRegressionTrackerConfig( + settings.getApiUrl(), + settings.getApiKey(), + settings.getProject(), + settings.getBranch(), + settings.getCiBuildId(), + settings.isEnableSoftAssert(), + settings.getHttpTimeout() + ); + private static io.visual_regression_tracker.sdk_java.VisualRegressionTracker tracker; + + static { + tracker = new io.visual_regression_tracker.sdk_java.VisualRegressionTracker(config); + } + + @SneakyThrows + public static void takeSnapshot() { + var caller = Thread.currentThread().getStackTrace()[1]; // gets the caller method of screenshot() + var name = caller.getMethodName(); + + tracker.track(name, Objects.requireNonNull(SingletonFactory.getInstance(ScreenshotPlugin.class)).takeScreenshot(name)); + } + + @SneakyThrows + public static void takeSnapshot(String name) { + tracker.track(name, Objects.requireNonNull(SingletonFactory.getInstance(ScreenshotPlugin.class)).takeScreenshot(name)); + } +} diff --git a/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTrackerSettings.java b/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTrackerSettings.java new file mode 100644 index 00000000..7369f03a --- /dev/null +++ b/bellatrix.addons.visual-regression-tracker/src/main/java/solutions/bellatrix/plugins/vrt/VisualRegressionTrackerSettings.java @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Automate The Planet Ltd. + * Author: Miriam Kyoseva + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package solutions.bellatrix.plugins.vrt; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter @Setter @NoArgsConstructor +public class VisualRegressionTrackerSettings { + private String apiUrl; + private String project; + private String apiKey; + private String branch; + private boolean enableSoftAssert; + private String ciBuildId; + private int httpTimeout; +} diff --git a/bellatrix.android/src/main/java/solutions/bellatrix/android/infrastructure/MobileScreenshotPlugin.java b/bellatrix.android/src/main/java/solutions/bellatrix/android/infrastructure/MobileScreenshotPlugin.java index 89b74937..61250771 100644 --- a/bellatrix.android/src/main/java/solutions/bellatrix/android/infrastructure/MobileScreenshotPlugin.java +++ b/bellatrix.android/src/main/java/solutions/bellatrix/android/infrastructure/MobileScreenshotPlugin.java @@ -18,11 +18,14 @@ import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import plugins.screenshots.ScreenshotPlugin; +import plugins.screenshots.ScreenshotPluginEventArgs; import solutions.bellatrix.android.configuration.AndroidSettings; import solutions.bellatrix.core.configuration.ConfigurationService; import solutions.bellatrix.core.utilities.PathNormalizer; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.file.Paths; import java.util.UUID; @@ -33,10 +36,43 @@ public MobileScreenshotPlugin() { @Override @SneakyThrows - protected void takeScreenshot(String screenshotSaveDir, String filename) { - File screenshot = ((TakesScreenshot)DriverService.getWrappedAndroidDriver()).getScreenshotAs(OutputType.FILE); - var destFile = new File(Paths.get(screenshotSaveDir, filename) + ".png"); - FileUtils.copyFile(screenshot, destFile); + public String takeScreenshot(String name) { + var screenshotSaveDir = getOutputFolder(); + var filename = getUniqueFileName(name); + + var screenshot = ((TakesScreenshot)DriverService.getWrappedAndroidDriver()).getScreenshotAs(OutputType.BASE64); + + var path = Paths.get(screenshotSaveDir, filename) + ".png"; + + var file = new File(path); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; + } + + @Override + @SneakyThrows + public String takeScreenshot(String screenshotSaveDir, String filename) { + var screenshot = ((TakesScreenshot)DriverService.getWrappedAndroidDriver()).getScreenshotAs(OutputType.BASE64); + + var path = Paths.get(screenshotSaveDir, filename) + ".png"; + + var file = new File(path); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; } @Override diff --git a/bellatrix.desktop/src/main/java/solutions/bellatrix/desktop/infrastructure/DesktopScreenshotPlugin.java b/bellatrix.desktop/src/main/java/solutions/bellatrix/desktop/infrastructure/DesktopScreenshotPlugin.java index ffafa2ed..b8c95926 100644 --- a/bellatrix.desktop/src/main/java/solutions/bellatrix/desktop/infrastructure/DesktopScreenshotPlugin.java +++ b/bellatrix.desktop/src/main/java/solutions/bellatrix/desktop/infrastructure/DesktopScreenshotPlugin.java @@ -18,10 +18,14 @@ import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import plugins.screenshots.ScreenshotPlugin; +import plugins.screenshots.ScreenshotPluginEventArgs; import solutions.bellatrix.core.configuration.ConfigurationService; import solutions.bellatrix.desktop.configuration.DesktopSettings; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.UUID; @@ -32,10 +36,41 @@ public DesktopScreenshotPlugin() { @Override @SneakyThrows - protected void takeScreenshot(String screenshotSaveDir, String filename) { - File screenshot = ((TakesScreenshot)DriverService.getWrappedDriver()).getScreenshotAs(OutputType.FILE); - var destFile = new File(Paths.get(screenshotSaveDir, filename) + ".png"); - FileUtils.copyFile(screenshot, destFile); + public String takeScreenshot(String name) { + var screenshotSaveDir = getOutputFolder(); + var filename = getUniqueFileName(name); + + var screenshot = ((TakesScreenshot)DriverService.getWrappedDriver()).getScreenshotAs(OutputType.BASE64); + Path path = Paths.get(screenshotSaveDir, filename); + + var file = new File(path + ".png"); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; + } + + @Override + @SneakyThrows + public String takeScreenshot(String screenshotSaveDir, String filename) { + var screenshot = ((TakesScreenshot)DriverService.getWrappedDriver()).getScreenshotAs(OutputType.BASE64); + Path path = Paths.get(screenshotSaveDir, filename); + + var file = new File(path + ".png"); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; } @Override diff --git a/bellatrix.ios/src/main/java/solutions/bellatrix/ios/infrastructure/MobileScreenshotPlugin.java b/bellatrix.ios/src/main/java/solutions/bellatrix/ios/infrastructure/MobileScreenshotPlugin.java index 12eff289..6e932d0b 100644 --- a/bellatrix.ios/src/main/java/solutions/bellatrix/ios/infrastructure/MobileScreenshotPlugin.java +++ b/bellatrix.ios/src/main/java/solutions/bellatrix/ios/infrastructure/MobileScreenshotPlugin.java @@ -18,11 +18,14 @@ import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import plugins.screenshots.ScreenshotPlugin; +import plugins.screenshots.ScreenshotPluginEventArgs; import solutions.bellatrix.core.configuration.ConfigurationService; import solutions.bellatrix.core.utilities.PathNormalizer; import solutions.bellatrix.ios.configuration.IOSSettings; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.file.Paths; import java.util.UUID; @@ -33,10 +36,39 @@ public MobileScreenshotPlugin() { @Override @SneakyThrows - protected void takeScreenshot(String screenshotSaveDir, String filename) { - File screenshot = ((TakesScreenshot)DriverService.getWrappedIOSDriver()).getScreenshotAs(OutputType.FILE); - var destFile = new File(Paths.get(screenshotSaveDir, filename) + ".png"); - FileUtils.copyFile(screenshot, destFile); + public String takeScreenshot(String name) { + var screenshotSaveDir = getOutputFolder(); + var filename = getUniqueFileName(name); + + var screenshot = ((TakesScreenshot)DriverService.getWrappedIOSDriver()).getScreenshotAs(OutputType.BASE64); + var path = Paths.get(screenshotSaveDir, filename) + ".png"; + var file = new File(path); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; + } + + @Override + @SneakyThrows + public String takeScreenshot(String screenshotSaveDir, String filename) { + var screenshot = ((TakesScreenshot)DriverService.getWrappedIOSDriver()).getScreenshotAs(OutputType.BASE64); + var path = Paths.get(screenshotSaveDir, filename) + ".png"; + var file = new File(path); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(screenshot); + } catch (IOException e) { + e.printStackTrace(); + } + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, screenshot)); + return screenshot; } @Override diff --git a/bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/infrastructure/WebScreenshotPlugin.java b/bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/infrastructure/WebScreenshotPlugin.java index 116b9dba..e8c1ab98 100644 --- a/bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/infrastructure/WebScreenshotPlugin.java +++ b/bellatrix.playwright/src/main/java/solutions/bellatrix/playwright/infrastructure/WebScreenshotPlugin.java @@ -16,11 +16,13 @@ import com.microsoft.playwright.Page; import com.microsoft.playwright.options.ScreenshotType; import plugins.screenshots.ScreenshotPlugin; +import plugins.screenshots.ScreenshotPluginEventArgs; import solutions.bellatrix.core.utilities.PathNormalizer; import solutions.bellatrix.playwright.utilities.Settings; import java.io.File; import java.nio.file.Paths; +import java.util.Base64; import java.util.UUID; public class WebScreenshotPlugin extends ScreenshotPlugin { @@ -29,12 +31,36 @@ public WebScreenshotPlugin() { } @Override - protected void takeScreenshot(String screenshotSaveDir, String filename) { + public String takeScreenshot(String name) { + var screenshotSaveDir = getOutputFolder(); + var filename = getUniqueFileName(name); + + var path = Paths.get(screenshotSaveDir, filename); + var screenshot = PlaywrightService.wrappedBrowser().getCurrentPage() + .screenshot(new Page.ScreenshotOptions() + .setPath(path) + .setType(ScreenshotType.PNG) + ); + + var image = Base64.getEncoder().encodeToString(screenshot); + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, image)); + return image; + } + + @Override + public String takeScreenshot(String screenshotSaveDir, String filename) { + var path = Paths.get(screenshotSaveDir, filename); var screenshot = PlaywrightService.wrappedBrowser().getCurrentPage() .screenshot(new Page.ScreenshotOptions() - .setPath(Paths.get(screenshotSaveDir, filename)) + .setPath(path) .setType(ScreenshotType.PNG) ); + + var image = Base64.getEncoder().encodeToString(screenshot); + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, image)); + return image; } @Override diff --git a/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPlugin.java b/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPlugin.java index b31e3805..528d24e7 100644 --- a/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPlugin.java +++ b/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPlugin.java @@ -29,7 +29,8 @@ public ScreenshotPlugin(boolean isEnabled) { this.isEnabled = isEnabled; } - protected abstract void takeScreenshot(String screenshotSaveDir, String filename); + public abstract String takeScreenshot(String fileName); + public abstract String takeScreenshot(String screenshotSaveDir, String filename); protected abstract String getOutputFolder(); protected abstract String getUniqueFileName(String testName); @@ -41,7 +42,7 @@ public void postAfterTest(TestResult testResult, Method memberInfo, Throwable fa var screenshotSaveDir = getOutputFolder(); var screenshotFileName = getUniqueFileName(memberInfo.getName()); - takeScreenshot(screenshotSaveDir, screenshotFileName); - SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(Paths.get(screenshotSaveDir, screenshotFileName).toString(), screenshotFileName)); + var image = takeScreenshot(screenshotSaveDir, screenshotFileName); + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(Paths.get(screenshotSaveDir, screenshotFileName).toString(), screenshotFileName, image)); } } diff --git a/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPluginEventArgs.java b/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPluginEventArgs.java index b0026cc5..f68c842e 100644 --- a/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPluginEventArgs.java +++ b/bellatrix.plugins.screenshots/src/main/java/plugins/screenshots/ScreenshotPluginEventArgs.java @@ -20,7 +20,7 @@ public class ScreenshotPluginEventArgs { private final String screenshotPath; private final String fileName; - public ScreenshotPluginEventArgs(String screenshotPath, String fileName) { + public ScreenshotPluginEventArgs(String screenshotPath, String fileName, String image) { this.screenshotPath = screenshotPath; this.fileName = fileName; } diff --git a/bellatrix.web/src/main/java/solutions/bellatrix/web/infrastructure/WebScreenshotPlugin.java b/bellatrix.web/src/main/java/solutions/bellatrix/web/infrastructure/WebScreenshotPlugin.java index b58a9ab0..bc58145e 100644 --- a/bellatrix.web/src/main/java/solutions/bellatrix/web/infrastructure/WebScreenshotPlugin.java +++ b/bellatrix.web/src/main/java/solutions/bellatrix/web/infrastructure/WebScreenshotPlugin.java @@ -14,6 +14,7 @@ package solutions.bellatrix.web.infrastructure; import plugins.screenshots.ScreenshotPlugin; +import plugins.screenshots.ScreenshotPluginEventArgs; import ru.yandex.qatools.ashot.AShot; import ru.yandex.qatools.ashot.shooting.ShootingStrategies; import solutions.bellatrix.core.configuration.ConfigurationService; @@ -22,9 +23,12 @@ import solutions.bellatrix.web.configuration.WebSettings; import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Paths; +import java.util.Base64; import java.util.UUID; public class WebScreenshotPlugin extends ScreenshotPlugin { @@ -33,17 +37,48 @@ public WebScreenshotPlugin() { } @Override - protected void takeScreenshot(String screenshotSaveDir, String filename) { + public String takeScreenshot(String name) { + var screenshotSaveDir = getOutputFolder(); + var filename = getUniqueFileName(name); + + var screenshot = new AShot() + .shootingStrategy(ShootingStrategies.viewportPasting(100)) + .takeScreenshot(DriverService.getWrappedDriver()); + + var path = Paths.get(screenshotSaveDir, filename).toString(); + var destFile = new File(path); + Log.info("Saving screenshot with path: " + destFile); + try { + ImageIO.write(screenshot.getImage(), "png", destFile); + } catch (IOException e) { + Log.error(e.toString()); + } + + var base64image = bufferedImageToBase64(screenshot.getImage()); + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, base64image)); + return base64image; + } + + @Override + public String takeScreenshot(String screenshotSaveDir, String filename) { var screenshot = new AShot() .shootingStrategy(ShootingStrategies.viewportPasting(100)) .takeScreenshot(DriverService.getWrappedDriver()); - var destFile = new File(Paths.get(screenshotSaveDir, filename).toString()); + + var path = Paths.get(screenshotSaveDir, filename).toString(); + var destFile = new File(path); Log.info("Saving screenshot with path: " + destFile); try { ImageIO.write(screenshot.getImage(), "png", destFile); } catch (IOException e) { Log.error(e.toString()); } + + var base64image = bufferedImageToBase64(screenshot.getImage()); + + SCREENSHOT_GENERATED.broadcast(new ScreenshotPluginEventArgs(path.toString(), filename, base64image)); + return base64image; } @Override @@ -63,4 +98,23 @@ protected String getOutputFolder() { protected String getUniqueFileName(String testName) { return testName.concat(UUID.randomUUID().toString()).concat(".png"); } + + private static String bufferedImageToBase64(BufferedImage image) { + String base64String = null; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", outputStream); + byte[] imageBytes = outputStream.toByteArray(); + base64String = Base64.getEncoder().encodeToString(imageBytes); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return base64String; + } } diff --git a/pom.xml b/pom.xml index d7e3e3ed..52b2847b 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ bellatrix.plugins.screenshots bellatrix.plugins.video bellatrix.plugins.jira.zephyr + bellatrix.addons.visual-regression-tracker bellatrix.web bellatrix.playwright bellatrix.desktop