Skip to content

Commit

Permalink
Merge pull request #244 from giulong/release/v1.15.0
Browse files Browse the repository at this point in the history
Release/v1.15.0
  • Loading branch information
giulong authored Sep 22, 2024
2 parents 7282105 + 0641bbb commit 9f5f627
Show file tree
Hide file tree
Showing 48 changed files with 655 additions and 79 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,17 @@ jobs:
echo "branches = ${{ steps.jacoco.outputs.branches }}"
- name: Setup GitHub Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v5

- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: docs/
source: ${{ env.DOCS_FOLDER }}/
destination: ./_site

- name: Upload GitHub Pages Artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3

- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
echo "branches = ${{ steps.jacoco.outputs.branches }}"
- name: Setup GitHub Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v5

- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
Expand All @@ -58,8 +58,8 @@ jobs:
destination: ./_site

- name: Upload GitHub Pages Artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3

- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4
60 changes: 60 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,66 @@ extent:
> Mind that the inline report has the same name of the regular one, so it's important to have them generated in separate folders
> not to override each other.

### Tests order

By default, tests are shown in the html report in the order of execution. You can override this behaviour via the `extent.sort` key,
which accepts an object.

> 💡 **Tip**<br/>
> Providing a fixed sorting can be particularly useful when running tests in parallel, so that they're shown in the same order
> regardless of the execution randomness.

The available sorters are:

* `noOp`: leaves the tests in the order of execution. This is the default, as you can see in the internal
[configuration.default.yaml]({{ site.repository_url }}/spectrum/src/main/resources/yaml/configuration.default.yaml){:target="_blank"}:

{% include copyCode.html %}

```yaml
extent:
sort: # How to sort tests in the produced report
noOp: { } # By default, no sort is applied
```

* `name`: sorts tests alphabetically by their name

{% include copyCode.html %}

```yaml
extent:
sort:
name: { }
```

* `status`: sorts tests by their status (passed, failed...). You can decide which to show first via the `weights` map.

{% include copyCode.html %}

```yaml
extent:
sort:
status:
weights: # Weights of tests statuses. A lower weight means the test is shown before those with a higher one in the Extent report
INFO: 10
PASS: 20
WARNING: 30
SKIP: 40
FAIL: 50
```

> ⚠️ **Default weights**<br/>
> The weights shown in the snippet above are the default, meaning **passed** tests are shown before **skipped** ones,
> which in turn are shown before those that **failed**. If this order is fine for you, there's no need to explicitly provide those weights. You can just write:

{% include copyCode.html %}

```yaml
extent:
sort:
status: { }
```

### Custom locators

Selenium doesn't provide any way to get a webElement's locator, by design. So, Spectrum extracts the locator from the `webElement.toString()`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Map;

import static java.util.Comparator.comparingLong;
import static java.util.function.Predicate.not;
import static org.junit.jupiter.api.Assertions.*;

