Skip to content

Update capabilities to support mobile browsers #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ repositories {
dependencies {
constraints {
api 'com.nordstrom.tools:java-utils:3.4.1'
api 'com.nordstrom.tools:settings:3.0.7'
api 'com.nordstrom.tools:junit-foundation:17.2.2'
api 'com.nordstrom.tools:settings:3.0.8'
api 'com.nordstrom.tools:junit-foundation:17.2.4'
api 'com.github.sbabcoc:logback-testng:2.0.1'
api 'org.hamcrest:hamcrest-core:3.0'
api 'org.yaml:snakeyaml:2.4'
Expand Down
2 changes: 1 addition & 1 deletion selenium3Deps.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ sourceSets {

dependencies {
constraints {
api 'com.nordstrom.tools:testng-foundation:5.1.3-j8'
api 'com.nordstrom.tools:testng-foundation:5.2.1-j8'
api 'org.seleniumhq.selenium:selenium-server:3.141.59'
api 'org.seleniumhq.selenium:selenium-support:3.141.59'
api 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59'
Expand Down
16 changes: 8 additions & 8 deletions selenium4Deps.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ sourceSets {

dependencies {
constraints {
api 'com.nordstrom.tools:testng-foundation:5.1.3-j11'
api 'org.seleniumhq.selenium:selenium-grid:4.30.0'
api 'org.seleniumhq.selenium:selenium-support:4.30.0'
api 'org.seleniumhq.selenium:selenium-chrome-driver:4.30.0'
api 'org.seleniumhq.selenium:selenium-edge-driver:4.30.0'
api 'org.seleniumhq.selenium:selenium-firefox-driver:4.30.0'
api 'com.nordstrom.tools:testng-foundation:5.2.1-j11'
api 'org.seleniumhq.selenium:selenium-grid:4.32.0'
api 'org.seleniumhq.selenium:selenium-support:4.32.0'
api 'org.seleniumhq.selenium:selenium-chrome-driver:4.32.0'
api 'org.seleniumhq.selenium:selenium-edge-driver:4.32.0'
api 'org.seleniumhq.selenium:selenium-firefox-driver:4.32.0'
api 'org.seleniumhq.selenium:selenium-opera-driver:4.4.0'
api 'org.seleniumhq.selenium:selenium-safari-driver:4.30.0'
api 'com.nordstrom.ui-tools:htmlunit-remote:4.30.0'
api 'org.seleniumhq.selenium:selenium-safari-driver:4.32.0'
api 'com.nordstrom.ui-tools:htmlunit-remote:4.30.1'
api 'org.seleniumhq.selenium:htmlunit3-driver:4.30.0'
api 'org.htmlunit:htmlunit:4.11.1'
api 'com.codeborne:phantomjsdriver:1.5.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public static HttpHost extractHost(URL url) {
* @return IP address for the machine we're running on (a.k.a. - 'localhost')
*/
public static String getLocalHost() {
return IDENTITY.getNonLoopbackAddressOfThisMachine();
return IDENTITY.getIp4NonLoopbackAddressOfThisMachine().getHostAddress();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
* // Execute script as anonymous function, passing specified argument
* WebElement response = JsUtility.runAndReturn(driver, script, name);
* // If element reference was returned, extract 'content' attribute
* return (response == null) ? null : response.getAttribute("content");
* return (response == null) ? null : WebDriverUtils.getDomAttributeOf(response, "content");
* }
* }</code></pre>
*
Expand All @@ -64,7 +64,7 @@
*
* <pre><code> var found = document.getElementsByTagName("meta");
* for (var i = 0; i &lt; found.length; i++) {
* if (found[i].getAttribute("name") == arguments[0]) return found[i];
* if (WebDriverUtils.getDomAttributeOf(found[i], "name") == arguments[0]) return found[i];
* }
* return null;</code></pre>
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ public boolean setInputValue(boolean value) {
* @return input field value
*/
public String getInputValue() {
return findElement(Using.INPUT).getAttribute("value");
return getDomPropertyOfElement(Using.INPUT, "value");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public boolean setInputValue(String value) {
* @return input field value
*/
public String getInputValue() {
return findElement(Using.INPUT).getAttribute("value");
return getDomPropertyOfElement(Using.INPUT, "value");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

import com.nordstrom.automation.selenium.core.WebDriverUtils;
import com.nordstrom.automation.selenium.model.ComponentContainer;
import com.nordstrom.automation.selenium.model.PageComponent;
import com.nordstrom.automation.selenium.model.RobustWebElement;
Expand Down Expand Up @@ -115,7 +116,7 @@ private List<TableRowComponent> getTableRows() {
* @return table component key
*/
public static Object getKey(SearchContext context) {
return ((WebElement) context).getAttribute("id");
return WebDriverUtils.getDomAttributeOf((WebElement) context, "id");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,50 @@ public RobustWebElement findOptional(final By by) {
return (RobustWebElement) RobustElementFactory.getElement(this, by, RobustElementWrapper.OPTIONAL);
}

/**
* Get the value of the named DOM property of the first WebElement matching the specified locator constant.
*
* @param constant the locator constant
* @param name the name of the property
* @return the property's current value or {@code null} if the value is not set
*/
public String getDomPropertyOfElement(final ByEnum constant, final String name) {
return getDomPropertyOfElement(constant.locator(), name);
}

/**
* Get the value of the named DOM property of the first WebElement matching the specified locator.
*
* @param by the locating mechanism
* @param name the name of the property
* @return the property's current value or {@code null} if the value is not set
*/
public String getDomPropertyOfElement(final By by, final String name) {
return WebDriverUtils.getDomPropertyOf(findElement(by), name);
}

/**
* Get the value of the named DOM attribute of the first WebElement matching the specified locator constant.
*
* @param constant the locator constant
* @param name the name of the attribute
* @return the attribute's value or {@code null} if the value is not set
*/
public String getDomAttributeOfElement(final ByEnum constant, final String name) {
return getDomAttributeOfElement(constant.locator(), name);
}

/**
* Get the value of the named DOM attribute of the first WebElement matching the specified locator.
*
* @param by the locating mechanism
* @param name the name of the attribute
* @return the attribute's value or {@code null} if the value is not set
*/
public String getDomAttributeOfElement(final By by, final String name) {
return WebDriverUtils.getDomAttributeOf(findElement(by), name);
}

/**
* Get the driver object associated with this container.
*
Expand All @@ -394,7 +438,7 @@ public static boolean updateValue(final WebElement element, final boolean value)
Objects.requireNonNull(element, ELEMENT_MESSAGE);

String tagName = element.getTagName().toLowerCase();
if ("input".equals(tagName) && "checkbox".equals(element.getAttribute("type"))) {
if ("input".equals(tagName) && "checkbox".equals(WebDriverUtils.getDomAttributeOf(element, "type"))) {
if (element.isSelected() != value) {
element.click();
return true;
Expand All @@ -418,15 +462,15 @@ public static boolean updateValue(final WebElement element, final String value)

String tagName = element.getTagName().toLowerCase();
if ("input".equals(tagName)) {
if ("checkbox".equals(element.getAttribute("type"))) {
if ("checkbox".equals(WebDriverUtils.getDomAttributeOf(element, "type"))) {
return updateValue(element, Boolean.parseBoolean(value));
} else if (!valueEquals(element, value)) {
if (WebDriverUtils.isJavascriptEnabled(element)) {
JsUtility.run(WebDriverUtils.getDriver(element), UPDATE_VALUE,
element, (value != null) ? value : "");
} else {
StringBuilder keys = new StringBuilder();
String exist = element.getAttribute("value");
String exist = WebDriverUtils.getDomPropertyOf(element, "value");
if (!(exist == null || exist.isEmpty())) {
keys.append(Keys.END);
for (int i = 0; i < exist.length(); i++) {
Expand Down Expand Up @@ -457,7 +501,7 @@ public static boolean updateValue(final WebElement element, final String value)
private static boolean valueEquals(final WebElement element, final String value) {
Objects.requireNonNull(element, ELEMENT_MESSAGE);

String exist = element.getAttribute("value");
String exist = WebDriverUtils.getDomPropertyOf(element, "value");
return (exist != null) ? exist.equals(value) : (value == null);
}

Expand Down Expand Up @@ -599,6 +643,7 @@ public <T extends Page> T openPageAtUrl(final Class<T> pageClass, final String u
* @param url target URL or activity
* @param driver driver object
*/
@SuppressWarnings("serial")
public static void getUrl(final String url, final WebDriver driver) {
Objects.requireNonNull(url, "[url] must be non-null");
Objects.requireNonNull(driver, "[driver] must be non-null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public enum TargetType implements TargetTypeName {

/**
* target: web application<br>
* driver: {@link RemoteWebDriverPlugin}
* driver: {@link RemoteWebDriverPlugin}, {@link UiAutomator2Plugin}
*/
WEB_APP(WEB_APP_NAME, RemoteWebDriverPlugin.class),
WEB_APP(WEB_APP_NAME, RemoteWebDriverPlugin.class, UiAutomator2Plugin.class),

/**
* target: Android application<br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@ public UiAutomator2Plugin() {
}

private static final String CAPABILITIES =
"{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\"," +
"\"browserName\":\"Chrome\",\"appium:deviceName\":\"Android Emulator\"}";
"{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\"}," +
"{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\",\"browserName\":\"chrome\"}";

private static final String BASELINE =
"{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\"," +
"\"nord:options\":{\"personality\":\"UiAutomator2\"," +
"\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin\"}}";

private static final String CHROME =
"{\"appium:automationName\":\"UiAutomator2\",\"platformName\":\"Android\",\"browserName\":\"chrome\"," +
"\"nord:options\":{\"personality\":\"UiAutomator2.chrome\"," +
"\"pluginClass\":\"com.nordstrom.automation.selenium.plugins.UiAutomator2Plugin\"}}";

private static final Map<String, String> PERSONALITIES;

private static final String DRIVER_CLASS_NAME = "io.appium.java_client.android.AndroidDriver";

static {
Map<String, String> personalities = new HashMap<>();
personalities.put(DRIVER_NAME, BASELINE);
personalities.put(DRIVER_NAME + ".chrome", CHROME);
PERSONALITIES = Collections.unmodifiableMap(personalities);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ public static Coordinator<Boolean> textToBePresentInElementValue(final By locato
@Override
public Boolean apply(SearchContext context) {
try {
String elementText = context.findElement(locator).getAttribute("value");
String elementText = WebDriverUtils.getDomPropertyOf(context.findElement(locator), "value");
return elementText != null && elementText.contains(text);
} catch (StaleElementReferenceException | NoSuchElementException e) {
return null;
Expand Down Expand Up @@ -653,7 +653,7 @@ public static Coordinator<Boolean> elementToHaveAttributeValue(final By locator,
@Override
public Boolean apply(SearchContext context) {
try {
String attrib = context.findElement(locator).getAttribute(attribute);
String attrib = WebDriverUtils.getDomPropertyOf(context.findElement(locator), attribute);
if (attrib != null) {
return attrib.equals(value);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,21 @@
import org.openqa.selenium.WebDriverException;
import org.testng.ITestResult;

import com.nordstrom.automation.testng.RetryManager;
import com.nordstrom.automation.testng.TestNGRetryAnalyzer;

/**
* This class implements a Selenium-specific <b>TestNG</b> retry analyzer.
*/
public class RetryAnalyzer extends RetryManager {
public class RetryAnalyzer implements TestNGRetryAnalyzer {

/**
* {@inheritDoc}
* <p>
* This implementation deems that a test that fails with an instance of {@link WebDriverException} is re-triable.
*/
@Override
protected boolean isRetriable(ITestResult result) {
if (result.getThrowable() instanceof WebDriverException) {
return true;
}
return super.isRetriable(result);
public boolean retry(ITestResult result) {
return (result.getThrowable() instanceof WebDriverException);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public final class WebDriverUtils {
TimeoutException.class));

private static final Pattern FRAMEWORK_PACKAGE = Pattern.compile(
"^(?:sun\\.reflect|java\\.lang"
+ "|org\\.(?:openqa|testng|junit|hamcrest)"
+ "|com\\.nordstrom\\.automation\\.selenium)\\.");
"^(?:sun\\.reflect|java\\.lang"
+ "|org\\.(?:openqa|testng|junit|hamcrest)"
+ "|com\\.nordstrom\\.automation\\.selenium)\\.");

/**
* Private constructor to prevent instantiation.
*/
Expand Down Expand Up @@ -122,6 +122,30 @@ public static Capabilities getCapabilities(final SearchContext context) {
return (driver instanceof HasCapabilities) ? ((HasCapabilities) driver).getCapabilities() : null;
}

/**
* Get the value of the named DOM property of the specified WebElement.
*
* @param element the target element
* @param name the name of the property
* @return the property's current value or {@code null} if the value is not set
*/
public static String getDomPropertyOf(final WebElement element, final String name) {
String script = String.format("return arguments[0].%s;", name);
return JsUtility.runAndReturn(getDriver(element), script, element);
}

/**
* Get the value of the named DOM attribute of the specified WebElement.
*
* @param element the target element
* @param name the name of the attribute
* @return the attribute's value or {@code null} if the value is not set
*/
public static String getDomAttributeOf(final WebElement element, final String name) {
String script = String.format("return arguments[0].getAttribute('%s');", name);
return JsUtility.runAndReturn(getDriver(element), script, element);
}

/**
* Remove hidden elements from specified list.
*
Expand Down
Loading