Skip to content

Commit f0b2d28

Browse files
authored
Merge pull request #499 from dougnoel/498-clear-config
Add ability to clear config
2 parents 9160a8c + 027206b commit f0b2d28

File tree

16 files changed

+208
-53
lines changed

16 files changed

+208
-53
lines changed

.github/workflows/maven.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ jobs:
5757
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
5858
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
5959
run: mvn -B org.jacoco:jacoco-maven-plugin:prepare-agent verify org.jacoco:jacoco-maven-plugin:report org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dheadless -DrecordTests -Dtimeout=20
60-
- name: Attach screen recordings from build
60+
- name: Attach reports folder from build
6161
if: ${{ failure() }}
6262
continue-on-error: true
6363
uses: actions/upload-artifact@v2
6464
with:
65-
name: ScreenRecordings
66-
path: ${{ github.workspace }}/reports/*.avi
65+
name: ReportsFolder
66+
path: ${{ github.workspace }}/reports

src/main/java/com/dougnoel/sentinel/configurations/Configuration.java

+33-18
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ public class Configuration {
3636
private static final Map<String,YAMLData> YAML_DATA = new ConcurrentHashMap<>();
3737

3838
private static final String ENV_REPLACE_STRING = "{env}";
39-
40-
private static Properties appProps = new Properties();
39+
private static final String ENV = "env";
40+
41+
private static final Properties appProps = new Properties();
4142

4243
private static ConfigurationData sentinelConfigurations = null;
4344

@@ -79,8 +80,8 @@ private static String getOrCreateConfigurationData(String configurationKey) {
7980
throw new FileException(errorMessage, e, CONFIGURATION_FILE);
8081
}
8182
}
82-
83-
return sentinelConfigurations.getConfigurationValue(environment(), configurationKey);
83+
84+
return sentinelConfigurations.getConfigurationValue(environment(), configurationKey);
8485
}
8586

8687
/**
@@ -100,20 +101,25 @@ private static String getOrCreateConfigurationData(String configurationKey) {
100101
public static String toString(String property) {
101102
String propertyValue = appProps.getProperty(property);
102103

104+
if(propertyValue == null)
105+
propertyValue = System.getProperty(property);
106+
103107
if(propertyValue == null) {
104108
try {
105-
propertyValue = System.getProperty(property);
106-
if(propertyValue == null) {
107-
propertyValue = getOrCreateConfigurationData(property);
109+
if (property.contentEquals(ENV)) {
110+
String warningMessage = "Configuration value 'env' not found in runtime or command-line configuration. Default value will be used.";
111+
log.warn(warningMessage);
112+
return null;
108113
}
109-
appProps.setProperty(property, propertyValue);
110-
return propertyValue;
114+
propertyValue = getOrCreateConfigurationData(property);
111115
} catch (FileException e) {
112-
log.trace(e.getMessage(),Arrays.toString(e.getStackTrace()));
116+
log.trace(e.getMessage(), Arrays.toString(e.getStackTrace()));
113117
return null;
114118
}
115119
}
116-
120+
121+
if(propertyValue != null)
122+
appProps.setProperty(property, propertyValue);
117123
return propertyValue;
118124
}
119125

@@ -136,11 +142,10 @@ public static String toString(String property, String defaultValue) {
136142
String propertyValue = toString(property);
137143
if (propertyValue == null) {
138144
appProps.setProperty(property, defaultValue);
139-
if (property.contentEquals("env")) {
140-
String warningMessage = SentinelStringUtils.format("localhost env being used by default. {}",
141-
Configuration.configurationNotFoundErrorMessage("env"));
142-
log.warn(warningMessage);
143-
}
145+
String warningMessage = SentinelStringUtils.format("{} being used by default for configuration property {}. {}",
146+
defaultValue, property, Configuration.configurationNotFoundErrorMessage(property));
147+
log.warn(warningMessage);
148+
144149
return defaultValue;
145150
}
146151
return propertyValue;
@@ -166,6 +171,11 @@ public static void update(String property, String value) {
166171
public static void clear(String property) {
167172
appProps.remove(property);
168173
}
174+
175+
/**
176+
* Clears all configuration values that have been set since runtime started.
177+
*/
178+
public static void clearAllSessionAppProps() { appProps.clear(); }
169179

