diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..eded3ad83 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,36 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 +charset = utf-8 + +[*.java] +ij_visual_guides = 180 +ij_java_align_multiline_throws_list = true +ij_java_align_multiline_annotation_parameters = true +ij_java_annotation_parameter_wrap = off +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_before_imports = 1 +ij_java_class_count_to_use_import_on_demand = 5 +ij_java_names_count_to_use_import_on_demand = 5 +ij_java_imports_layout = static java.**,static javax.**,$*,|,java.**,javax.**,* +ij_java_insert_inner_class_imports = true +ij_java_layout_static_imports_separately = false +ij_java_method_call_chain_wrap = off +ij_java_throws_list_wrap = off +ij_java_block_comment_at_first_column = false +ij_java_line_comment_at_first_column = false +ij_java_line_comment_add_space = true + +[{*.yml,*.yaml}] +indent_size = 2 +ij_continuation_indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true +ij_any_block_comment_at_first_column = false +ij_java_line_comment_at_first_column = false +ij_java_line_comment_add_space = true diff --git a/README.md b/README.md index 250c46519..44dee97d3 100644 --- a/README.md +++ b/README.md @@ -41,17 +41,12 @@ Spectrum is an **e2e test automation framework** that leverages **JUnit 6** and **Selenium 4** to provide these features automatically: -* **Driver** management -* **Auto-waiting** before interacting with elements, to **highly reduce flakiness** -* **Html report** generation with the **execution video** -* **Coverage report** generation by reading a **testbook** +* **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** -* It provides **out-of-the-box defaults** to let you run tests with no additional configuration -* It supports **browsers automation** via Selenium -* It supports **mobile and desktop applications automation** via Appium +* 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 -* It generates **several additional reports** that are **fully customisable** 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, @@ -73,37 +68,11 @@ https://github.com/giulong/spectrum/assets/27963644/fecee8f5-f6dc-4b47-81a3-514e ``` 2. Run the `LoginFormIT` demo test. -3. Check the report generated under `target/spectrum/reports`. +3. Check the generated report, which opens automatically in your browser. > ⚠️ Tests run on Chrome by default. As shown in the video above, you can change this with:
> `-Dspectrum.driver=firefox`, `-Dspectrum.driver=edge` or `-Dspectrum.driver=safari` -If you like Spectrum, please consider giving it a GitHub Star ⭐ - -# Usage - -If you'd rather add Spectrum manually to your project, you just need to add its dependency: - -## Maven - -```xml - - - io.github.giulong - spectrum - 1.26.1 - test - -``` - -## Gradle - -```gradle -dependencies { - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '1.26.1' -} -``` - Here's an overview of the project created by the archetype, along with the generated report and video:

