diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index d2f785c..b986cf6 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -10,7 +10,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 18ec0b6..0b02394 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/Controlium.iml b/Controlium.iml
index 845e6c1..8a430ab 100644
--- a/Controlium.iml
+++ b/Controlium.iml
@@ -1,15 +1,13 @@
-
+
-
-
@@ -39,40 +37,14 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 53fa98c..0ef5a9b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
TeamControlium Controlium control-based Selenium abstraction
- org.testcontrolium
+ org.teamcontrolium
controlium
- 1.0
+ 1.1.0
TeamControlium Controlium provides the API and foundations for Control-based use of Selenium
https://github.com/TeamControlium
2017
@@ -17,7 +17,7 @@
The MIT License
- https://github.com/TeamControlium/Controlium.java/blob/master/LICENSE
+ https://github.com/TeamControlium/Controlium.java/blob/master/LICENSE.txt
repo
@@ -39,10 +39,64 @@
3.7.0
- 10
- 10
+ 1.8
+ 1.8
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.7
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.0.0
+
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
@@ -70,11 +124,6 @@
commons-io
2.5
-
- org.junit.jupiter
- junit-jupiter-api
- 5.0.0
-
org.jsoup
@@ -84,7 +133,7 @@
org.teamcontrolium
utilities
- 1.1.0
+ 1.2.3
org.junit.jupiter
@@ -107,6 +156,16 @@
+10
+
+ K8Coder
+ Kate More
+ kmoor@alexview.com
+
+ Java Developer
+
+ TeamControlium Committers
+ +10
+
\ No newline at end of file
diff --git a/src/main/java/TeamControlium/Controlium/ControlBase.java b/src/main/java/TeamControlium/Controlium/ControlBase.java
index 3796b7f..7806ce3 100644
--- a/src/main/java/TeamControlium/Controlium/ControlBase.java
+++ b/src/main/java/TeamControlium/Controlium/ControlBase.java
@@ -94,10 +94,11 @@ public static T setControl(SeleniumDriver seleniumDriver
StopWatch timeWaited = StopWatch.createStarted();
try {
- Logger.WriteLine(Logger.LogLevels.TestInformation, "Setting on Control [%s] from Parent [%s]",
+ Logger.WriteLine(Logger.LogLevels.TestInformation, "Setting on Control [%s] from Parent [%s] with SeleniumDriver hash [%s] (WebDriver hash [%s])",
newControl.getMapping() == null ? "No mapping logic!" : newControl.getMapping().getFriendlyName(),
- parentControl == null ? "No parent Control - So Top Level control" : parentControl.getMapping() == null ? "No mapping logic!" : parentControl.getMapping().getFriendlyName());
-
+ parentControl == null ? "No parent Control - So Top Level control" : parentControl.getMapping() == null ? "No mapping logic!" : parentControl.getMapping().getFriendlyName(),
+ seleniumDriver==null?"null":Integer.toString(seleniumDriver.hashCode()),
+ seleniumDriver==null?"N/A":(seleniumDriver.getWebDriver()==null?"Null!":Integer.toString(seleniumDriver.getWebDriver().hashCode())));
//
// Check if ParentControl has become stale (has been redrawn). If so, refresh it (force a new findElement on it). Note that this
// will effectively ripple up to the top level
@@ -149,6 +150,11 @@ public static T setControl(SeleniumDriver seleniumDriver
}
}
+ public static boolean controlExists(ControlBase parentControl,T control) { return parentControl.elementExists(control.getMapping());}
+ public static boolean controlExists(SeleniumDriver seleniumDriver, T control) { return seleniumDriver.findElementOrNull(control.getMapping())!=null;}
+ public static boolean controlExists(SeleniumDriver seleniumDriver, ControlBase parentControl, T control) { return seleniumDriver.findElement(parentControl.getMapping()).findElementOrNull(control.getMapping())!=null;}
+
+ public boolean controlExists(T control) {return this.elementExists(control.getMapping());}
//
// All Controls must implement a ControlBeingSet. This is called when the Control is set upon (IE. Find logic applied and bound to a Selenium element). It really
// will become useful when caching is implemented. It is used by a Control to do stuff when located in the Dom - IE. A dropdown control may click on it whenever
@@ -175,6 +181,9 @@ public boolean isStale() {
}
}
+ public boolean elementExists(ObjectMapping mapping) {
+ return getRootElement().findElementOrNull(mapping)!=null;
+ }
public void clearElement(ObjectMapping mapping) {
findElement(mapping).clear();
@@ -240,6 +249,42 @@ public boolean hasAttribute(String attributeName) {
}
}
+ public String getCssValue(ObjectMapping mapping, String valueName) {
+ HTMLElement element = findElement(mapping);
+ try {
+ return element.getCssValue(valueName);
+ }
+ catch (Exception e) {
+ return "";
+ }
+ }
+ public String getCssValue(String valueName) {
+ try {
+ return getRootElement().getCssValue(valueName);
+ }
+ catch (Exception e) {
+ return "";
+ }
+ }
+
+ public boolean hasCssValue(ObjectMapping mapping, String valueName) {
+ HTMLElement element = findElement(mapping);
+ try {
+ return element.hasCssValue(valueName);
+ }
+ catch (Exception e) {
+ return false;
+ }
+ }
+ public boolean hasCssValue(String valueName) {
+ try {
+ return getRootElement().hasCssValue(valueName);
+ }
+ catch (Exception e) {
+ return false;
+ }
+ }
+
public String getText(ObjectMapping mapping) {
HTMLElement element = findElement(mapping);
try {
diff --git a/src/main/java/TeamControlium/Controlium/HTMLElement.java b/src/main/java/TeamControlium/Controlium/HTMLElement.java
index 71a3735..583e3a9 100644
--- a/src/main/java/TeamControlium/Controlium/HTMLElement.java
+++ b/src/main/java/TeamControlium/Controlium/HTMLElement.java
@@ -423,7 +423,7 @@ public void setText(String text,Duration retryInterval) {
setText(text,1,retryInterval);}
public void setText(String text,int maxTries,Duration retryInterval)
{
- RuntimeException lastException=null;
+ Exception lastException=null;
throwIfUnbound();
if (maxTries<1) throw new RuntimeException(String.format("Maximum tries [%d]. Cannot be less than 1.",maxTries));
int tryIndex = 0;
@@ -435,6 +435,7 @@ public void setText(String text,int maxTries,Duration retryInterval)
{
try
{
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Calling Selenium driver clear with WebElement. Driver %s, Element %s",getSeleniumDriver()==null?"Null":"Good",getUnderlyingWebElement()==null?"Null!":"Good");
getSeleniumDriver().clear(this.getUnderlyingWebElement());
enterText(text);
if (tryIndex > 1) Logger.WriteLine(Logger.LogLevels.FrameworkDebug, "{0} attempt attempt good.)", tryIndex);
@@ -445,11 +446,16 @@ public void setText(String text,int maxTries,Duration retryInterval)
Thread.sleep(retryInterval.toMillis());
lastException = e;
}
+ catch (Exception e)
+ {
+ Thread.sleep(retryInterval.toMillis());
+ lastException = e;
+ }
}
throw lastException;
}
catch (InvalidElementState ex) {
- Logger.WriteLine(Logger.LogLevels.Error,"Error thrown setting text (clear then enter) in [%s] (Tried %d times): %s",getFriendlyName(),tryIndex,ex.getMessage());
+ Logger.WriteLine(Logger.LogLevels.Error,"Error thrown setting text (clear then enter) in [%s] (Tried %d times): %s",getFriendlyName(),tryIndex,ex);
throw new InvalidElementState(String.format("Error thrown setting text (clear then enter) in [%s] ([%s]). Tried %d times).",getFriendlyName(),getMappingDetails().getActualFindLogic(),tryIndex),ex);
}
@@ -480,6 +486,7 @@ public void enterText(String text,int maxTries,Duration retryInterval) {
{
try
{
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Calling Selenium driver setText with WebElement. Driver %s, Element %s, text = [%s]",getSeleniumDriver()==null?"Null":"Good",getUnderlyingWebElement()==null?"Null!":"Good",text==null?"":text);
getSeleniumDriver().setText(this.getUnderlyingWebElement(),(text==null)?"":text);
if (tryIndex > 1) Logger.WriteLine(Logger.LogLevels.FrameworkDebug, "{0} attempt attempt good.)", tryIndex);
return;
@@ -549,7 +556,7 @@ public String getAttribute(String attribute) {
throw ex;
}
catch (Exception e) {
- Logger.WriteLine(Logger.LogLevels.TestInformation,"Error thrown getting attribute [%s] from [%s]: %s",getFriendlyName(),e.getMessage());
+ Logger.WriteLine(Logger.LogLevels.TestInformation,"Error thrown getting attribute [%s] from [%s]: %s",attribute,getFriendlyName(),e.getMessage());
throw new RuntimeException(String.format("Error thrown getting attribute [%s] from [%s] ([%s])",attribute==null?"Null":attribute,getFriendlyName(),getMappingDetails().getActualFindLogic()),e);
}
}
@@ -563,11 +570,40 @@ public boolean hasAttribute(String attribute) {
throw ex;
}
catch (Exception e) {
- Logger.WriteLine(Logger.LogLevels.TestDebug,"Error thrown getting attribute [%s] from [%s]. Assume does not have attribute: %s",getFriendlyName(),e.getMessage());
+ Logger.WriteLine(Logger.LogLevels.TestDebug,"Error thrown getting attribute [%s] from [%s]. Assume does not have attribute: %s",attribute,getFriendlyName(),e.getMessage());
return false;
}
}
+ public String getCssValue(String valueName) {
+ throwIfUnbound();
+ try {
+ return getSeleniumDriver().getCssValue(getUnderlyingWebElement(),valueName);
+ }
+ catch (InvalidElementState ex) {
+ throw ex;
+ }
+ catch (Exception e) {
+ Logger.WriteLine(Logger.LogLevels.TestInformation,"Error thrown getting CSS value [%s] from [%s]: %s",valueName,getFriendlyName(),e.getMessage());
+ throw new RuntimeException(String.format("Error thrown getting CSS value [%s] from [%s] ([%s])",valueName==null?"Null":valueName,getFriendlyName(),getMappingDetails().getActualFindLogic()),e);
+ }
+ }
+
+ public boolean hasCssValue(String valueName) {
+ throwIfUnbound();
+ try {
+ return getSeleniumDriver().hasCssValue(getUnderlyingWebElement(),valueName);
+ }
+ catch (InvalidElementState ex) {
+ throw ex;
+ }
+ catch (Exception e) {
+ Logger.WriteLine(Logger.LogLevels.TestDebug,"Error thrown getting CSS value [%s] from [%s]. Assume does not have CSS value: %s",valueName,getFriendlyName(),e.getMessage());
+ return false;
+ }
+ }
+
+
public void click() {
throwIfUnbound();
try {
diff --git a/src/main/java/TeamControlium/Controlium/ObjectMapping.java b/src/main/java/TeamControlium/Controlium/ObjectMapping.java
index d356baa..208618f 100644
--- a/src/main/java/TeamControlium/Controlium/ObjectMapping.java
+++ b/src/main/java/TeamControlium/Controlium/ObjectMapping.java
@@ -1,8 +1,9 @@
package TeamControlium.Controlium;
import java.lang.*;
+import java.util.Map;
+
import TeamControlium.Utilities.Logger;
-import javafx.util.Pair;
import org.openqa.selenium.By;
public class ObjectMapping {
@@ -106,7 +107,7 @@ public ObjectMapping copy() {
}
}
- public ObjectMapping ResolveParameters(String... params) {
+ public ObjectMapping resolveParameters(String... params) {
String newFindLogic = null;
String newFriendlyName = null;
@@ -141,7 +142,7 @@ public ObjectMapping ResolveParameters(String... params) {
private By processFindLogic(String property) {
By returnValue;
- Pair findLogicNameValue;
+ KeyValue findLogicNameValue;
//
// Get the find type and logic
@@ -151,10 +152,10 @@ private By processFindLogic(String property) {
} else {
if (property.contains("=")) {
String[] nameValue = property.split("=", 2);
- findLogicNameValue = new Pair(nameValue[0], nameValue[1]);
+ findLogicNameValue = new KeyValue(nameValue[0], nameValue[1]);
} else {
// Default to xpath....
- findLogicNameValue = new Pair("xpath", property);
+ findLogicNameValue = new KeyValue("xpath", property);
}
switch (findLogicNameValue.getKey()) {
@@ -198,4 +199,36 @@ private By processFindLogic(String property) {
return returnValue;
}
}
+
+ public class KeyValue implements Map.Entry
+ {
+ private K key;
+ private V value;
+
+ public KeyValue(K key, V value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ public K getKey()
+ {
+ return this.key;
+ }
+
+ public V getValue()
+ {
+ return this.value;
+ }
+
+ public K setKey(K key)
+ {
+ return this.key = key;
+ }
+
+ public V setValue(V value)
+ {
+ return this.value = value;
+ }
+ }
}
diff --git a/src/main/java/TeamControlium/Controlium/SeleniumDriver.java b/src/main/java/TeamControlium/Controlium/SeleniumDriver.java
index 59f1b5e..3bbf12d 100644
--- a/src/main/java/TeamControlium/Controlium/SeleniumDriver.java
+++ b/src/main/java/TeamControlium/Controlium/SeleniumDriver.java
@@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.*;
@@ -28,6 +29,7 @@
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.ie.InternetExplorerDriverService;
import org.openqa.selenium.ie.InternetExplorerOptions;
+import org.openqa.selenium.internal.WrapsDriver;
import org.w3c.dom.NodeList;
import javax.swing.text.html.HTMLDocument;
@@ -133,6 +135,9 @@ private void commonConstructs(boolean killFirst, String seleniumHost,String devi
setSeleniumHost(seleniumHost);
startOrConnectToSeleniumServer(killFirst);
+ Logger.WriteLine(Logger.LogLevels.FrameworkInformation,"Started SeleniumDriver. Hash code [%d]. WebDriver hash code [%s]",
+ this.hashCode(),
+ getWebDriver()==null?"null!":Integer.toString(getWebDriver().hashCode()));
}
@@ -158,6 +163,24 @@ public Duration setPageLoadTimeout(Duration pageLoadTimeout) {
public Browsers getDevice() { return _browser; }
public Browsers setDevice(Devices device) { _device=device; return getDevice();}
+ public Set getAllBrowserWindows() {
+ if (getWebDriver()!=null) {
+ return getWebDriver().getWindowHandles();
+ }
+ else {
+ throw new RuntimeException("Cannot get Browser windows handles as WebDriver null!");
+ }
+ }
+
+ public void setBrowserWindow(String handle) {
+ if (getWebDriver()!=null) {
+ getWebDriver().switchTo().window(handle);
+ }
+ else {
+ throw new RuntimeException("Cannot set Browser window as WebDriver null!");
+ }
+ }
+
public void setIFrame(HTMLElement htmlElement) {
WebElement webElement = ((WebElement)htmlElement.getUnderlyingWebElement());
String tag = webElement.getTagName();
@@ -268,13 +291,15 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
pollIntervalMillis, // Polling ionterval in milliseconds
timer); // Find stopwatch timing whole finding
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Got [%s] elements",clauseResults==null?"Null!":Integer.toString(clauseResults.size()));
+
if (clauseResults.size()==0) {
String errorText = String.format("Time reached and found 0 matching elements using ([%s] - %s) from [%s] (Waited upto %dmS).",
objectMapping.getActualFindLogic(),
objectMapping.getFriendlyName(),
(parentElement == null) ? "DOM Top Level" : parentElement.getMappingDetails().getFriendlyName(),
timer.getTime());
- Logger.WriteLine(Logger.LogLevels.Error, errorText);
+ Logger.WriteLine(Logger.LogLevels.FrameworkInformation, errorText);
throw new RuntimeException(errorText);
}
@@ -349,7 +374,7 @@ public List findElements(HTMLElement parentElement, ObjectMapping m
throw new RuntimeException("SeleniumDriver.FindElements called with mapping null!");
}
- Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"ObjectMapping = [%s] (%s)",mapping.getOriginalFindLogic(),mapping.getFriendlyName());
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"webDriver hash [%s], ObjectMapping = [%s] (%s)",webDriver==null?"Null!":Integer.toString(webDriver.hashCode()),mapping.getOriginalFindLogic(),mapping.getFriendlyName());
By seleniumFindBy = mapping.getSeleniumBy();
@@ -401,6 +426,19 @@ public List findElements(HTMLElement parentElement, ObjectMapping m
return returnElements;
}
+ public WebDriver getWebDriver() { return webDriver;}
+ public void setBrowserSize(int x, int y) {
+ if (webDriver==null) {
+ throw new RuntimeException("Selenium web driver has not been started!");
+ }
+ webDriver.manage().window().setSize(new Dimension(x,y));
+ }
+ public void setBrowserFullScreen() {
+ if (webDriver==null) {
+ throw new RuntimeException("Selenium web driver has not been started!");
+ }
+ webDriver.manage().window().fullscreen();
+ }
public void gotoURL(String fullURLPath) {
try {
@@ -412,7 +450,6 @@ public void gotoURL(String fullURLPath) {
throw e;
}
}
-
public String getPageTitle() {
try {
String title = webDriver.getTitle();
@@ -467,7 +504,8 @@ public void clear(Object webElement) {
//
// Usually thrown by Selenium when element stale
//
- throw new InvalidElementState("Unable to clear element. See underlying cause.",e);
+ Logger.WriteLine(Logger.LogLevels.Error,"Unable to clear element. See underlying cause: %s",e);
+ throw new InvalidElementState(String.format("Unable to clear element. See underlying cause: %s",e),e);
}
}
@@ -560,6 +598,7 @@ public void click(Object webElement) {
if (webElement == null) throw new RuntimeException("webElement null!");
try {
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Click using WebDriver hash [%s]",((WrapsDriver) webElement).getWrappedDriver()==null?"null!!":Integer.toString(((WrapsDriver) webElement).getWrappedDriver().hashCode()));
((WebElement) webElement).click();
} catch (WebDriverException wde) {
String errorMessage = wde.getMessage();
@@ -629,6 +668,29 @@ public boolean hasAttribute(Object webElement,String attribute) {
}
}
+ public String getCssValue(Object webElement,String valueName) {
+ if (webElement==null) throw new RuntimeException("webElement null!");
+
+ try {
+ String cssValue = ((WebElement)webElement).getCssValue(valueName);
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug, "Got CSS Value [%s]: [%s]", valueName,(cssValue==null)?"Null":cssValue);
+ return (cssValue==null)?"":cssValue;
+
+ }
+ catch (InvalidElementStateException e) {
+ //
+ // Usually thrown by Selenium when element stale
+ //
+ throw new InvalidElementState(String.format("Unable to get element CSS Value [%s]. See underlying cause.",(valueName==null)?"Null!!":valueName),e);
+ }
+ }
+ public boolean hasCssValue(Object webElement,String valueName) {
+ if (webElement==null) throw new RuntimeException("webElement null!");
+
+ return (!getCssValue(webElement,valueName).isEmpty());
+ }
+
+
public void scrollIntoView(Object webElement) {
Logger.WriteLine(Logger.LogLevels.FrameworkDebug, "Scrolling element in to view using JavaScript injection - [Element].scrollIntoView()");
executeJavaScriptNoReturnData("arguments[0].scrollIntoView();", webElement);
@@ -853,8 +915,6 @@ else if (Browsers.isChrome()) {
String executable = "ChromeDriver.exe";
ChromeOptions options = new ChromeOptions();
-
-
setPathToDriverIfExistsAndIsExecutable(seleniumServerFolder, ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY,executable);
if (seleniumDebugMode) System.setProperty(ChromeDriverService.CHROME_DRIVER_VERBOSE_LOG_PROPERTY, "true");
@@ -867,6 +927,7 @@ else if (Browsers.isChrome()) {
if (killFirst) killAllProcesses(executable);
webDriver = new ChromeDriver(ChromeDriverService.createDefaultService(), options);
+ Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Chrome driver created. Hash [%d]",webDriver.hashCode());
}
else if (Browsers.isEdge()) {
String executable = "EdgeDriver.exe";
@@ -890,15 +951,17 @@ else if (Browsers.isEdge()) {
} catch (Exception e) {
throw new RuntimeException(String.format("Error instantiating [%s] (%s)", Browsers.isChrome() ? "Chrome" : Browsers.isEdge() ? "Edge" : Browsers.isInternetExplorer() ? "Internet Explorer" : Browsers.isSafari() ? "Safari" : "UNKNOWN!", seleniumServerFolder));
}
+
}
private List getHtmlElements(HTMLElement parentElement, ObjectMapping objectMapping, boolean allowMultipleMatches, boolean waitUntilSingle, boolean showMultiFound, long totalTimeoutMillis, long pollIntervalMillis, StopWatch timer) {
List clauseResults = new ArrayList();
while (clauseResults.size() == 0 || (clauseResults.size() != 1 && !allowMultipleMatches && waitUntilSingle)) {
+ Logger.WriteLine(Logger.LogLevels.TestDebug, "Allow Multiple Matches [%s], Wait until single match [%s].", allowMultipleMatches?"true":"false",waitUntilSingle?"true":"false");
clauseResults = findElements(parentElement, objectMapping);
+ Logger.WriteLine(Logger.LogLevels.TestDebug, "Found %s elements matching [%s].", clauseResults==null?"Null!":Integer.toString(clauseResults.size()), objectMapping.getActualFindLogic());
if (clauseResults.size() == 0 || (clauseResults.size() != 1 && !allowMultipleMatches && waitUntilSingle)) {
if (clauseResults.size() > 0 && showMultiFound) {
- Logger.WriteLine(Logger.LogLevels.TestDebug, "Found %d elements matching [%s]. Waiting until only a single element is found...", clauseResults.size(), objectMapping.getActualFindLogic());
showMultiFound = false;
}
try {
@@ -918,6 +981,8 @@ private void setPathToDriverIfExistsAndIsExecutable(final String pathToDriver, f
if (driver.exists() && driver.canExecute()) {
System.setProperty(driverExeProperty, driver.getAbsolutePath());
} else {
+
+
throw new IllegalArgumentException(String.format("Driver not found or is not executable in %s", pathToDriver));
}
}