170180
/**
171181
* Returns the given configuration value stored in the passed property as a Double, or 0.0 if nothing is
@@ -251,7 +261,7 @@ public static void update(String property, long value) {
251261
* @return String text of system env info
252262
*/
253263
public static String environment() {
254-
return toString("env", "localhost");
264+
return toString(ENV, "localhost");
255265
}
256266

257267
/**
@@ -547,7 +557,12 @@ public static String[] getPageParts(String pageName) {
547557
* @return String the formatted error message
548558
*/
549559
private static String configurationNotFoundErrorMessage(String configurtaionValue) {
550-
return SentinelStringUtils.format("No {} property set. This can be set in the sentinel.yml config file with a '{}=' property or on the command line with the switch '-D{}='.", configurtaionValue, configurtaionValue, configurtaionValue);
560+
if(configurtaionValue.equalsIgnoreCase(ENV))
561+
return SentinelStringUtils.format("No {} property set. This can be set on the command line with the switch '-D{}='.",
562+
configurtaionValue);
563+
else
564+
return SentinelStringUtils.format("No {} property set. This can be set in the sentinel.yml config file with a '{}=' property or on the command line with the switch '-D{}='.",
565+
configurtaionValue, configurtaionValue, configurtaionValue);
551566
}
552567

553568
/**

src/main/java/com/dougnoel/sentinel/elements/Element.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private WebElement getElement(final By locator) {
115115
try {
116116
return driver().findElement(locator);
117117
}
118-
catch (TimeoutException | StaleElementReferenceException | NoSuchElementException | InvalidArgumentException e) {
118+
catch (TimeoutException | StaleElementReferenceException | NoSuchElementException | InvalidArgumentException | NoSuchWindowException e) {
119119
return null;
120120
}
121121
}
@@ -198,7 +198,8 @@ protected WebElement element(final By locator) {
198198
.pollingEvery(Time.interval())
199199
.ignoring(org.openqa.selenium.NoSuchElementException.class)
200200
.ignoring(StaleElementReferenceException.class)
201-
.ignoring(InvalidArgumentException.class);
201+
.ignoring(InvalidArgumentException.class)
202+
.ignoring(NoSuchWindowException.class);
202203

203204
return wait.until(d -> element().findElement(locator));
204205
} catch (org.openqa.selenium.TimeoutException e) {
@@ -231,7 +232,7 @@ protected WebElement findElementInIFrame() {
231232
driver().switchTo().parentFrame();
232233
}
233234
}
234-
catch(StaleElementReferenceException | NoSuchFrameException | InvalidArgumentException e) {
235+
catch(StaleElementReferenceException | NoSuchFrameException | InvalidArgumentException | NoSuchWindowException e) {
235236
var errorMessage = SentinelStringUtils.format("Error when searching for {} element named \"{}\" while attempting to search through iFrames. Looping again. Error: {}",
236237
elementType, getName(), e);
237238
log.trace(errorMessage);
@@ -461,7 +462,8 @@ private FluentWait<WebDriver> constructElementWait(Duration timeout) {
461462
.pollingEvery(Time.interval())
462463
.ignoring(NoSuchElementException.class)
463464
.ignoring(StaleElementReferenceException.class)
464-
.ignoring(InvalidArgumentException.class);
465+
.ignoring(InvalidArgumentException.class)
466+
.ignoring(NoSuchWindowException.class);
465467
}
466468

467469
/**
@@ -635,6 +637,9 @@ public boolean doesNotExist() {
635637
} catch (StaleElementReferenceException e) {
636638
log.trace("doesNotExist() StaleElementException return result: true");
637639
return true;
640+
} catch(InvalidArgumentException | NoSuchWindowException e){
641+
log.trace("Unable to determine existence of element. Retrying.");
642+
return doesNotExist();
638643
}
639644
}
640645
log.trace("doesNotExist() return result: false");
@@ -898,7 +903,7 @@ private Color getBackgroundColor(WebElement element) {
898903
* Get the value of the given attribute of the element. Will return the current value,
899904
* even if this has been modified after the page has been loaded. <br>
900905
* More exactly, this method will return the value of the property with the given name,
901-
* if it exists. If it does not, then the value of the attribute with the given name is returned.
906+
* if it exists. If it does not, then the value of the attribute with the given name is returned.
902907
* If neither exists, null is returned. <br>
903908
* The "style" attribute is converted as best can be to a text representation with a trailingsemi-colon. <br>
904909
* The following are deemed to be "boolean" attributes, and will return either "true" or null:
@@ -946,4 +951,14 @@ public By getBy() {
946951
return createByLocator(selectorType, selector);
947952
}
948953

954+
/**
955+
* Performs exactly 1 sweep across the DOM for the element (includes all selectors).
956+
* Does not loop for the full timeout.
957+
* @return boolean true if the element is found. false otherwise.
958+
*/
959+
public boolean existsAtThisInstant(){
960+
driver().switchTo().defaultContent();
961+
return findElementInCurrentFrame() != null || findElementInIFrame() != null;
962+
}
963+
949964
}

