diff --git a/README.md b/README.md
index 6ae8b456..221e7a52 100644
--- a/README.md
+++ b/README.md
@@ -41,17 +41,23 @@
Spectrum is an **e2e test automation framework** that leverages **JUnit 6** and **Selenium 4** to provide these features automatically:
-* **Driver** management with **auto-waiting**, to **highly reduce flakiness**
-* Generation of a **html report** with the **execution video**, **coverage** and **several additional reports**. All **fully customisable**
-* **Mail/Slack notifications** with reports as attachments
-* It is fully configurable via a **declarative yaml file**, providing **out-of-the-box defaults** to let you run tests with no additional configuration
-* It supports **browsers automation** via Selenium and **mobile and desktop automation** via Appium
-* It supports **WebDriver BiDi** protocol
-
-Spectrum manages all the boilerplate code, allowing you to focus on test logic:
-you just need to write a JUnit test using the native Selenium API as you would do in a vanilla Selenium test,
-and Spectrum will enrich your suite transparently.
-Be sure to check the [full documentation](https://giulong.github.io/spectrum/) to see all the available features.
+* [Driver management](https://giulong.github.io/spectrum/#drivers-and-environments) with
+ [auto-waiting](https://giulong.github.io/spectrum/#auto-waiting), to **highly reduce flakiness**.
+* [Generation of a html report](https://giulong.github.io/spectrum/#automatically-generated-reports) with the
+ [execution video](https://giulong.github.io/spectrum/#automatic-execution-video-generation),
+ [coverage report](https://giulong.github.io/spectrum/#testbook---coverage) and more. All **fully customisable**.
+* [Visual Regression Testing](https://giulong.github.io/spectrum/#visual-regression-testing),
+ comparing visual snapshots of the AUT to identify regressions.
+* [Mail/Slack notifications](https://giulong.github.io/spectrum/#event-sourcing---notifications) with reports as attachments.
+* **No custom API**: it enriches plain Selenium tests transparently.
+* Configurable via a **declarative yaml file**, with
+ [defaults](https://github.com/giulong/spectrum/blob/develop/spectrum/src/main/resources/yaml/configuration.default.yaml)
+ to run with no additional configuration.
+* Supports **browsers automation** via Selenium and **mobile/desktop automation** via Appium.
+* Supports [WebDriver BiDi](https://giulong.github.io/spectrum/#webdriver-bidi) protocol.
+
+Spectrum manages all the boilerplate code, allowing you to **focus on test logic**:
+write a JUnit test using the vanilla Selenium API, and **Spectrum will enrich your suite transparently**.
# Getting Started
@@ -79,7 +85,8 @@ Here's an overview of the project created by the archetype, along with the gener
https://github.com/giulong/spectrum/assets/27963644/df6b801e-91ca-415b-b510-a45b7392de20
-You can also configure Spectrum to produce additional reports, such as summary and coverage:
+You can also configure Spectrum to produce additional reports, such as [summary](https://giulong.github.io/spectrum/#execution-summary)
+and [coverage](https://giulong.github.io/spectrum/#testbook---coverage):
@@ -87,7 +94,7 @@ If you like Spectrum, please consider giving it a GitHub Star ⭐
# Usage
-To start without the archetype, it's as simple as following these steps:
+Starting without the archetype is as simple as following these steps:
1. Add the Spectrum dependency to your project, you can find the snippet for every build tool [here](https://central.sonatype.com/artifact/io.github.giulong/spectrum).
@@ -119,14 +126,17 @@ To start without the archetype, it's as simple as following these steps:
```yaml
application:
- baseUrl: https://the-internet.herokuapp.com/ # Change this with your app's landing page
+ baseUrl: https://the-internet.herokuapp.com/ # Change it with your app's landing page
- video: # video of the execution attached to the html report (will be empty since the test is doing nothing)
+ # video of the execution attached to the html report
+ # (will be empty since the test is doing nothing)
+ video:
frames:
- autoBefore
+ # the html report will open automatically in your browser after the execution
extent:
- openAtEnd: true # the html report will open automatically in your browser after the execution
+ openAtEnd: true
```
4. Run the test!
diff --git a/docs/README.md b/docs/README.md
index 20eb88a9..fd9f1a1c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -347,10 +347,15 @@ so you can directly use them in your tests/pages.
* `T hover(WebElement)`: hovers on the provided WebElement, leveraging the `actions` field
* `T screenshot()`: adds a screenshot at INFO level to the current test in the Extent Report
+* `T screenshot(WebElement)`: adds a screenshot of the provided WebElement at INFO level to the current test in the Extent Report
* `T screenshotInfo(String)`: adds a screenshot with the provided message and INFO status to the current test in the Extent Report
+* `T screenshotInfo(WebElement, String)`: adds a screenshot of the provided WebElement with the provided message and INFO status to the current test in the Extent Report
* `T screenshotWarning(String)`: adds a screenshot status with the provided message and WARN to the current test in the Extent Report
+* `T screenshotWarning(WebElement, String)`: adds a screenshot of the provided WebElement status with the provided message and WARN to the current test in the Extent Report
* `T screenshotFail(String)`: adds a screenshot with the provided message and FAIL status to the current test in the Extent Report
+* `T screenshotFail(WebElement, String)`: adds a screenshot of the provided WebElement with the provided message and FAIL status to the current test in the Extent Report
* `Media addScreenshotToReport(String, Status)`: adds a screenshot with the provided message and the provided status to the current test in the Extent Report
+* `Media addScreenshotToReport(WebElement, String, Status)`: adds a screenshot of the provided WebElement with the provided message and the provided status to the current test in the Extent Report
* `void deleteDownloadsFolder()`: deletes the download folder (its path is provided in the `configuration*.yaml`)
* `T waitForDownloadOf(Path)`: leverages the configurable `downloadWait` to check fluently if the file at the provided path is fully downloaded
* `boolean checkDownloadedFile(String, String)`: leverages the `waitForDownloadOf` method and then compares checksum of the two files provided. Check
diff --git a/it/src/test/java/io/github/giulong/spectrum/it/tests/CheckboxIT.java b/it/src/test/java/io/github/giulong/spectrum/it/tests/CheckboxIT.java
index 9afabb02..b881e37c 100644
--- a/it/src/test/java/io/github/giulong/spectrum/it/tests/CheckboxIT.java
+++ b/it/src/test/java/io/github/giulong/spectrum/it/tests/CheckboxIT.java
@@ -1,6 +1,9 @@
package io.github.giulong.spectrum.it.tests;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.openqa.selenium.OutputType.BYTES;
import io.github.giulong.spectrum.SpectrumTest;
import io.github.giulong.spectrum.it.pages.CheckboxPage;
@@ -25,6 +28,9 @@ void testWithNoDisplayName() {
driver.get(configuration.getApplication().getBaseUrl());
assertEquals("Welcome to the-internet", landingPage.getTitle().getText());
+ // Taking a screenshot with the native Selenium API to prove it's the same as using the helper methods
+ takesScreenshot.getScreenshotAs(BYTES);
+
extentTest.info("Custom step that should not be highlighted on video playback");
landingPage.getCheckboxLink().click();
extentTest.info("Custom step that should not be highlighted on video playback");
diff --git a/pom.xml b/pom.xml
index 1e3f679a..0ed6b076 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
truetrue
- 6.0.1
+ 6.0.25.21.02.20.18.12.6
@@ -217,7 +217,7 @@
ch.qos.logbacklogback-classic
- 1.5.23
+ 1.5.24org.slf4j
@@ -340,7 +340,7 @@
com.puppycrawl.toolscheckstyle
- 12.3.0
+ 13.0.0com.github.sevntu-checkstyle
diff --git a/spectrum/pom.xml b/spectrum/pom.xml
index a447379e..231e93fd 100644
--- a/spectrum/pom.xml
+++ b/spectrum/pom.xml
@@ -368,7 +368,7 @@
org.sonatype.centralcentral-publishing-maven-plugin
- 0.9.0
+ 0.10.0truecentral
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumEntity.java b/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumEntity.java
index 437f993a..5aee67ae 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumEntity.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumEntity.java
@@ -4,10 +4,8 @@
import static com.aventstack.extentreports.Status.INFO;
import static com.aventstack.extentreports.Status.WARNING;
import static io.github.giulong.spectrum.enums.Frame.MANUAL;
-import static io.github.giulong.spectrum.extensions.resolvers.DriverResolver.ORIGINAL_DRIVER;
import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.EXTENSION_CONTEXT;
import static io.github.giulong.spectrum.utils.web_driver_events.VideoAutoScreenshotProducer.SCREENSHOT;
-import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import static org.openqa.selenium.OutputType.BYTES;
import java.nio.file.Files;
@@ -30,10 +28,7 @@
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.ExtensionContext;
-import org.openqa.selenium.By;
-import org.openqa.selenium.TakesScreenshot;
-import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.WebElement;
+import org.openqa.selenium.*;
import org.openqa.selenium.bidi.browsingcontext.BrowsingContext;
import org.openqa.selenium.bidi.module.BrowsingContextInspector;
import org.openqa.selenium.bidi.module.LogInspector;
@@ -71,6 +66,12 @@ public abstract class SpectrumEntity, Data> {
@Shared
protected WebDriver driver;
+ @Shared
+ protected TakesScreenshot takesScreenshot;
+
+ @Shared
+ protected JavascriptExecutor javascriptExecutor;
+
@Shared
protected WebDriverWait implicitWait;
@@ -229,17 +230,8 @@ public T screenshotFail(final WebElement webElement, final String message) {
* @return the calling SpectrumEntity instance
*/
public T addScreenshotToReport(final String message, final Status status) {
- final ExtensionContext context = testContext.get(EXTENSION_CONTEXT, ExtensionContext.class);
- final TakesScreenshot originalDriver = (TakesScreenshot) context.getStore(GLOBAL).get(ORIGINAL_DRIVER, WebDriver.class);
- final Payload payload = Payload
- .builder()
- .screenshot(originalDriver.getScreenshotAs(BYTES))
- .message(message)
- .status(status)
- .takesScreenshot(originalDriver)
- .build();
-
- eventsDispatcher.fire(MANUAL.getValue(), SCREENSHOT, context, payload);
+ testData.buildScreenshotFor(MANUAL, message, status);
+ takesScreenshot.getScreenshotAs(BYTES);
return (T) this;
}
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumTest.java b/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumTest.java
index 059ff681..5563ef77 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumTest.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/SpectrumTest.java
@@ -35,6 +35,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.bidi.browsingcontext.BrowsingContext;
import org.openqa.selenium.bidi.module.BrowsingContextInspector;
@@ -128,6 +130,8 @@ void beforeEach(final TestContext testContext, final TestData testData, final St
final Actions actions, final Js js, final JsWebElementProxyBuilder jsWebElementProxyBuilder, final LogInspector logInspector,
final BrowsingContext browsingContext, final BrowsingContextInspector browsingContextInspector, final Network network, final Data data) {
this.driver = driver;
+ this.takesScreenshot = (TakesScreenshot) driver;
+ this.javascriptExecutor = (JavascriptExecutor) driver;
this.implicitWait = implicitWait;
this.pageLoadWait = pageLoadWait;
this.scriptWait = scriptWait;
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolver.java b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolver.java
index 46bd0d2b..f88f205b 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolver.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolver.java
@@ -2,6 +2,7 @@
import static io.github.giulong.spectrum.extensions.resolvers.ConfigurationResolver.CONFIGURATION;
import static io.github.giulong.spectrum.extensions.resolvers.StatefulExtentTestResolver.STATEFUL_EXTENT_TEST;
+import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.TEST_CONTEXT;
import static io.github.giulong.spectrum.extensions.resolvers.TestDataResolver.TEST_DATA;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
@@ -11,11 +12,7 @@
import io.github.giulong.spectrum.internals.web_driver_listeners.AutoWaitWebDriverListener;
import io.github.giulong.spectrum.internals.web_driver_listeners.EventsWebDriverListener;
-import io.github.giulong.spectrum.utils.Configuration;
-import io.github.giulong.spectrum.utils.ContextManager;
-import io.github.giulong.spectrum.utils.FileUtils;
-import io.github.giulong.spectrum.utils.StatefulExtentTest;
-import io.github.giulong.spectrum.utils.TestData;
+import io.github.giulong.spectrum.utils.*;
import io.github.giulong.spectrum.utils.video.Video;
import io.github.giulong.spectrum.utils.web_driver_events.*;
@@ -57,6 +54,7 @@ public WebDriver resolveParameter(@NonNull final ParameterContext parameterConte
final Configuration.Drivers.Waits.AutoWait autoWait = drivers.getWaits().getAuto();
final StatefulExtentTest statefulExtentTest = store.get(STATEFUL_EXTENT_TEST, StatefulExtentTest.class);
final TestData testData = store.get(TEST_DATA, TestData.class);
+ final TestContext testContext = store.get(TEST_CONTEXT, TestContext.class);
final Configuration.Application.Highlight highlight = configuration.getApplication().getHighlight();
final Pattern locatorPattern = Pattern.compile(configuration.getExtent().getLocatorRegex());
final Video video = configuration.getVideo();
@@ -110,6 +108,7 @@ public WebDriver resolveParameter(@NonNull final ParameterContext parameterConte
.locatorPattern(locatorPattern)
.events(events)
.consumers(consumers)
+ .testContext(testContext)
.build());
final WebDriver decoratedDriver = new EventFiringDecorator<>(webDriverListeners.toArray(new WebDriverListener[0])).decorate(driver);
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolver.java b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolver.java
index b406e0e0..e2afaa46 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolver.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolver.java
@@ -1,5 +1,7 @@
package io.github.giulong.spectrum.extensions.resolvers;
+import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
+
import io.github.giulong.spectrum.utils.ContextManager;
import io.github.giulong.spectrum.utils.TestContext;
@@ -23,6 +25,7 @@ public TestContext resolveParameter(@NonNull final ParameterContext parameterCon
log.debug("Resolving {}", TEST_CONTEXT);
final TestContext testContext = contextManager.initFor(context);
+ context.getStore(GLOBAL).put(TEST_CONTEXT, testContext);
testContext.put(EXTENSION_CONTEXT, context);
return testContext;
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListener.java b/spectrum/src/main/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListener.java
index 84e09481..e92e6684 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListener.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListener.java
@@ -1,7 +1,13 @@
package io.github.giulong.spectrum.internals.web_driver_listeners;
+import static com.aventstack.extentreports.Status.INFO;
import static io.github.giulong.spectrum.enums.Frame.AUTO_AFTER;
import static io.github.giulong.spectrum.enums.Frame.AUTO_BEFORE;
+import static io.github.giulong.spectrum.enums.Frame.MANUAL;
+import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.EXTENSION_CONTEXT;
+import static io.github.giulong.spectrum.extensions.resolvers.TestDataResolver.TEST_DATA;
+import static io.github.giulong.spectrum.utils.web_driver_events.VideoAutoScreenshotProducer.SCREENSHOT;
+import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -12,8 +18,13 @@
import java.util.regex.Pattern;
import io.github.giulong.spectrum.enums.Frame;
+import io.github.giulong.spectrum.pojos.events.Event.Payload;
import io.github.giulong.spectrum.utils.Configuration;
import io.github.giulong.spectrum.utils.Configuration.Drivers.Events;
+import io.github.giulong.spectrum.utils.TestContext;
+import io.github.giulong.spectrum.utils.TestData;
+import io.github.giulong.spectrum.utils.TestData.Screenshot;
+import io.github.giulong.spectrum.utils.events.EventsDispatcher;
import io.github.giulong.spectrum.utils.web_driver_events.WebDriverEvent;
import io.github.giulong.spectrum.utils.web_driver_events.WebDriverEventConsumer;
@@ -22,6 +33,7 @@
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.extension.ExtensionContext;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Sequence;
import org.slf4j.event.Level;
@@ -32,8 +44,11 @@ public class EventsWebDriverListener extends SpectrumWebDriverListener {
private static final Pattern SECURED_PATTERN = Pattern.compile("@Secured@(?.*)@Secured@");
+ private final EventsDispatcher eventsDispatcher = EventsDispatcher.getInstance();
+
private Events events;
private List consumers;
+ private TestContext testContext;
List parse(final Object[] args) {
return Arrays
@@ -484,8 +499,29 @@ public void beforeGetScreenshotAs(final WebDriver driver, final OutputType void afterGetScreenshotAs(final WebDriver driver, final OutputType target, final X result) {
+ if (result instanceof byte[]) {
+ final ExtensionContext context = testContext.get(EXTENSION_CONTEXT, ExtensionContext.class);
+ final Screenshot screenshot = Optional
+ .ofNullable(context.getStore(GLOBAL).get(TEST_DATA, TestData.class).getScreenshot())
+ .orElseGet(() -> Screenshot
+ .builder()
+ .frame(MANUAL)
+ .message("")
+ .status(INFO)
+ .build());
+
+ final Payload payload = Payload
+ .builder()
+ .screenshot((byte[]) result)
+ .message(screenshot.getMessage())
+ .status(screenshot.getStatus())
+ .takesScreenshot((TakesScreenshot) driver)
+ .build();
+
+ eventsDispatcher.fire(screenshot.getFrame().getValue(), SCREENSHOT, context, payload);
+ }
+
listenTo(AUTO_AFTER, events.getAfterGetScreenshotAs(), driver, target, result);
}
diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/utils/TestData.java b/spectrum/src/main/java/io/github/giulong/spectrum/utils/TestData.java
index 29a4b76a..fa1eefd4 100644
--- a/spectrum/src/main/java/io/github/giulong/spectrum/utils/TestData.java
+++ b/spectrum/src/main/java/io/github/giulong/spectrum/utils/TestData.java
@@ -4,6 +4,9 @@
import java.util.HashMap;
import java.util.Map;
+import com.aventstack.extentreports.Status;
+
+import io.github.giulong.spectrum.enums.Frame;
import io.github.giulong.spectrum.exceptions.TestFailedException;
import io.github.giulong.spectrum.exceptions.VisualRegressionException;
@@ -26,6 +29,7 @@ public class TestData {
private Path videoPath;
private VisualRegression visualRegression;
private TestFailedException testFailedException;
+ private Screenshot screenshot;
@Builder.Default
private Map encoders = new HashMap<>();
@@ -64,6 +68,15 @@ public void registerFailedVisualRegression() {
testFailedException = new VisualRegressionException(String.format("There were %d visual regressions", ++visualRegression.count));
}
+ public void buildScreenshotFor(final Frame frame, final String message, final Status status) {
+ screenshot = Screenshot
+ .builder()
+ .frame(frame)
+ .message(message)
+ .status(status)
+ .build();
+ }
+
@Getter
@Builder
public static class VisualRegression {
@@ -74,4 +87,12 @@ public static class VisualRegression {
@Setter
private Path dynamicPath;
}
+
+ @Getter
+ @Builder
+ public static class Screenshot {
+ private Frame frame;
+ private String message;
+ private Status status;
+ }
}
diff --git a/spectrum/src/main/resources/yaml/configuration.default.yaml b/spectrum/src/main/resources/yaml/configuration.default.yaml
index 9782e858..53045e6b 100644
--- a/spectrum/src/main/resources/yaml/configuration.default.yaml
+++ b/spectrum/src/main/resources/yaml/configuration.default.yaml
@@ -370,10 +370,10 @@ drivers:
level: INFO
message: Css property %2$s of element %1$s is %3$s
beforeGetScreenshotAs:
- level: INFO
+ level: DEBUG
message: Getting screenshot as %2$s
afterGetScreenshotAs:
- level: ERROR
+ level: DEBUG
message: Screenshot as %2$s resulted is %3$s
beforeAnyNavigationCall:
message: Calling navigation method %2$s with args %3$s
diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumEntityTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumEntityTest.java
index bb982208..0132fff1 100644
--- a/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumEntityTest.java
+++ b/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumEntityTest.java
@@ -4,13 +4,11 @@
import static com.aventstack.extentreports.Status.INFO;
import static com.aventstack.extentreports.Status.WARNING;
import static io.github.giulong.spectrum.enums.Frame.MANUAL;
-import static io.github.giulong.spectrum.extensions.resolvers.DriverResolver.ORIGINAL_DRIVER;
import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.EXTENSION_CONTEXT;
import static io.github.giulong.spectrum.utils.web_driver_events.VideoAutoScreenshotProducer.SCREENSHOT;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.*;
import static org.openqa.selenium.OutputType.BYTES;
@@ -29,10 +27,7 @@
import io.github.giulong.spectrum.interfaces.Shared;
import io.github.giulong.spectrum.pojos.events.Event.Payload;
-import io.github.giulong.spectrum.utils.Configuration;
-import io.github.giulong.spectrum.utils.FileUtils;
-import io.github.giulong.spectrum.utils.Reflections;
-import io.github.giulong.spectrum.utils.TestContext;
+import io.github.giulong.spectrum.utils.*;
import io.github.giulong.spectrum.utils.events.EventsDispatcher;
import org.junit.jupiter.api.AfterEach;
@@ -97,6 +92,9 @@ class SpectrumEntityTest {
@Mock
private File file;
+ @Mock
+ private TestData testData;
+
@MockFinal
@SuppressWarnings("unused")
private EventsDispatcher eventsDispatcher;
@@ -108,11 +106,11 @@ class SpectrumEntityTest {
private ExtensionContext context;
@Mock
- private ExtensionContext.Store store;
-
- @Mock(extraInterfaces = TakesScreenshot.class)
private WebDriver driver;
+ @Mock
+ private TakesScreenshot takesScreenshot;
+
@Captor
private ArgumentCaptor> functionArgumentCaptor;
@@ -135,31 +133,16 @@ void afterEach() {
messageDigestMockedStatic.close();
}
- private void screenshotStubs() {
- when(testContext.get(EXTENSION_CONTEXT, ExtensionContext.class)).thenReturn(context);
-
- when(context.getStore(GLOBAL)).thenReturn(store);
- when(store.get(ORIGINAL_DRIVER, WebDriver.class)).thenReturn(driver);
-
- when(((TakesScreenshot) driver).getScreenshotAs(BYTES)).thenReturn(bytes);
- }
-
private void screenshotWebElementStubs() {
when(testContext.get(EXTENSION_CONTEXT, ExtensionContext.class)).thenReturn(context);
when(webElement.getScreenshotAs(BYTES)).thenReturn(bytes);
}
private void screenshotVerificationsFor(final String message, final Status status) {
- final Payload payload = Payload
- .builder()
- .screenshot(bytes)
- .message(message)
- .status(status)
- .takesScreenshot((TakesScreenshot) driver)
- .build();
+ verify(testData).buildScreenshotFor(MANUAL, message, status);
+ verify(takesScreenshot).getScreenshotAs(BYTES);
- verify(eventsDispatcher).fire(MANUAL.getValue(), SCREENSHOT, context, payload);
- verifyNoMoreInteractions(eventsDispatcher);
+ verifyNoInteractions(eventsDispatcher);
}
private void screenshotWebElementVerificationsFor(final String message, final Status status) {
@@ -185,7 +168,7 @@ void getSharedFields() {
.toList();
// we're checking real size and names here, no mocks
- assertEquals(21, actual.size());
+ assertEquals(23, actual.size());
assertTrue(sharedFieldsNames.containsAll(List.of(
"configuration",
"extentReports",
@@ -193,6 +176,8 @@ void getSharedFields() {
"actions",
"eventsDispatcher",
"driver",
+ "takesScreenshot",
+ "javascriptExecutor",
"implicitWait",
"pageLoadWait",
"scriptWait",
@@ -220,8 +205,6 @@ void hover() {
@Test
@DisplayName("screenshot should delegate to addScreenshotToReport")
void screenshot() {
- screenshotStubs();
-
assertEquals(spectrumEntity, spectrumEntity.screenshot());
screenshotVerificationsFor("", INFO);
@@ -240,8 +223,6 @@ void screenshotWebElement() {
@Test
@DisplayName("infoWithScreenshot should delegate to addScreenshotToReport")
void infoWithScreenshot() {
- screenshotStubs();
-
assertEquals(spectrumEntity, spectrumEntity.screenshotInfo(msg));
screenshotVerificationsFor(msg, INFO);
@@ -260,8 +241,6 @@ void infoWithScreenshotWebElement() {
@Test
@DisplayName("warningWithScreenshot should delegate to addScreenshotToReport")
void warningWithScreenshot() {
- screenshotStubs();
-
assertEquals(spectrumEntity, spectrumEntity.screenshotWarning(msg));
screenshotVerificationsFor(msg, WARNING);
@@ -280,8 +259,6 @@ void warningWithScreenshotWebElement() {
@Test
@DisplayName("failWithScreenshot should delegate to addScreenshotToReport")
void failWithScreenshot() {
- screenshotStubs();
-
assertEquals(spectrumEntity, spectrumEntity.screenshotFail(msg));
screenshotVerificationsFor(msg, FAIL);
@@ -302,8 +279,6 @@ void failWithScreenshotWebElement() {
void addScreenshotToReport() {
final Status status = INFO;
- screenshotStubs();
-
spectrumEntity.addScreenshotToReport(msg, status);
screenshotVerificationsFor(msg, status);
diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumTestTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumTestTest.java
index 8334bb34..2d309b63 100644
--- a/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumTestTest.java
+++ b/spectrum/src/test/java/io/github/giulong/spectrum/SpectrumTestTest.java
@@ -32,6 +32,8 @@
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.bidi.browsingcontext.BrowsingContext;
@@ -45,8 +47,8 @@ class SpectrumTestTest {
@Mock
private TestContext testContext;
- @Mock
- private WebDriver webDriver;
+ @Mock(extraInterfaces = {TakesScreenshot.class, JavascriptExecutor.class})
+ private WebDriver driver;
@Mock
private FakeData data;
@@ -185,29 +187,33 @@ void testBeforeEach() {
when(auto.getTimeout()).thenReturn(timeout);
when(timeout.toSeconds()).thenReturn(seconds);
+ when(statefulExtentTest.getCurrentNode()).thenReturn(extentTest);
+
assertNull(childTestVoid.childTestPage);
assertNull(childTestVoid.getParentTestPage());
- childTestVoid.beforeEach(testContext, testData, statefulExtentTest, webDriver, implicitWait, pageLoadWait, scriptWait, downloadWait,
+ childTestVoid.beforeEach(testContext, testData, statefulExtentTest, driver, implicitWait, pageLoadWait, scriptWait, downloadWait,
actions, js, jsWebElementProxyBuilder, logInspector, browsingContext, browsingContextInspector, network, null);
- assertEquals(webDriver, spectrumTest.driver);
- assertEquals(implicitWait, spectrumTest.implicitWait);
- assertEquals(pageLoadWait, spectrumTest.pageLoadWait);
- assertEquals(scriptWait, spectrumTest.scriptWait);
- assertEquals(downloadWait, spectrumTest.downloadWait);
- assertEquals(statefulExtentTest, spectrumTest.statefulExtentTest);
- assertEquals(extentTest, spectrumTest.extentTest);
- assertEquals(actions, spectrumTest.actions);
- assertEquals(testData, spectrumTest.testData);
- assertEquals(js, spectrumTest.js);
- assertEquals(logInspector, spectrumTest.logInspector);
- assertEquals(browsingContext, spectrumTest.browsingContext);
- assertEquals(browsingContextInspector, spectrumTest.browsingContextInspector);
- assertEquals(network, spectrumTest.network);
- assertEquals(jsWebElementProxyBuilder, spectrumTest.jsWebElementProxyBuilder);
- assertEquals(data, spectrumTest.data);
- assertEquals(testContext, spectrumTest.testContext);
+ assertEquals(driver, childTestVoid.driver);
+ assertEquals(driver, childTestVoid.takesScreenshot);
+ assertEquals(driver, childTestVoid.javascriptExecutor);
+ assertEquals(implicitWait, childTestVoid.implicitWait);
+ assertEquals(pageLoadWait, childTestVoid.pageLoadWait);
+ assertEquals(scriptWait, childTestVoid.scriptWait);
+ assertEquals(downloadWait, childTestVoid.downloadWait);
+ assertEquals(statefulExtentTest, childTestVoid.statefulExtentTest);
+ assertEquals(extentTest, childTestVoid.extentTest);
+ assertEquals(actions, childTestVoid.actions);
+ assertEquals(testData, childTestVoid.testData);
+ assertEquals(js, childTestVoid.js);
+ assertEquals(logInspector, childTestVoid.logInspector);
+ assertEquals(browsingContext, childTestVoid.browsingContext);
+ assertEquals(browsingContextInspector, childTestVoid.browsingContextInspector);
+ assertEquals(network, childTestVoid.network);
+ assertEquals(jsWebElementProxyBuilder, childTestVoid.jsWebElementProxyBuilder);
+ assertNull(childTestVoid.data);
+ assertEquals(testContext, childTestVoid.testContext);
// injectPages
assertNull(childTestVoid.toSkip);
diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolverTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolverTest.java
index c2fb21e0..5789ca2f 100644
--- a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolverTest.java
+++ b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/DriverResolverTest.java
@@ -3,6 +3,7 @@
import static io.github.giulong.spectrum.extensions.resolvers.ConfigurationResolver.CONFIGURATION;
import static io.github.giulong.spectrum.extensions.resolvers.DriverResolver.*;
import static io.github.giulong.spectrum.extensions.resolvers.StatefulExtentTestResolver.STATEFUL_EXTENT_TEST;
+import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.TEST_CONTEXT;
import static io.github.giulong.spectrum.extensions.resolvers.TestDataResolver.TEST_DATA;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -92,6 +93,9 @@ class DriverResolverTest {
@Mock
private Configuration.Drivers.Events events;
+ @Mock
+ private TestContext testContext;
+
@SuppressWarnings("rawtypes")
@Mock
private EventsWebDriverListener.EventsWebDriverListenerBuilder eventsWebDriverListenerBuilder;
@@ -229,6 +233,7 @@ private void stubs() {
when(store.get(STATEFUL_EXTENT_TEST, StatefulExtentTest.class)).thenReturn(statefulExtentTest);
when(store.get(TEST_DATA, TestData.class)).thenReturn(testData);
+ when(store.get(TEST_CONTEXT, TestContext.class)).thenReturn(testContext);
when(configuration.getVideo()).thenReturn(video);
when(LogConsumer.builder()).thenReturn(logConsumerBuilder);
@@ -266,6 +271,7 @@ private void stubs() {
when(eventsWebDriverListenerBuilder.locatorPattern(pattern)).thenReturn(eventsWebDriverListenerBuilder);
when(eventsWebDriverListenerBuilder.events(events)).thenReturn(eventsWebDriverListenerBuilder);
when(eventsWebDriverListenerBuilder.consumers(consumersArgumentCaptor.capture())).thenReturn(eventsWebDriverListenerBuilder);
+ when(eventsWebDriverListenerBuilder.testContext(testContext)).thenReturn(eventsWebDriverListenerBuilder);
when(eventsWebDriverListenerBuilder.build()).thenReturn(eventsWebDriverListener);
}
diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolverTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolverTest.java
index 8158fac5..9ff02ef4 100644
--- a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolverTest.java
+++ b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/TestContextResolverTest.java
@@ -1,7 +1,9 @@
package io.github.giulong.spectrum.extensions.resolvers;
import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.EXTENSION_CONTEXT;
+import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.TEST_CONTEXT;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import static org.mockito.Mockito.*;
import io.github.giulong.spectrum.MockFinal;
@@ -40,11 +42,13 @@ class TestContextResolverTest {
@DisplayName("resolveParameter should return an instance of TestContext")
void resolveParameter() {
when(contextManager.initFor(context)).thenReturn(testContext);
+ when(context.getStore(GLOBAL)).thenReturn(store);
final TestContext actual = testContextResolver.resolveParameter(parameterContext, context);
assertEquals(testContext, actual);
verify(testContext).put(EXTENSION_CONTEXT, context);
- verifyNoInteractions(store);
+ verify(store).put(TEST_CONTEXT, testContext);
+ verifyNoInteractions(parameterContext);
}
}
diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListenerTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListenerTest.java
index cddb9411..2610b039 100644
--- a/spectrum/src/test/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListenerTest.java
+++ b/spectrum/src/test/java/io/github/giulong/spectrum/internals/web_driver_listeners/EventsWebDriverListenerTest.java
@@ -2,21 +2,45 @@
import static io.github.giulong.spectrum.enums.Frame.AUTO_AFTER;
import static io.github.giulong.spectrum.enums.Frame.AUTO_BEFORE;
-import static org.junit.jupiter.api.Assertions.*;
+import static io.github.giulong.spectrum.enums.Frame.MANUAL;
+import static io.github.giulong.spectrum.extensions.resolvers.TestContextResolver.EXTENSION_CONTEXT;
+import static io.github.giulong.spectrum.extensions.resolvers.TestDataResolver.TEST_DATA;
+import static io.github.giulong.spectrum.utils.web_driver_events.VideoAutoScreenshotProducer.SCREENSHOT;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.*;
-import static org.slf4j.event.Level.*;
+import static org.openqa.selenium.OutputType.BASE64;
+import static org.openqa.selenium.OutputType.BYTES;
+import static org.openqa.selenium.OutputType.FILE;
+import static org.slf4j.event.Level.DEBUG;
+import static org.slf4j.event.Level.INFO;
+import static org.slf4j.event.Level.TRACE;
+import static org.slf4j.event.Level.WARN;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
+import com.aventstack.extentreports.Status;
+
+import io.github.giulong.spectrum.MockFinal;
+import io.github.giulong.spectrum.pojos.events.Event.Payload;
import io.github.giulong.spectrum.utils.Configuration;
import io.github.giulong.spectrum.utils.Configuration.Drivers.Event;
import io.github.giulong.spectrum.utils.Reflections;
+import io.github.giulong.spectrum.utils.TestContext;
+import io.github.giulong.spectrum.utils.TestData;
+import io.github.giulong.spectrum.utils.TestData.Screenshot;
+import io.github.giulong.spectrum.utils.events.EventsDispatcher;
import io.github.giulong.spectrum.utils.web_driver_events.WebDriverEvent;
import io.github.giulong.spectrum.utils.web_driver_events.WebDriverEventConsumer;
@@ -24,13 +48,16 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
-import org.openqa.selenium.Keys;
-import org.openqa.selenium.WebElement;
+import org.openqa.selenium.*;
import org.slf4j.LoggerFactory;
class EventsWebDriverListenerTest {
@@ -41,6 +68,24 @@ class EventsWebDriverListenerTest {
private final String message = "message
%s
";
private final long wait = 1L;
+ @Mock(extraInterfaces = TakesScreenshot.class)
+ private WebDriver driver;
+
+ @Mock
+ private TestContext testContext;
+
+ @Mock
+ private ExtensionContext context;
+
+ @Mock
+ private TestData testData;
+
+ @Mock
+ private Screenshot screenshot;
+
+ @Mock
+ private Store store;
+
@Mock
private Event event;
@@ -80,6 +125,10 @@ class EventsWebDriverListenerTest {
@Mock
private Configuration.Drivers.Events events;
+ @MockFinal
+ @SuppressWarnings("unused")
+ private EventsDispatcher eventsDispatcher;
+
@InjectMocks
private EventsWebDriverListener eventsWebDriverListener = new EventsWebDriverListener(EventsWebDriverListener.builder());
@@ -475,6 +524,119 @@ void afterSendKeysSecured() {
verify(consumer2).accept(webDriverEvent);
}
+ @Test
+ @DisplayName("afterGetScreenshotAs should dispatch an event when the screenshot is in bytes")
+ void afterGetScreenshotAsBytes() {
+ final String message = "message";
+ final Status status = Status.INFO;
+ final byte[] result = new byte[]{1, 2, 3};
+ final Payload payload = Payload
+ .builder()
+ .screenshot(result)
+ .message(message)
+ .status(status)
+ .takesScreenshot((TakesScreenshot) driver)
+ .build();
+
+ when(testContext.get(EXTENSION_CONTEXT, ExtensionContext.class)).thenReturn(context);
+ when(context.getStore(GLOBAL)).thenReturn(store);
+ when(store.get(TEST_DATA, TestData.class)).thenReturn(testData);
+ when(testData.getScreenshot()).thenReturn(screenshot);
+ when(screenshot.getMessage()).thenReturn(message);
+ when(screenshot.getStatus()).thenReturn(status);
+ when(screenshot.getFrame()).thenReturn(MANUAL);
+
+ when(WebDriverEvent.builder()).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.frame(AUTO_AFTER)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.level(INFO)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.args(List.of(driver, BYTES, result))).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.message(message)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.build()).thenReturn(webDriverEvent);
+
+ when(events.getAfterGetScreenshotAs()).thenReturn(event);
+
+ // listenTo
+ ((Logger) LoggerFactory.getLogger(EventsWebDriverListener.class)).setLevel(Level.INFO);
+ when(event.getMessage()).thenReturn(message);
+ when(event.getLevel()).thenReturn(INFO);
+ when(event.getWait()).thenReturn(wait);
+
+ eventsWebDriverListener.afterGetScreenshotAs(driver, BYTES, result);
+
+ verify(eventsDispatcher).fire(MANUAL.getValue(), SCREENSHOT, context, payload);
+ }
+
+ @Test
+ @DisplayName("afterGetScreenshotAs should dispatch an event when the screenshot is in bytes, building a default screenshot if none is found in testData")
+ void afterGetScreenshotAsBytesNoScreenshot() {
+ final String message = "";
+ final Status status = Status.INFO;
+ final byte[] result = new byte[]{1, 2, 3};
+ final Payload payload = Payload
+ .builder()
+ .screenshot(result)
+ .message(message)
+ .status(status)
+ .takesScreenshot((TakesScreenshot) driver)
+ .build();
+
+ when(testContext.get(EXTENSION_CONTEXT, ExtensionContext.class)).thenReturn(context);
+ when(context.getStore(GLOBAL)).thenReturn(store);
+ when(store.get(TEST_DATA, TestData.class)).thenReturn(testData);
+ when(testData.getScreenshot()).thenReturn(null);
+
+ when(WebDriverEvent.builder()).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.frame(AUTO_AFTER)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.level(INFO)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.args(List.of(driver, BYTES, result))).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.message(message)).thenReturn(webDriverEventBuilder);
+ when(webDriverEventBuilder.build()).thenReturn(webDriverEvent);
+
+ when(events.getAfterGetScreenshotAs()).thenReturn(event);
+
+ // listenTo
+ ((Logger) LoggerFactory.getLogger(EventsWebDriverListener.class)).setLevel(Level.INFO);
+ when(event.getMessage()).thenReturn(message);
+ when(event.getLevel()).thenReturn(INFO);
+ when(event.getWait()).thenReturn(wait);
+
+ eventsWebDriverListener.afterGetScreenshotAs(driver, BYTES, result);
+
+ verify(eventsDispatcher).fire(MANUAL.getValue(), SCREENSHOT, context, payload);
+ }
+
+ @DisplayName("afterGetScreenshotAs should just delegates to listenTo when the screenshot is not in bytes")
+ @ParameterizedTest(name = "with output type {0}")
+ @MethodSource("valuesProvider")
+ void afterGetScreenshotAs(final OutputType