Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
f117cfe
build: changing central publishing plugin waitUntil to uploaded to av…
giulong Jan 16, 2026
2eac47f
build(deps): bump jackson.version from 2.20.1 to 2.21.0
dependabot[bot] Jan 19, 2026
767bf58
build(deps): bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.25
dependabot[bot] Jan 19, 2026
7a48bf6
Merge pull request #524 from giulong/dependabot/maven/develop/ch.qos.…
giulong Jan 19, 2026
96cf910
Merge pull request #523 from giulong/dependabot/maven/develop/jackson…
giulong Jan 19, 2026
cb60555
#154 - Adds a way to configure an instance of ElementLocatorFactory
pratiktiwari13 Jan 18, 2026
6cd7c16
refactor(#521): minor code cleaning during pr 521 review
giulong Jan 24, 2026
0aad97a
Merge pull request #521 from pratiktiwari13/feature/154-elementlocator
giulong Jan 24, 2026
7e4e292
test: cleaning mockConstruction with overloaded method that does not …
giulong Jan 24, 2026
26599f6
test: solving minor checkstyle warning
giulong Jan 24, 2026
68afed7
test: fixing stubs to cover missed branch
giulong Jan 24, 2026
524ae1a
refactor: finally leveraging JacksonInject.Optional
giulong Jan 24, 2026
c4282da
build(deps): bump ch.qos.logback:logback-classic from 1.5.25 to 1.5.26
dependabot[bot] Jan 26, 2026
2db8829
build(deps): bump com.diffplug.spotless:spotless-maven-plugin
dependabot[bot] Jan 26, 2026
aea0c49
Merge pull request #526 from giulong/dependabot/maven/develop/com.dif…
giulong Jan 27, 2026
d0cc819
Merge pull request #525 from giulong/dependabot/maven/develop/ch.qos.…
giulong Jan 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

<junit.version>6.0.2</junit.version>
<mockito.version>5.21.0</mockito.version>
<jackson.version>2.20.1</jackson.version>
<jackson.version>2.21.0</jackson.version>
<simplejavamail.version>8.12.6</simplejavamail.version>
<jsonschemagenerator.version>4.38.0</jsonschemagenerator.version>
<slf4j.version>2.0.17</slf4j.version>
Expand Down Expand Up @@ -217,7 +217,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.24</version>
<version>1.5.26</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -382,7 +382,7 @@
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<configuration>
<java>
<removeUnusedImports/>
Expand Down
2 changes: 1 addition & 1 deletion spectrum/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
<waitUntil>published</waitUntil>
<waitUntil>uploaded</waitUntil>
</configuration>
</plugin>
<!-- OSSRH -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.time.Duration;
import java.util.List;

import io.github.giulong.spectrum.interfaces.Endpoint;
Expand All @@ -17,7 +16,7 @@

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

@Slf4j
@Getter
Expand Down Expand Up @@ -85,8 +84,8 @@ SpectrumPage<?, Data> init() {
log.debug("The endpoint of page '{}' is '{}'", className, endpointValue);
Reflections.setField("endpoint", this, endpointValue);

final Duration autoWaitDuration = configuration.getDrivers().getWaits().getAuto().getTimeout();
PageFactory.initElements(new SpectrumFieldDecorator(new AjaxElementLocatorFactory(driver, (int) autoWaitDuration.toSeconds())), this);
final ElementLocatorFactory locatorFactory = configuration.getDrivers().getLocatorFactory().buildFor(driver);
PageFactory.initElements(new SpectrumFieldDecorator(locatorFactory), this);

Reflections
.getAnnotatedFields(this, JsWebElement.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.giulong.spectrum.element_locator_factories;

import java.time.Duration;

import com.fasterxml.jackson.annotation.JsonPropertyDescription;

import io.github.giulong.spectrum.interfaces.JsonSchemaTypes;
import io.github.giulong.spectrum.interfaces.LocatorFactory;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

@Slf4j
@Getter
@SuppressWarnings("unused")
public class AjaxLocatorFactory implements LocatorFactory {

@JsonPropertyDescription("Timeout in seconds")
@JsonSchemaTypes(double.class)
private Duration timeout;

@Override
public ElementLocatorFactory buildFor(final WebDriver driver) {
log.debug("Configuring AjaxElementLocatorFactory with a timeout of {}", timeout);
return new AjaxElementLocatorFactory(driver, (int) timeout.toSeconds());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.giulong.spectrum.element_locator_factories;

import io.github.giulong.spectrum.interfaces.LocatorFactory;

import lombok.extern.slf4j.Slf4j;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

@Slf4j
public class DefaultLocatorFactory implements LocatorFactory {

@Override
public ElementLocatorFactory buildFor(final WebDriver driver) {
log.debug("Configuring DefaultElementLocatorFactory");
return new DefaultElementLocatorFactory(driver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.giulong.spectrum.interfaces;

import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.WRAPPER_OBJECT;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import io.github.giulong.spectrum.element_locator_factories.AjaxLocatorFactory;
import io.github.giulong.spectrum.element_locator_factories.DefaultLocatorFactory;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

@JsonTypeInfo(use = NAME, include = WRAPPER_OBJECT)
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultLocatorFactory.class, name = "default"),
@JsonSubTypes.Type(value = AjaxLocatorFactory.class, name = "ajax"),
})
public interface LocatorFactory {
ElementLocatorFactory buildFor(WebDriver driver);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.giulong.spectrum.internals.jackson.deserializers.interpolation.interpolators;

import static com.fasterxml.jackson.annotation.OptBoolean.TRUE;

import java.util.Optional;

import com.fasterxml.jackson.annotation.JacksonInject;
Expand All @@ -12,7 +14,7 @@
public abstract class Interpolator {

@SuppressWarnings("unused")
@JacksonInject("enabledFromClient")
@JacksonInject(value = "enabledFromClient", optional = TRUE)
@JsonPropertyDescription("Whether to enable this interpolator. Injected to true by default, so no need to explicitly set it")
private boolean enabled;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.giulong.spectrum.utils;

import static com.fasterxml.jackson.annotation.OptBoolean.TRUE;
import static lombok.AccessLevel.PRIVATE;

import java.io.File;
Expand All @@ -17,6 +18,7 @@
import io.github.giulong.spectrum.drivers.Driver;
import io.github.giulong.spectrum.enums.Frame;
import io.github.giulong.spectrum.interfaces.JsonSchemaTypes;
import io.github.giulong.spectrum.interfaces.LocatorFactory;
import io.github.giulong.spectrum.internals.jackson.deserializers.interpolation.interpolators.EnvironmentInterpolator;
import io.github.giulong.spectrum.internals.jackson.deserializers.interpolation.interpolators.InPlaceInterpolator;
import io.github.giulong.spectrum.internals.jackson.deserializers.interpolation.interpolators.PropertiesInterpolator;
Expand Down Expand Up @@ -160,7 +162,7 @@ public static class Application {
public static class Highlight {

@JsonIgnore
@JacksonInject("enabledFromClient")
@JacksonInject(value = "enabledFromClient", optional = TRUE)
private boolean enabled;

@JsonPropertyDescription("Path to the js used to highlight. Relative to the resources folder")
Expand All @@ -172,7 +174,7 @@ public static class Highlight {
public static class VisualRegression {

@JsonIgnore
@JacksonInject("enabledFromClient")
@JacksonInject(value = "enabledFromClient", optional = TRUE)
private boolean enabled;

@JsonPropertyDescription("Whether to fail immediately when the first visual regression is found, rather than running the entire test")
Expand Down Expand Up @@ -275,6 +277,9 @@ public static class Drivers {
@JsonPropertyDescription("Driver's fluent waits")
private Waits waits;

@JsonPropertyDescription("An instance of ElementLocatorFactory to be used across Drivers")
private LocatorFactory locatorFactory;

@JsonPropertyDescription("Chrome capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/chrome/")
private Chrome chrome;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public Class<? extends Views> getViews() {

@Override
public InjectableValues getInjectableValues() {
return new InjectableValues.Std()
.addValue("enabledFromClient", false);
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.giulong.spectrum.utils.testbook;

import static com.fasterxml.jackson.annotation.OptBoolean.TRUE;
import static io.github.giulong.spectrum.enums.Result.*;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
Expand Down Expand Up @@ -38,7 +39,7 @@ public class TestBook implements SessionHook, Reportable {
private final FileUtils fileUtils = FileUtils.getInstance();

@JsonIgnore
@JacksonInject("enabledFromClient")
@JacksonInject(value = "enabledFromClient", optional = TRUE)
@SuppressWarnings("unused")
private boolean enabled;

Expand Down
5 changes: 5 additions & 0 deletions spectrum/src/main/resources/yaml/configuration.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ drivers:
enabled: true # Whether to enable the auto-wait
timeout: 30 # Timeout in seconds

# The ElementLocatorFactory to be used across Drivers
locatorFactory:
ajax:
timeout: 30

# Chrome capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/chrome/
chrome:
biDi: false # Whether to enable the BiDi protocol instead of CDP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.time.Duration;
import java.util.List;
import java.util.stream.Stream;

import io.github.giulong.spectrum.interfaces.JsWebElement;
import io.github.giulong.spectrum.interfaces.LocatorFactory;
import io.github.giulong.spectrum.interfaces.Secured;
import io.github.giulong.spectrum.internals.page_factory.SpectrumFieldDecorator;
import io.github.giulong.spectrum.utils.Configuration;
Expand All @@ -31,7 +31,7 @@
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

class SpectrumPageTest {

Expand Down Expand Up @@ -62,15 +62,6 @@ class SpectrumPageTest {
@Mock
private Configuration.Drivers drivers;

@Mock
private Configuration.Drivers.Waits waits;

@Mock
private Configuration.Drivers.Waits.AutoWait auto;

@Mock
private Duration timeout;

@Mock
private Configuration.Application application;

Expand Down Expand Up @@ -149,23 +140,18 @@ static Stream<Arguments> valuesProvider() {
@DisplayName("init should set the endpoint, init the web elements, add the secured web elements, set the js web elements, and return the page instance")
void init() {
final WebElement proxy = mock();
final long seconds = 123L;
final LocatorFactory locatorFactory = mock();
final ElementLocatorFactory elementLocatorFactory = mock();

when(jsWebElementProxyBuilder.buildFor(webElementArgumentCaptor.capture())).thenReturn(proxy);
when(JsWebElementListInvocationHandler.builder()).thenReturn(jsWebElementListInvocationHandlerBuilder);

when(configuration.getDrivers()).thenReturn(drivers);
when(drivers.getWaits()).thenReturn(waits);
when(waits.getAuto()).thenReturn(auto);
when(auto.getTimeout()).thenReturn(timeout);
when(timeout.toSeconds()).thenReturn(seconds);

final MockedConstruction<AjaxElementLocatorFactory> factoryMockedConstruction = mockConstruction(AjaxElementLocatorFactory.class, (mock, context) -> {
assertEquals(webDriver, context.arguments().getFirst());
assertEquals((int) seconds, context.arguments().get(1));
});
when(drivers.getLocatorFactory()).thenReturn(locatorFactory);
when(locatorFactory.buildFor(webDriver)).thenReturn(elementLocatorFactory);

final MockedConstruction<SpectrumFieldDecorator> decoratorMockedConstruction = mockConstruction(SpectrumFieldDecorator.class);
final MockedConstruction<SpectrumFieldDecorator> decoratorMockedConstruction = mockConstruction(
(mock, context) -> assertEquals(elementLocatorFactory, context.arguments().getFirst()));

assertEquals(spectrumPage, spectrumPage.init());

Expand All @@ -176,7 +162,6 @@ void init() {

assertEquals(proxy, Reflections.getFieldValue("jsWebElement", spectrumPage));

factoryMockedConstruction.close();
decoratorMockedConstruction.close();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import java.time.Duration;
import java.util.List;

import com.aventstack.extentreports.ExtentReports;
Expand All @@ -14,6 +13,7 @@
import io.github.giulong.spectrum.exceptions.TestFailedException;
import io.github.giulong.spectrum.interfaces.Endpoint;
import io.github.giulong.spectrum.interfaces.JsWebElement;
import io.github.giulong.spectrum.interfaces.LocatorFactory;
import io.github.giulong.spectrum.types.DownloadWait;
import io.github.giulong.spectrum.types.ImplicitWait;
import io.github.giulong.spectrum.types.PageLoadWait;
Expand Down Expand Up @@ -41,6 +41,7 @@
import org.openqa.selenium.bidi.module.LogInspector;
import org.openqa.selenium.bidi.module.Network;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

class SpectrumTestTest {

Expand Down Expand Up @@ -90,15 +91,6 @@ class SpectrumTestTest {
@Mock
private Configuration.Drivers drivers;

@Mock
private Configuration.Drivers.Waits waits;

@Mock
private Configuration.Drivers.Waits.AutoWait auto;

@Mock
private Duration timeout;

@Mock
private ExtentReports extentReports;

Expand Down Expand Up @@ -176,17 +168,14 @@ void testBeforeAll() {
void testBeforeEach() {
// injectDataIn
final String folder = "folder";
final LocatorFactory locatorFactory = mock();
final ElementLocatorFactory elementLocatorFactory = mock();
when(configuration.getData()).thenReturn(dataConfiguration);
when(dataConfiguration.getFolder()).thenReturn(folder);
when(yamlUtils.readClient(folder + "/data.yaml", FakeData.class)).thenReturn(data);

final long seconds = 123L;
when(configuration.getDrivers()).thenReturn(drivers);
when(drivers.getWaits()).thenReturn(waits);
when(waits.getAuto()).thenReturn(auto);
when(auto.getTimeout()).thenReturn(timeout);
when(timeout.toSeconds()).thenReturn(seconds);

when(drivers.getLocatorFactory()).thenReturn(locatorFactory);
when(locatorFactory.buildFor(driver)).thenReturn(elementLocatorFactory);
when(statefulExtentTest.getCurrentNode()).thenReturn(extentTest);

assertNull(childTestVoid.childTestPage);
Expand Down Expand Up @@ -247,12 +236,11 @@ void testBaseSpectrumAfterEachThrows() {
@Test
@DisplayName("injectPages should init also init pages from super classes")
void injectPages() {
final long seconds = 123L;
final LocatorFactory locatorFactory = mock();
final ElementLocatorFactory elementLocatorFactory = mock();
when(configuration.getDrivers()).thenReturn(drivers);
when(drivers.getWaits()).thenReturn(waits);
when(waits.getAuto()).thenReturn(auto);
when(auto.getTimeout()).thenReturn(timeout);
when(timeout.toSeconds()).thenReturn(seconds);
when(drivers.getLocatorFactory()).thenReturn(locatorFactory);
when(locatorFactory.buildFor(driver)).thenReturn(elementLocatorFactory);

assertNull(childTest.childTestPage);
assertNull(childTest.getParentTestPage());
Expand Down
Loading
Loading