@Slf4j
Expand Down Expand Up @@ -243,7 +244,7 @@ private FileReporter getSummaryReporterFrom(final Configuration configuration, f
private List<File> getRemainingFilesFrom(final File[] files, final String extension) {
return Arrays
.stream(files)
.filter(file -> !file.isDirectory())
.filter(not(File::isDirectory))
.filter(file -> file.getName().endsWith(extension))
.sorted(comparingLong(File::lastModified))
.toList();
Expand Down
2 changes: 2 additions & 0 deletions it/src/test/java/io/github/giulong/spectrum/it/data/Data.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
public class Data {

private Map<String, User> users;
private String checkboxEndpoint;
private String flashMessageId;

@Getter
public static class User {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@Slf4j
@Endpoint("login")
@SuppressWarnings("unused")
public class LoginPage extends SpectrumPage<LoginPage, Void> {
public class LoginPage extends SpectrumPage<LoginPage, Data> {

@FindBy(id = "flash")
private WebElement errorMessage;
Expand All @@ -38,7 +38,7 @@ public class LoginPage extends SpectrumPage<LoginPage, Void> {
@Override
public LoginPage waitForPageLoading() {
log.info("Wait for page loading: waiting for errorMessage to disappear");
implicitWait.until((ExpectedCondition<Boolean>) driver -> isNotPresent(id("flash")));
implicitWait.until((ExpectedCondition<Boolean>) driver -> isNotPresent(id(data.getFlashMessageId())));

return this;
}
Expand Down
10 changes: 10 additions & 0 deletions it/src/test/java/io/github/giulong/spectrum/it/tests/BaseIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.giulong.spectrum.it.tests;

import io.github.giulong.spectrum.SpectrumTest;
import io.github.giulong.spectrum.it.data.Data;

/**
* Dummy parent class to demonstrate inheritance works
*/
public abstract class BaseIT extends SpectrumTest<Data> {
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.giulong.spectrum.it.tests;

import io.github.giulong.spectrum.SpectrumTest;
import io.github.giulong.spectrum.it.data.Data;
import io.github.giulong.spectrum.it.pages.LoginPage;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -16,15 +16,21 @@
import static org.openqa.selenium.By.id;
import static org.openqa.selenium.support.ui.ExpectedConditions.urlContains;

@Slf4j
@DisplayName("Login Form")
@SuppressWarnings("unused")
public class LoginFormIT extends SpectrumTest<Data> {
public class LoginFormIT extends BaseIT {

private LoginPage loginPage;

@BeforeEach
public void beforeEach() {
log.info("Here just to check we're not overriding the internal beforeEach");
}

// Let's try with JUnit's parameterized tests
@DisplayName("Login Form leveraging the data.yaml")
@ParameterizedTest(name = "with user {0} we expect login to be successful: {1}")
@ParameterizedTest(name = "with user {0} we expect login to be successful {1}")
@MethodSource("valuesProvider")
public void shouldRunSuccessfully(final String userName, final boolean expected, final String endpoint) {
loginPage.open();
Expand Down
2 changes: 2 additions & 0 deletions it/src/test/resources/configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ extent:
theme: DARK
fileName: report.html
inline: true
sort:
name: { }

# these will fail since the slack consumer is lacking the token. We use them to check the log file and verify they're consumed
eventsConsumers:
Expand Down
3 changes: 3 additions & 0 deletions it/src/test/resources/data/data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ users:
giulio:
name: Giulio Longfils
password: pwd

checkboxEndpoint: https://the-internet.herokuapp.com/checkboxes
flashMessageId: flash
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public abstract class SpectrumEntity<T extends SpectrumEntity<T, Data>, Data> {
@Shared
StatefulExtentTest statefulExtentTest;

protected List<Field> getSharedFields() {
List<Field> getSharedFields() {
return Arrays
.stream(SpectrumEntity.class.getDeclaredFields())
.filter(f -> f.isAnnotationPresent(Shared.class))
Expand Down Expand Up @@ -288,7 +288,9 @@ public boolean isNotPresent(final By by) {
* @return true if the WebElement has the provided css class
*/
public boolean hasClass(final WebElement webElement, final String className) {
return Arrays.asList(webElement.getAttribute("class").split(" ")).contains(className);
final String attribute = webElement.getAttribute("class");

return attribute != null && Arrays.asList(attribute.split(" ")).contains(className);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ public boolean isLoaded() {
log.debug("Current url: {}", currentUrl);
log.debug("Page url: {}", pageUrl);

return currentUrl.equals(pageUrl);
return pageUrl.equals(currentUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import java.util.*;

import static java.util.function.Predicate.not;

@Slf4j
public class SpectrumSessionListener implements LauncherSessionListener {

Expand Down Expand Up @@ -81,7 +83,7 @@ protected List<String> parseProfiles() {
.ofNullable(yamlUtils.readNode(PROFILE_NODE, CONFIGURATION, String.class))
.orElse(yamlUtils.readInternalNode(PROFILE_NODE, DEFAULT_CONFIGURATION_YAML, String.class))
.split(","))
.filter(profile -> !profile.isBlank())
.filter(not(String::isBlank))
.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.github.giulong.spectrum.extensions.watchers.EventsWatcher;
import io.github.giulong.spectrum.interfaces.Endpoint;
import io.github.giulong.spectrum.interfaces.JsWebElement;
import io.github.giulong.spectrum.utils.TestContext;
import io.github.giulong.spectrum.types.*;
import io.github.giulong.spectrum.utils.*;
import io.github.giulong.spectrum.utils.events.EventsDispatcher;
Expand All @@ -21,11 +20,13 @@

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.util.Arrays.asList;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.toList;

@Slf4j
Expand Down Expand Up @@ -82,13 +83,15 @@ public abstract class SpectrumTest<Data> extends SpectrumEntity<SpectrumTest<Dat
@RegisterExtension
public final DataResolver<Data> dataResolver = new DataResolver<>();

protected List<SpectrumPage<?, Data>> spectrumPages;
protected List<SpectrumPage<?, ?>> spectrumPages;

private JsWebElementProxyBuilder jsWebElementProxyBuilder;

private final YamlUtils yamlUtils = YamlUtils.getInstance();

@BeforeEach
@SuppressWarnings({"checkstyle:ParameterNumber", "checkstyle:HiddenField", "unused"})
public void beforeEach(final TestContext testContext, final Configuration configuration, final TestData testData, final StatefulExtentTest statefulExtentTest,
void beforeEach(final TestContext testContext, final Configuration configuration, final TestData testData, final StatefulExtentTest statefulExtentTest,
final WebDriver driver, final ImplicitWait implicitWait, final PageLoadWait pageLoadWait, final ScriptWait scriptWait, final DownloadWait downloadWait,
final ExtentReports extentReports, final Actions actions, final EventsDispatcher eventsDispatcher, final Js js,
final JsWebElementProxyBuilder jsWebElementProxyBuilder, final Data data) {
Expand All @@ -111,7 +114,7 @@ public void beforeEach(final TestContext testContext, final Configuration config
initPages();
}

public void initPages() {
void initPages() {
final Class<?> clazz = this.getClass();
log.debug("Initializing pages of test '{}'", clazz.getSimpleName());

Expand All @@ -130,10 +133,12 @@ public void initPages() {
.filter(f -> SpectrumPage.class.isAssignableFrom(f.getType()))
.map(f -> initPage(f, sharedFields))
.collect(toList());

injectDataInPages();
}

@SneakyThrows
public SpectrumPage<?, Data> initPage(final Field spectrumPageField, final List<Field> sharedFields) {
SpectrumPage<?, Data> initPage(final Field spectrumPageField, final List<Field> sharedFields) {
log.debug("Initializing page {}", spectrumPageField.getName());

@SuppressWarnings("unchecked") final SpectrumPage<?, Data> spectrumPage = (SpectrumPage<?, Data>) spectrumPageField.getType().getDeclaredConstructor().newInstance();
Expand All @@ -156,7 +161,7 @@ public SpectrumPage<?, Data> initPage(final Field spectrumPageField, final List<
return spectrumPage;
}

protected void initJsWebElements(final SpectrumPage<?, Data> spectrumPage) {
void initJsWebElements(final SpectrumPage<?, Data> spectrumPage) {
final String className = spectrumPage.getClass().getSimpleName();

Arrays.stream(spectrumPage.getClass().getDeclaredFields())
Expand All @@ -167,7 +172,7 @@ protected void initJsWebElements(final SpectrumPage<?, Data> spectrumPage) {
}

@SneakyThrows
protected void setJsWebElementProxy(final Field field, final SpectrumPage<?, Data> spectrumPage) {
void setJsWebElementProxy(final Field field, final SpectrumPage<?, Data> spectrumPage) {
final Object value = field.get(spectrumPage);

if (value instanceof List<?>) {
Expand All @@ -187,4 +192,29 @@ protected void setJsWebElementProxy(final Field field, final SpectrumPage<?, Dat

field.set(spectrumPage, jsWebElementProxyBuilder.buildFor(value));
}

void injectDataInPages() {
if (data != null) {
log.debug("Data field was already injected from SpectrumTest");
return;
}

final List<SpectrumPage<?, ?>> dataSpectrumPages = spectrumPages
.stream()
.filter(not(spectrumPage -> Void.class.equals(Reflections.getGenericSuperclassOf(spectrumPage.getClass(), SpectrumPage.class).getActualTypeArguments()[1])))
.toList();

if (!dataSpectrumPages.isEmpty()) {
final Type type = Reflections.getGenericSuperclassOf(dataSpectrumPages.getFirst().getClass(), SpectrumPage.class).getActualTypeArguments()[1];
final String typeName = type.getTypeName();

@SuppressWarnings("unchecked") final Class<Data> dataClass = (Class<Data>) type;
final Data data = yamlUtils.read(String.format("%s/data.yaml", configuration.getData().getFolder()), dataClass);

dataSpectrumPages
.stream()
.peek(dataSpectrumPage -> log.trace("Running SpectrumTest<Void> with {}<{}>. Injecting data field.", dataSpectrumPage.getClass().getTypeName(), typeName))
.forEach(dataSpectrumPage -> Reflections.setField("data", dataSpectrumPage, data));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package io.github.giulong.spectrum.extensions.resolvers;

import io.github.giulong.spectrum.SpectrumTest;
import io.github.giulong.spectrum.utils.Configuration;
import io.github.giulong.spectrum.utils.Reflections;
import io.github.giulong.spectrum.utils.YamlUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import static io.github.giulong.spectrum.extensions.resolvers.ConfigurationResolver.CONFIGURATION;
Expand All @@ -27,8 +28,7 @@ public boolean supportsParameter(final ParameterContext parameterContext, final

@Override
public Data resolveParameter(final ParameterContext arg0, final ExtensionContext context) throws ParameterResolutionException {
final ParameterizedType dataType = (ParameterizedType) context.getRequiredTestClass().getGenericSuperclass();
final Type type = dataType.getActualTypeArguments()[0];
final Type type = Reflections.getGenericSuperclassOf(context.getRequiredTestClass(), SpectrumTest.class).getActualTypeArguments()[0];

if (Void.class.equals(type)) {
log.debug("Running an instance of SpectrumTest<Void>. No Data class injected in test '{}'", context.getDisplayName());
Expand Down
Loading

0 comments on commit 9f5f627

Please sign in to comment.