src/main/java/com/dougnoel/sentinel/elements/ElementFunctions.java

+27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.dougnoel.sentinel.elements;
22

3+
import com.dougnoel.sentinel.configurations.Time;
4+
import com.dougnoel.sentinel.webdrivers.WebDriverFactory;
35
import org.apache.logging.log4j.LogManager;
46
import org.apache.logging.log4j.Logger;
57

@@ -8,6 +10,8 @@
810
import com.dougnoel.sentinel.elements.tables.Table;
911
import com.dougnoel.sentinel.pages.PageManager;
1012
import com.dougnoel.sentinel.strings.SentinelStringUtils;
13+
import org.openqa.selenium.*;
14+
import org.openqa.selenium.support.ui.FluentWait;
1115

1216
/**
1317
* Retrieves as an element as an Element or as a child type.
@@ -100,4 +104,27 @@ private static String buildClassCastExceptionMessage(String elementName, String
100104
log.error(errorMessage);
101105
return errorMessage;
102106
}
107+
108+
/**
109+
* Waits the configured timeout for either of the two elements to be found. Does not take into consideration visibility of elements.
110+
* Does not take into consideration which element is found first.
111+
* @param elementName String an element to search for.
112+
* @param otherElementName String another element to search for.
113+
* @return boolean true if either of the elements are found within the configured timeout. false otherwise.
114+
*/
115+
public static boolean waitForEitherElementToExist(String elementName, String otherElementName){
116+
var wait = new FluentWait<>(WebDriverFactory.getWebDriver())
117+
.withTimeout(Time.out())
118+
.pollingEvery(Time.interval())
119+
.ignoring(WebDriverException.class);
120+
121+
try{
122+
wait.until(x -> getElement(elementName).existsAtThisInstant() || getElement(otherElementName).existsAtThisInstant());
123+
return true;
124+
}
125+
catch(TimeoutException e){
126+
log.trace(SentinelStringUtils.format("Timed out waiting for either {} or {} to exist.", elementName, otherElementName), e);
127+
return false;
128+
}
129+
}
103130
}

src/main/java/com/dougnoel/sentinel/steps/VerificationSteps.java

+17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.junit.Assert.*;
55

66
import com.dougnoel.sentinel.configurations.Configuration;
7+
import com.dougnoel.sentinel.elements.ElementFunctions;
78
import com.dougnoel.sentinel.math.Decimal;
89
import org.apache.commons.lang3.StringUtils;
910

@@ -391,7 +392,23 @@ else if(relativeLocation.contains("right")){
391392
else{
392393
assertTrue(expectedResult, element1point.x < element2point.x);
393394
}
395+
}
394396