login-form  report @@ -114,6 +83,54 @@ You can also configure Spectrum to produce additional reports, such as summary a summary  html testbook +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: + +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). + + ```xml + + io.github.giulong + spectrum + 1.26.1 + test + + ``` + +2. Create the `HelloWorldIT` test class extending `SpectrumTest`: + + ```java + import io.github.giulong.spectrum.SpectrumTest; + import org.junit.jupiter.api.Test; + + public class HelloWorldIT extends SpectrumTest { + + @Test + public void dummyTest() { + driver.get(configuration.getApplication().getBaseUrl()); + } + } + ``` + +3. create a basic `src/test/resources/configuration.yaml`: + + ```yaml + application: + baseUrl: https://the-internet.herokuapp.com/ # Change this 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) + frames: + - autoBefore + + extent: + openAtEnd: true # the html report will open automatically in your browser after the execution + ``` + +4. Run the test! + # Contributing Contributions to Spectrum are welcome! Please check out the [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/docs/README.md b/docs/README.md index 61addfa01..a181b8fd7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,8 +34,8 @@ and **mobile and desktop applications** via [Appium](http://appium.io/docs/en/la ## Spectrum Archetype -You should leverage the latest published version of the [Spectrum Archetype](https://mvnrepository.com/artifact/io.github.giulong/spectrum-archetype){:target="_blank"} to create a -new project either via your IDE or by running this from command line: +You should leverage the latest published version of the [Spectrum Archetype](https://central.sonatype.com/artifact/io.github.giulong/spectrum-archetype){:target="_blank"} +to create a new project either via your IDE or by running this from command line: {% include copyCode.html %} @@ -48,10 +48,11 @@ mvn archetype:generate -DarchetypeGroupId=io.github.giulong -DarchetypeArtifactI > official [archetype:generate docs](https://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html){:target="_blank"}. The project created contains a demo test you can immediately run. -If you don't want to leverage the archetype, you can manually add the [Spectrum dependency](https://mvnrepository.com/artifact/io.github.giulong/spectrum){:target="_blank"} to your -project: -## Maven +## Manual Configuration + +If you don't want to leverage the archetype, you can manually add the Spectrum dependency to your project. +[Here](https://central.sonatype.com/artifact/io.github.giulong/spectrum){:target="_blank"} you can find the snippet for every build tool. {% include copyCode.html %} @@ -65,19 +66,7 @@ project: ``` -## Gradle - -{% include copyCode.html %} - -```gradle -dependencies { - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '1.26.1' -} -``` - -## Test creation - -In general, all you need to do is create a **JUnit 6** test class extending the `SpectrumTest` class: +Then, you need to create a **JUnit 6** test class extending `SpectrumTest`: {% include copyCode.html %} @@ -94,7 +83,23 @@ public class HelloWorldIT extends SpectrumTest { } ``` -After running it, you will find a html report in the `target/spectrum/reports` folder. +As third and last step, you need create a basic `src/test/resources/configuration.yaml`: + +{% include copyCode.html %} + +```yaml +application: + baseUrl: https://the-internet.herokuapp.com/ # Change this 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) + frames: + - autoBefore + +extent: + openAtEnd: true # the html report will open automatically in your browser after the execution +``` + +Now you can run the test, and the generated report will automatically open in your browser. > ⚠️ **Running with Maven**
> If you run tests with Maven, the name of your test classes should end with `IT` as in the example above (`HelloWorldIT`), @@ -1580,6 +1585,9 @@ If you'd like to customise the js applied when highlighting, you have 2 options JSON Schema really comes in handy when editing `configuration*.yaml`, since it allows you to have autocompletion and a non-blocking validation (just warnings). + + + This is the list of the available schemas, be sure to pick the right one according to the version of Spectrum you are using. > 💡 **Tip**
diff --git a/docs/assets/videos/json-schema.mov b/docs/assets/videos/json-schema.mov new file mode 100644 index 000000000..9b37f6885 Binary files /dev/null and b/docs/assets/videos/json-schema.mov differ diff --git a/pom.xml b/pom.xml index 88c002cc8..7ddff1521 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ${revision} pom Spectrum Aggregate - E2E Test Automation Framework + Modern Selenium Framework https://github.com/giulong/spectrum @@ -52,7 +52,7 @@ 8.12.6 4.38.0 2.0.17 - 4.36.0 + 4.37.0 @@ -195,7 +195,7 @@ ch.qos.logback logback-classic - 1.5.19 + 1.5.20 org.codehaus.janino @@ -250,7 +250,7 @@ net.datafaker datafaker - 2.5.1 + 2.5.2 @@ -323,7 +323,7 @@ com.puppycrawl.tools checkstyle - 11.1.0 + 12.1.0 com.github.sevntu-checkstyle @@ -411,6 +411,7 @@ true same_thread concurrent + 5 m @@ -525,10 +526,6 @@ .*]]> ${revision}]]> - - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '.*' - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '${revision}' - @@ -545,10 +542,6 @@ .*]]> ${revision}]]> - - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '.*' - testImplementation group: 'io.github.giulong', name: 'spectrum', version: '${revision}' - diff --git a/spectrum/pom.xml b/spectrum/pom.xml index cad716f5b..18496d782 100644 --- a/spectrum/pom.xml +++ b/spectrum/pom.xml @@ -12,7 +12,7 @@ spectrum Spectrum - E2E Test Automation Framework + Modern Selenium Framework https://github.com/giulong/spectrum @@ -215,7 +215,7 @@ org.jacoco jacoco-maven-plugin - 0.8.13 + 0.8.14 io/github/giulong/spectrum/pojos/**/* diff --git a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolver.java b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolver.java index d1ab4226a..45fd2ae3a 100644 --- a/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolver.java +++ b/spectrum/src/main/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolver.java @@ -1,12 +1,12 @@ package io.github.giulong.spectrum.extensions.resolvers.bidi; +import io.github.giulong.spectrum.utils.Configuration; import lombok.extern.slf4j.Slf4j; import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolver; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.bidi.HasBiDi; import static io.github.giulong.spectrum.extensions.resolvers.DriverResolver.DRIVER; import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL; @@ -14,6 +14,8 @@ @Slf4j public abstract class BiDiTypeBasedParameterResolver implements ParameterResolver { + private final Configuration configuration = Configuration.getInstance(); + abstract String getKey(); abstract Class getType(); @@ -31,7 +33,7 @@ public final T resolveParameter(@NonNull final ParameterContext parameterContext final WebDriver driver = store.get(DRIVER, WebDriver.class); final String key = getKey(); - if (!isBiDiEnabledFor(driver)) { + if (!configuration.getDrivers().isBiDi()) { log.debug("BiDi disabled. Avoid resolving {}", key); return null; } @@ -42,8 +44,4 @@ public final T resolveParameter(@NonNull final ParameterContext parameterContext store.put(key, t); return t; } - - boolean isBiDiEnabledFor(final WebDriver driver) { - return driver instanceof HasBiDi && ((HasBiDi) driver).maybeGetBiDi().isPresent(); - } } diff --git a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolverTest.java b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolverTest.java index 91465db84..622833562 100644 --- a/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolverTest.java +++ b/spectrum/src/test/java/io/github/giulong/spectrum/extensions/resolvers/bidi/BiDiTypeBasedParameterResolverTest.java @@ -1,6 +1,9 @@ package io.github.giulong.spectrum.extensions.resolvers.bidi; +import io.github.giulong.spectrum.utils.Configuration; +import io.github.giulong.spectrum.utils.Reflections; import lombok.Getter; +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; @@ -11,21 +14,26 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.bidi.BiDi; import org.openqa.selenium.bidi.HasBiDi; import java.lang.reflect.Parameter; -import java.util.Optional; import java.util.stream.Stream; import static io.github.giulong.spectrum.extensions.resolvers.DriverResolver.DRIVER; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.*; class BiDiTypeBasedParameterResolverTest { + @Mock + private Configuration configuration; + + @Mock + private Configuration.Drivers drivers; + @Mock private ParameterContext parameterContext; @@ -35,9 +43,6 @@ class BiDiTypeBasedParameterResolverTest { @Mock private ExtensionContext.Store store; - @Mock - private BiDi biDi; - @Mock private WebDriver driver; @@ -50,6 +55,11 @@ class BiDiTypeBasedParameterResolverTest { @InjectMocks private DummyBiDiTypeBasedParameterResolver biDiTypeBasedParameterResolver; + @BeforeEach + void beforeEach() { + Reflections.setField("configuration", biDiTypeBasedParameterResolver, configuration); + } + @DisplayName("supportsParameter should check if the provided parameter type matches the concrete instance type") @ParameterizedTest(name = "with concrete type {0} we expect {1}") @MethodSource("valuesProvider") @@ -74,6 +84,8 @@ static Stream valuesProvider() { void resolveParameter() { when(context.getStore(GLOBAL)).thenReturn(store); when(store.get(DRIVER, WebDriver.class)).thenReturn(driver); + when(configuration.getDrivers()).thenReturn(drivers); + when(drivers.isBiDi()).thenReturn(false); assertNull(biDiTypeBasedParameterResolver.resolveParameter(parameterContext, context)); @@ -88,7 +100,8 @@ void resolveParameter() { void resolveParameterBiDi() { when(context.getStore(GLOBAL)).thenReturn(store); when(store.get(DRIVER, WebDriver.class)).thenReturn(bidiDriver); - when(((HasBiDi) bidiDriver).maybeGetBiDi()).thenReturn(Optional.of(biDi)); + when(configuration.getDrivers()).thenReturn(drivers); + when(drivers.isBiDi()).thenReturn(true); assertEquals("parameter", biDiTypeBasedParameterResolver.resolveParameter(parameterContext, context)); @@ -99,28 +112,6 @@ void resolveParameterBiDi() { verifyNoMoreInteractions(driver); } - @Test - @DisplayName("isBiDiEnabledFor should return false if the provided driver doesn't support BiDi") - void isBiDiEnabledForFalse() { - assertFalse(biDiTypeBasedParameterResolver.isBiDiEnabledFor(driver)); - } - - @Test - @DisplayName("isBiDiEnabledFor should return false if the provided driver supports BiDi but the optional is empty") - void isBiDiEnabledForEmpty() { - when(((HasBiDi) bidiDriver).maybeGetBiDi()).thenReturn(Optional.empty()); - - assertFalse(biDiTypeBasedParameterResolver.isBiDiEnabledFor(bidiDriver)); - } - - @Test - @DisplayName("isBiDiEnabledFor should return true if the provided driver supports BiDi") - void isBiDiEnabledFor() { - when(((HasBiDi) bidiDriver).maybeGetBiDi()).thenReturn(Optional.of(biDi)); - - assertTrue(biDiTypeBasedParameterResolver.isBiDiEnabledFor(bidiDriver)); - } - @Getter private static final class DummyBiDiTypeBasedParameterResolver extends BiDiTypeBasedParameterResolver {