Skip to content
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

Refactor collection conditions #2307

Merged
merged 4 commits into from
May 24, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions modules/appium/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
=== 6.15.0 (released 2x.05.2023)
Project "selenide-appium" was merged into project "selenide".

* #135 add appium collection conditions -- thanks to Amuthan Sakthivel
* #149 add wrapper methods for getContextHandles and setContext -- thanks to Amuthan Sakthivel

=== 2.8.1 (released 22.05.2023)
* Fix $.click(options) in web browser

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import com.codeborne.selenide.CollectionCondition;
import com.codeborne.selenide.appium.conditions.AppiumAttributeValues;
import com.codeborne.selenide.appium.conditions.CombinedAttribute;

import java.util.List;

public class AppiumCollectionCondition {

private AppiumCollectionCondition() {
public static CollectionCondition attributes(CombinedAttribute attribute, String... expectedAttributeValues) {
return new AppiumAttributeValues(attribute, expectedAttributeValues);
}

public static CollectionCondition exactAttributes(String androidAttributeName, String iosAttributeName,
String... expectedAttributeValues) {
return new AppiumAttributeValues(androidAttributeName, iosAttributeName, expectedAttributeValues);
}

public static CollectionCondition exactAttributes(String androidAttributeName, String iosAttributeName,
List<String> expectedAttributeValues) {
return new AppiumAttributeValues(androidAttributeName, iosAttributeName, expectedAttributeValues);
public static CollectionCondition attributes(CombinedAttribute attribute, List<String> expectedAttributeValues) {
return new AppiumAttributeValues(attribute, expectedAttributeValues);
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
package com.codeborne.selenide.appium;

import com.codeborne.selenide.Condition;
import com.codeborne.selenide.conditions.AttributeWithValue;
import com.codeborne.selenide.appium.conditions.AttributeWithValue;
import com.codeborne.selenide.appium.conditions.CombinedAttribute;

public class AppiumCondition {

private AppiumCondition() {
}

public static Condition attributeWithValue(String androidAttributeName, String iosAttributeName, String expectedAttributeValue) {
return AppiumDriverRunner.isAndroidDriver()
? androidAttributeWithValue(androidAttributeName, expectedAttributeValue)
: iosAttributeWithValue(iosAttributeName, expectedAttributeValue);
}

public static Condition androidAttributeWithValue(String androidAttributeName, String expectedAttributeValue) {
return new AttributeWithValue(androidAttributeName, expectedAttributeValue);
}

public static Condition iosAttributeWithValue(String iosAttributeName, String expectedAttributeValue) {
return new AttributeWithValue(iosAttributeName, expectedAttributeValue);
public static Condition attribute(CombinedAttribute attribute, String expectedAttributeValue) {
return new AttributeWithValue(attribute, expectedAttributeValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static <T extends AppiumDriver> T getMobileDriver() {
if (isAndroidDriver() || isIosDriver()) {
return isAndroidDriver() ? (T) getAndroidDriver() : (T) getIosDriver();
}
throw new ClassCastException("WebDriver is not instance of AndroidDriver or IOSDriver");
throw new ClassCastException("WebDriver is not instance of AndroidDriver or IOSDriver: " + WebDriverRunner.getWebDriver());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.codeborne.selenide.Driver;
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.WrapsDriver;
import org.openqa.selenium.WrapsElement;
Expand All @@ -18,26 +18,27 @@ public static boolean isMobile(Driver driver) {
return instanceOf(driver, AppiumDriver.class);
}

public static boolean isMobile(WebDriver driver) {
public static boolean isMobile(SearchContext driver) {
return instanceOf(driver, AppiumDriver.class);
}

public static <T> boolean instanceOf(Driver driver, Class<T> klass) {
return cast(driver, klass).isPresent();
}

public static <T> boolean instanceOf(WebDriver driver, Class<T> klass) {
public static <T> boolean instanceOf(SearchContext driver, Class<T> klass) {
return cast(driver, klass).isPresent();
}

public static <T> Optional<T> cast(Driver driver, Class<T> klass) {
return cast(driver.getWebDriver(), klass);
}

public static <T> Optional<T> cast(WebDriver probablyWrappedWebdriver, Class<T> klass) {
WebDriver unwrappedWebdriver = probablyWrappedWebdriver instanceof WrapsDriver ?
((WrapsDriver) probablyWrappedWebdriver).getWrappedDriver() :
probablyWrappedWebdriver;
public static <T> Optional<T> cast(SearchContext driverOrElement, Class<T> klass) {
SearchContext unwrappedWebdriver = driverOrElement;
while (unwrappedWebdriver instanceof WrapsDriver wrapper) {
unwrappedWebdriver = wrapper.getWrappedDriver();
}

//noinspection unchecked
return klass.isAssignableFrom(unwrappedWebdriver.getClass()) ?
Expand All @@ -46,9 +47,10 @@ public static <T> Optional<T> cast(WebDriver probablyWrappedWebdriver, Class<T>
}

public static <T> T cast(WebElement probablyWrappedWebElement, Class<T> klass) {
WebElement unwrappedWebElement = probablyWrappedWebElement instanceof WrapsElement ?
((WrapsElement) probablyWrappedWebElement).getWrappedElement() :
probablyWrappedWebElement;
WebElement unwrappedWebElement = probablyWrappedWebElement;
while (unwrappedWebElement instanceof WrapsElement wrapper) {
unwrappedWebElement = wrapper.getWrappedElement();
}

if (!klass.isAssignableFrom(unwrappedWebElement.getClass())) {
throw new IllegalArgumentException("WebElement " + unwrappedWebElement + " is not instance of " + klass.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,59 @@
package com.codeborne.selenide.appium.conditions;

import com.codeborne.selenide.appium.AppiumDriverRunner;
import com.codeborne.selenide.CheckResult;
import com.codeborne.selenide.Driver;
import com.codeborne.selenide.collections.ExactTexts;
import org.openqa.selenium.WebElement;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Objects;

import static com.codeborne.selenide.CheckResult.Verdict.ACCEPT;
import static com.codeborne.selenide.CheckResult.Verdict.REJECT;
import static java.util.stream.Collectors.toList;

@ParametersAreNonnullByDefault
public class AppiumAttributeValues extends ExactTexts {

protected String androidAttributeName;
protected String iosAttributeName;
protected final CombinedAttribute attribute;

public AppiumAttributeValues(String androidAttributeName, String iosAttributeName, String... expectedAttributeValues) {
public AppiumAttributeValues(CombinedAttribute attribute, String... expectedAttributeValues) {
super(expectedAttributeValues);
this.androidAttributeName = androidAttributeName;
this.iosAttributeName = iosAttributeName;
this.attribute = attribute;
}

public AppiumAttributeValues(String androidAttributeName, String iosAttributeName, List<String> expectedAttributeValues) {
public AppiumAttributeValues(CombinedAttribute attribute, List<String> expectedAttributeValues) {
super(expectedAttributeValues);
this.androidAttributeName = androidAttributeName;
this.iosAttributeName = iosAttributeName;
this.attribute = attribute;
}

@Override
public boolean test(List<WebElement> elements) {

@Nonnull
@CheckReturnValue
public CheckResult check(Driver driver, List<WebElement> elements) {
if (elements.size() != this.expectedTexts.size()) {
return false;
} else {
List<String> actualAttributeValues = getActualAttributeValuesToCompare(elements);
return new CheckResult(REJECT, elements.size());
}
else {
List<String> actualAttributeValues = getActualAttributes(driver, elements);
for (int i = 0; i < this.expectedTexts.size(); ++i) {
String expectedText = this.expectedTexts.get(i);
String actualAttributeValue = actualAttributeValues.get(i);
if (!expectedText.equals(actualAttributeValue)) {
return false;
if (!Objects.equals(expectedText, actualAttributeValue)) {
return new CheckResult(REJECT, actualAttributeValues);
}
}

return true;
return new CheckResult(ACCEPT, null);
}
}

private List<String> getActualAttributeValuesToCompare(List<WebElement> elements) {
return elements.stream().map(getFunctionBasedOnMobileOs()).collect(Collectors.toList());
}

private Function<WebElement, String> getFunctionBasedOnMobileOs() {
if (AppiumDriverRunner.isAndroidDriver()) {
return element -> element.getAttribute(androidAttributeName);
} else if (AppiumDriverRunner.isIosDriver()) {
return element -> element.getAttribute(iosAttributeName);
} else {
throw new IllegalArgumentException("Appium Collection Condition is only applicable for android and ios driver. " +
"Please use Condition class instead.");
}
private List<String> getActualAttributes(Driver driver, List<WebElement> elements) {
return elements.stream()
.map(element -> attribute.getAttributeValue(driver, element))
.collect(toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.codeborne.selenide.appium.conditions;

import com.codeborne.selenide.CheckResult;
import com.codeborne.selenide.Condition;
import com.codeborne.selenide.Driver;
import org.openqa.selenium.WebElement;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;

@ParametersAreNonnullByDefault
public class AttributeWithValue extends Condition {
private final CombinedAttribute attribute;
protected final String expectedAttributeValue;

public AttributeWithValue(CombinedAttribute attribute, String expectedAttributeValue) {
super(String.format("attribute %s=\"%s\"", attribute, expectedAttributeValue));
this.attribute = attribute;
this.expectedAttributeValue = expectedAttributeValue;
}

@Nonnull
@Override
public CheckResult check(Driver driver, WebElement element) {
String attributeValue = attribute.getAttributeValue(driver, element);
return new CheckResult(
expectedAttributeValue.equals(attributeValue),
String.format("%s=\"%s\"", attribute, attributeValue)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.codeborne.selenide.appium.conditions;

import com.codeborne.selenide.Driver;
import com.codeborne.selenide.WebDriverRunner;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.WebElement;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

import static com.codeborne.selenide.appium.WebdriverUnwrapper.instanceOf;
import static java.util.Objects.requireNonNull;

@ParametersAreNonnullByDefault
public class CombinedAttribute {
@Nullable
private final String androidAttribute;

@Nullable
private final String iosAttribute;

private CombinedAttribute(@Nullable String androidAttribute, @Nullable String iosAttribute) {
this.androidAttribute = androidAttribute;
this.iosAttribute = iosAttribute;
}

@CheckReturnValue
public static CombinedAttribute android(String android) {
return new CombinedAttribute(android, null);
}

@CheckReturnValue
public CombinedAttribute ios(String ios) {
return new CombinedAttribute(androidAttribute, ios);
}

@Nullable
@CheckReturnValue
public String getAttributeValue(Driver driver, WebElement element) {
return element.getAttribute(getAttributeName(driver));
}

private String getAttributeName(Driver driver) {
if (instanceOf(driver, AndroidDriver.class)) {
return requireNonNull(androidAttribute, "Android selector not given");
}
if (instanceOf(driver, IOSDriver.class)) {
return requireNonNull(iosAttribute, "iOS selector not given");
}
throw new UnsupportedOperationException("Unsupported webdriver: " + WebDriverRunner.getWebDriver());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.codeborne.selenide.appium.selector;

import com.codeborne.selenide.WebDriverRunner;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
Expand All @@ -9,8 +11,7 @@
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;

import static com.codeborne.selenide.appium.AppiumDriverRunner.isAndroidDriver;
import static com.codeborne.selenide.appium.AppiumDriverRunner.isIosDriver;
import static com.codeborne.selenide.appium.WebdriverUnwrapper.instanceOf;
import static java.util.Objects.requireNonNull;

@ParametersAreNonnullByDefault
Expand All @@ -36,19 +37,19 @@ public CombinedBy ios(By ios) {

@Override
public WebElement findElement(SearchContext context) {
return chooseSelector().findElement(context);
return chooseSelector(context).findElement(context);
}

@Override
public List<WebElement> findElements(SearchContext context) {
return chooseSelector().findElements(context);
return chooseSelector(context).findElements(context);
}

private By chooseSelector() {
if (isAndroidDriver()) {
private By chooseSelector(SearchContext context) {
if (instanceOf(context, AndroidDriver.class)) {
return requireNonNull(androidSelector, "Android selector not given");
}
if (isIosDriver()) {
if (instanceOf(context, IOSDriver.class)) {
return requireNonNull(iosSelector, "iOS selector not given");
}
throw new UnsupportedOperationException("Unsupported webdriver: " + WebDriverRunner.getWebDriver());
Expand Down