397+
/**
398+
* Verifies one of two given elements is present on the page. Does not check for visibility of elements.
399+
* Useful for when a certain action may produce two different results on a page and either is acceptable for test to continue.
400+
* <p>
401+
* <b>Gherkin Example:</b>
402+
* <ul>
403+
* <li>I verify either the client table or the no rows found warning exist</li>
404+
* </ul>
405+
* @param elementName
406+
* @param otherElementName
407+
*/
408+
@Then("^I verify either (?:the|a|an) (.*?) or (?:the|a|an) (.*?) exist$")
409+
public static void verifyOneOfTwoElementsIsFound(String elementName, String otherElementName){
410+
String expectedResult = SentinelStringUtils.format("Expected either the {} or the {} to exist. Neither was found.", elementName, otherElementName);
411+
assertTrue(expectedResult, ElementFunctions.waitForEitherElementToExist(elementName, otherElementName));
395412
}
396413

397414
}

src/main/java/com/dougnoel/sentinel/system/DownloadManager.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -401,14 +401,15 @@ public static String createDownloadDirectory() {
401401
Path downloadPath = Path.of(parentDirectoryPath, "/downloads/");
402402
downloadPath.toFile().mkdir(); //make download directory if it doesn't already exist
403403
downloadDirectory = downloadPath.toString();
404+
log.trace("Setting download directory to {}", downloadDirectory);
404405
}
405406
return downloadDirectory;
406407
}
407408

408409
/**
409410
* Sets given downloadDirectory object
410411
*
411-
* @param downloadDirectory String the downdloadDirectory to set
412+
* @param downloadDirectory String the downloadDirectory to set
412413
*/
413414
public static void setDownloadDirectory(String downloadDirectory) {
414415
DownloadManager.downloadDirectory = downloadDirectory;
@@ -419,6 +420,7 @@ public static void setDownloadDirectory(String downloadDirectory) {
419420
* @throws IOException in the case that an IOException occurs while clearing the directory
420421
*/
421422
public static void clearDownloadDirectory() throws IOException {
423+
log.trace("Clearing download directory at {}", downloadDirectory);
422424
FileUtils.cleanDirectory(new File(downloadDirectory));
423425
}
424426

src/main/java/com/dougnoel/sentinel/webdrivers/WebDriverFactory.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ public static boolean exists() {
127127
* Sets the download directory for chromedriver. Cannot be used with Saucelabs.
128128
* @param options ChromeOptions object to set
129129
*/
130-
private static void setChromeDownloadDirectory(ChromeOptions options) {
130+
private static ChromeOptions setChromeDownloadDirectory(ChromeOptions options) {
131131
HashMap<String, Object> chromePrefs = new HashMap<>();
132132
chromePrefs.put("download.prompt_for_download", false);
133133
chromePrefs.put("download.default_directory", DownloadManager.getDownloadDirectory());
134-
options.setExperimentalOption("prefs", chromePrefs);
134+
return options.setExperimentalOption("prefs", chromePrefs);
135135
}
136136

137137
/**
@@ -140,13 +140,11 @@ private static void setChromeDownloadDirectory(ChromeOptions options) {
140140
* @return WebDriver ChromeDrvier
141141
*/
142142
private static WebDriver createChromeDriver() {
143-
var chromeOptions = new ChromeOptions();
144-
setChromeDownloadDirectory(chromeOptions);
143+
var chromeOptions = setChromeDownloadDirectory(new ChromeOptions());
145144
String commandlineOptions = Configuration.toString("chromeOptions");
146145
if (commandlineOptions != null)
147146
chromeOptions.addArguments(commandlineOptions);
148-
var headless = Configuration.toString("headless");
149-
if (headless != null && !headless.equalsIgnoreCase("false")) {
147+
if (Configuration.toBoolean("headless")) {
150148
chromeOptions.addArguments("--no-sandbox");
151149
chromeOptions.addArguments("--disable-dev-shm-usage");
152150
chromeOptions.addArguments("--headless=new");

src/test/java/com/dougnoel/sentinel/assertions/TableAssertTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static void setUpBeforeClass() {
2626
@AfterClass
2727
public static void tearDownAfterClass() {
2828
Time.reset();
29-
Configuration.update("timeout", 10);
29+
Configuration.clear("timeout");
3030
Driver.quitAllDrivers();
3131
}
3232

0 commit comments

Comments
 (0)