Skip to content

Commit 42af04b

Browse files
authored
Feature/9 element action retrier (#26)
* #9 added ElementActionRetrier + tests added IUtilitiesModule * #9 removed using of DI in tests combined configuration tests in one class * #9 fixed comments * merge with master
1 parent 68d9114 commit 42af04b

15 files changed

+335
-103
lines changed
Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package aquality.selenium.core.application;
22

3+
import aquality.selenium.core.configurations.*;
34
import aquality.selenium.core.localization.*;
45
import aquality.selenium.core.logging.Logger;
6+
import aquality.selenium.core.utilities.IElementActionRetrier;
57
import aquality.selenium.core.utilities.ISettingsFile;
6-
import aquality.selenium.core.utilities.JsonSettingsFile;
7-
import aquality.selenium.core.configurations.*;
8+
import aquality.selenium.core.utilities.IUtilitiesModule;
89
import com.google.inject.AbstractModule;
910
import com.google.inject.Provider;
1011
import com.google.inject.Singleton;
@@ -13,7 +14,7 @@
1314
* Describes all dependencies which is registered for the project.
1415
*/
1516
public class AqualityModule<T extends IApplication> extends AbstractModule
16-
implements ILocalizationModule {
17+
implements ILocalizationModule, IUtilitiesModule {
1718

1819
private final Provider<T> applicationProvider;
1920

@@ -27,26 +28,14 @@ public AqualityModule (Provider<T> applicationProvider) {
2728
@Override
2829
protected void configure() {
2930
bind(IApplication.class).toProvider(applicationProvider);
30-
bind(ISettingsFile.class).toInstance(getSettings());
31+
bind(ISettingsFile.class).toInstance(getInstanceOfSettingsFile());
3132
bind(Logger.class).toInstance(Logger.getInstance());
3233
bind(ILoggerConfiguration.class).to(LoggerConfiguration.class).in(Singleton.class);
3334
bind(ITimeoutConfiguration.class).to(TimeoutConfiguration.class).in(Singleton.class);
3435
bind(IRetryConfiguration.class).to(RetryConfiguration.class).in(Singleton.class);
3536
bind(IElementCacheConfiguration.class).to(ElementCacheConfiguration.class).in(Singleton.class);
37+
bind(IElementActionRetrier.class).to(getElementActionRetrierImplementation()).in(Singleton.class);
3638
bind(ILocalizationManager.class).to(getLocalizationManagerImplementation()).in(Singleton.class);
3739
bind(ILocalizedLogger.class).to(getLocalizedLoggerImplementation()).in(Singleton.class);
3840
}
39-
40-
/**
41-
* Provides default {@link ISettingsFile}. with settings.
42-
* Default value is settings.json.
43-
* You are able to override this path, by setting environment variable 'profile'.
44-
* In this case, settings file will be settings.{profile}.json.
45-
*
46-
* @return An instance of settings.
47-
*/
48-
protected ISettingsFile getSettings() {
49-
String settingsProfile = System.getProperty("profile") == null ? "settings.json" : "settings." + System.getProperty("profile") + ".json";
50-
return new JsonSettingsFile(settingsProfile);
51-
}
5241
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package aquality.selenium.core.utilities;
2+
3+
import aquality.selenium.core.configurations.IRetryConfiguration;
4+
import com.google.inject.Inject;
5+
6+
import java.util.Optional;
7+
import java.util.function.Supplier;
8+
9+
public class ElementActionRetrier implements IElementActionRetrier {
10+
11+
private IRetryConfiguration retryConfiguration;
12+
13+
@Inject
14+
public ElementActionRetrier(IRetryConfiguration retryConfiguration) {
15+
this.retryConfiguration = retryConfiguration;
16+
}
17+
18+
@Override
19+
public void doWithRetry(Runnable runnable) {
20+
Supplier<?> supplier = () -> {
21+
runnable.run();
22+
return true;
23+
};
24+
doWithRetry(supplier);
25+
}
26+
27+
@Override
28+
public <T> T doWithRetry(Supplier<T> function) {
29+
int retryAttemptsLeft = retryConfiguration.getNumber();
30+
Optional<T> result = Optional.empty();
31+
while (retryAttemptsLeft >= 0) {
32+
try {
33+
result = Optional.of(function.get());
34+
break;
35+
} catch (Exception exception) {
36+
if (isExceptionHandled(exception) && retryAttemptsLeft != 0) {
37+
try {
38+
Thread.sleep(retryConfiguration.getPollingInterval());
39+
} catch (InterruptedException e) {
40+
Thread.currentThread().interrupt();
41+
}
42+
retryAttemptsLeft--;
43+
} else {
44+
throw exception;
45+
}
46+
}
47+
}
48+
return result.orElse(null);
49+
}
50+
51+
protected boolean isExceptionHandled(Exception exception) {
52+
return getHandledExceptions().contains(exception.getClass());
53+
}
54+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package aquality.selenium.core.utilities;
2+
3+
import org.openqa.selenium.InvalidElementStateException;
4+
import org.openqa.selenium.StaleElementReferenceException;
5+
6+
import java.util.Arrays;
7+
import java.util.List;
8+
import java.util.function.Supplier;
9+
10+
/**
11+
* Retries an action or function when {@link #getHandledExceptions()} occurs.
12+
*/
13+
public interface IElementActionRetrier {
14+
15+
/**
16+
* Retries the action when the handled exception {@link #getHandledExceptions()} occurs.
17+
* @param runnable Action to be applied.
18+
*/
19+
void doWithRetry(Runnable runnable);
20+
21+
/**
22+
* Retries the function when the handled exception {@link #getHandledExceptions()} occurs.
23+
* @param function Function to be applied.
24+
* @param <T> Return type of function.
25+
* @return Result of the function.
26+
*/
27+
<T> T doWithRetry(Supplier<T> function);
28+
29+
/**
30+
* Exceptions to be ignored during action retrying.
31+
* @return By the default implementation, {@link StaleElementReferenceException} and {@link InvalidElementStateException} are handled.
32+
*/
33+
default List<Class<? extends Exception>> getHandledExceptions() {
34+
return Arrays.asList(StaleElementReferenceException.class, InvalidElementStateException.class);
35+
}
36+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package aquality.selenium.core.utilities;
2+
3+
/**
4+
* Provides implementations for utilities module.
5+
*/
6+
public interface IUtilitiesModule {
7+
8+
/**
9+
* @return implementation of {@link IElementActionRetrier}.
10+
*/
11+
default Class<? extends IElementActionRetrier> getElementActionRetrierImplementation() {
12+
return ElementActionRetrier.class;
13+
}
14+
15+
/**
16+
* Provides default {@link ISettingsFile} with settings.
17+
* Default value is settings.json.
18+
* You are able to override this path, by setting environment variable 'profile'.
19+
* In this case, settings file will be settings.{profile}.json.
20+
*
21+
* @return An instance of settings.
22+
*/
23+
default ISettingsFile getInstanceOfSettingsFile() {
24+
String settingsProfile = System.getProperty("profile") == null ? "settings.json" : "settings." + System.getProperty("profile") + ".json";
25+
return new JsonSettingsFile(settingsProfile);
26+
}
27+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package tests.configurations;
2+
3+
import aquality.selenium.core.configurations.*;
4+
import aquality.selenium.core.utilities.ISettingsFile;
5+
import org.testng.annotations.Test;
6+
import tests.application.CustomAqualityServices;
7+
8+
import static org.testng.Assert.assertEquals;
9+
import static org.testng.Assert.assertFalse;
10+
11+
public class ConfigurationTests {
12+
private static final ISettingsFile settingsFile = CustomAqualityServices.getServiceProvider().getInstance(ISettingsFile.class);
13+
14+
@Test
15+
public void testShouldBePossibleCheckIsEnableElementCache() {
16+
boolean isEnable = new ElementCacheConfiguration(settingsFile).isEnabled();
17+
assertFalse(isEnable, "Element cache is disabled by default");
18+
}
19+
20+
@Test
21+
public void testShouldBePossibleToGetLanguage() {
22+
String language = new LoggerConfiguration(settingsFile).getLanguage();
23+
assertEquals(language, "en", "Current language should be got from logger configuration");
24+
}
25+
26+
@Test
27+
public void testShouldBePossibleToGetRetryConfiguration() {
28+
RetryConfiguration retryConfiguration = new RetryConfiguration(settingsFile);
29+
assertEquals(retryConfiguration.getNumber(), 2, "Number of retry attempts timeout should be got");
30+
assertEquals(retryConfiguration.getPollingInterval(), 300, "Polling interval of retrier should be got");
31+
}
32+
33+
@Test
34+
public void testShouldBePossibleToGetTimeoutConfiguration() {
35+
TimeoutConfiguration timeoutConfiguration = new TimeoutConfiguration(settingsFile);
36+
assertEquals(timeoutConfiguration.getCommand(), 60, "Command timeout should be got");
37+
assertEquals(timeoutConfiguration.getCondition(), 30, "Condition timeout should be got");
38+
assertEquals(timeoutConfiguration.getImplicit(), 0, "Implicit timeout should be got");
39+
assertEquals(timeoutConfiguration.getPollingInterval(), 300, "Polling interval should be got");
40+
}
41+
}

src/test/java/tests/configurations/ElementCacheConfigurationTests.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/test/java/tests/configurations/EnvConfigurationTests.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package tests.configurations;
22

33
import aquality.selenium.core.application.AqualityModule;
4-
import aquality.selenium.core.configurations.IElementCacheConfiguration;
5-
import aquality.selenium.core.configurations.ILoggerConfiguration;
6-
import aquality.selenium.core.configurations.IRetryConfiguration;
7-
import aquality.selenium.core.configurations.ITimeoutConfiguration;
4+
import aquality.selenium.core.configurations.*;
5+
import aquality.selenium.core.utilities.ISettingsFile;
86
import org.testng.Assert;
97
import org.testng.annotations.AfterMethod;
108
import org.testng.annotations.BeforeMethod;
@@ -25,6 +23,7 @@ public class EnvConfigurationTests extends BaseProfileTest {
2523
private static final String CONDITION_TIMEOUT_KEY = "timeouts.timeoutCondition";
2624
private static final String NEW_INT_VALUE = "10000";
2725
private static final String RETRY_NUMBER_KEY = "retry.number";
26+
private ISettingsFile settingsFile;
2827

2928
@BeforeMethod
3029
public void before() {
@@ -33,29 +32,30 @@ public void before() {
3332
System.setProperty(RETRY_NUMBER_KEY, NEW_INT_VALUE);
3433
System.setProperty(ELEMENT_CACHE, NEW_BOOL_VALUE);
3534
CustomAqualityServices.initInjector(new TestModule(CustomAqualityServices::getApplication));
35+
settingsFile = CustomAqualityServices.getServiceProvider().getInstance(ISettingsFile.class);
3636
}
3737

3838
@Test
3939
public void testShouldBePossibleToOverrideLanguageWithEnvVariable() {
40-
String language = CustomAqualityServices.getServiceProvider().getInstance(ILoggerConfiguration.class).getLanguage();
40+
String language = new LoggerConfiguration(settingsFile).getLanguage();
4141
assertEquals(language, "ru", "Current language should be overridden with env variable");
4242
}
4343

4444
@Test
4545
public void testShouldBePossibleToOverrideTimeoutWithEnvVariable() {
46-
long conditionTimeout = CustomAqualityServices.getServiceProvider().getInstance(ITimeoutConfiguration.class).getCondition();
46+
long conditionTimeout = new TimeoutConfiguration(settingsFile).getCondition();
4747
assertEquals(conditionTimeout, Long.parseLong(NEW_INT_VALUE), "Condition timeout should be overridden with env variable");
4848
}
4949

5050
@Test
5151
public void testShouldBePossibleToOverrideRetryConfigurationWithEnvVariable() {
52-
int retryNumber = CustomAqualityServices.getServiceProvider().getInstance(IRetryConfiguration.class).getNumber();
52+
int retryNumber = new RetryConfiguration(settingsFile).getNumber();
5353
assertEquals(retryNumber, Long.parseLong(NEW_INT_VALUE), "Number of retry attempts should be overridden with env variable");
5454
}
5555

5656
@Test
5757
public void testShouldBePossibleToOverrideElementCacheStateWithEnvVariable() {
58-
boolean isEnabled = CustomAqualityServices.getServiceProvider().getInstance(IElementCacheConfiguration.class).isEnabled();
58+
boolean isEnabled = new ElementCacheConfiguration(settingsFile).isEnabled();
5959
assertTrue(isEnabled, "Element cache state should be overridden with env variable");
6060
}
6161

@@ -76,7 +76,7 @@ public void testShouldGetDefaultLanguageIfConfigurationIsAbsent() {
7676
System.setProperty(PROFILE_KEY, "empty");
7777
System.clearProperty(LANGUAGE_KEY);
7878
CustomAqualityServices.initInjector(new TestModule(CustomAqualityServices::getApplication));
79-
String language = CustomAqualityServices.getServiceProvider().getInstance(ILoggerConfiguration.class).getLanguage();
79+
String language = new LoggerConfiguration(settingsFile).getLanguage();
8080
assertEquals(language, DEFAULT_LANGUAGE, "Current language should be got from logger configuration");
8181
}
8282

src/test/java/tests/configurations/LoggerConfigurationTests.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/test/java/tests/configurations/ProfileConfigurationTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package tests.configurations;
22

33
import aquality.selenium.core.application.AqualityModule;
4-
import aquality.selenium.core.configurations.ILoggerConfiguration;
4+
import aquality.selenium.core.configurations.LoggerConfiguration;
5+
import aquality.selenium.core.utilities.ISettingsFile;
56
import org.testng.annotations.BeforeMethod;
67
import org.testng.annotations.Test;
78
import tests.application.CustomAqualityServices;
@@ -20,7 +21,7 @@ public void before() {
2021

2122
@Test
2223
public void testShouldBePossibleToGetLanguageFromNewSettingsFile() {
23-
String language = CustomAqualityServices.getServiceProvider().getInstance(ILoggerConfiguration.class).getLanguage();
24+
String language = new LoggerConfiguration(CustomAqualityServices.getServiceProvider().getInstance(ISettingsFile.class)).getLanguage();
2425
assertEquals(language, "ru", String.format("Current language should be got from %s profile", PROFILE));
2526
}
2627
}

src/test/java/tests/configurations/RetryConfigurationTests.java

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)