diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java index 67af93096..280cd02d1 100644 --- a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java +++ b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java @@ -19,7 +19,6 @@ import io.appium.java_client.internal.CapabilityHelpers; import io.appium.java_client.pagefactory.bys.ContentType; import io.appium.java_client.pagefactory.locator.CacheableLocator; -import org.openqa.selenium.Capabilities; import org.openqa.selenium.HasCapabilities; import org.openqa.selenium.SearchContext; import org.openqa.selenium.WebDriver; @@ -46,7 +45,7 @@ import java.util.Map; import static io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy; -import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.unpackWebDriverFromSearchContext; +import static io.appium.java_client.pagefactory.utils.WebDriverUnpackUtility.unpackObjectFromSearchContext; import static io.appium.java_client.remote.options.SupportsAutomationNameOption.AUTOMATION_NAME_OPTION; import static java.time.Duration.ofSeconds; @@ -82,23 +81,15 @@ public class AppiumFieldDecorator implements FieldDecorator { * @param duration is a desired duration of the waiting for an element presence. */ public AppiumFieldDecorator(SearchContext context, Duration duration) { - WebDriver wd = unpackWebDriverFromSearchContext(context); - this.webDriverReference = wd == null ? null : new WeakReference<>(wd); - if (wd instanceof HasCapabilities) { - Capabilities caps = ((HasCapabilities) wd).getCapabilities(); - this.platform = CapabilityHelpers.getCapability(caps, CapabilityType.PLATFORM_NAME, String.class); - this.automation = CapabilityHelpers.getCapability(caps, AUTOMATION_NAME_OPTION, String.class); - } else { - this.platform = null; - this.automation = null; - } - + this.webDriverReference = unpackObjectFromSearchContext(context, WebDriver.class) + .map(WeakReference::new).orElse(null); + this.platform = readStringCapability(context, CapabilityType.PLATFORM_NAME); + this.automation = readStringCapability(context, AUTOMATION_NAME_OPTION); this.duration = duration; defaultElementFieldDecorator = createFieldDecorator(new AppiumElementLocatorFactory( context, duration, new DefaultElementByBuilder(platform, automation) )); - widgetLocatorFactory = new AppiumElementLocatorFactory( context, duration, new WidgetByBuilder(platform, automation) ); @@ -117,28 +108,32 @@ public AppiumFieldDecorator(SearchContext context) { * @param duration is a desired duration of the waiting for an element presence. */ AppiumFieldDecorator(WeakReference contextReference, Duration duration) { - WebDriver wd = unpackWebDriverFromSearchContext(contextReference.get()); - this.webDriverReference = wd == null ? null : new WeakReference<>(wd); - if (wd instanceof HasCapabilities) { - Capabilities caps = ((HasCapabilities) wd).getCapabilities(); - this.platform = CapabilityHelpers.getCapability(caps, CapabilityType.PLATFORM_NAME, String.class); - this.automation = CapabilityHelpers.getCapability(caps, AUTOMATION_NAME_OPTION, String.class); - } else { - this.platform = null; - this.automation = null; - } - + var cr = contextReference.get(); + this.webDriverReference = unpackObjectFromSearchContext(cr, WebDriver.class) + .map(WeakReference::new).orElse(null); + this.platform = readStringCapability(cr, CapabilityType.PLATFORM_NAME); + this.automation = readStringCapability(cr, AUTOMATION_NAME_OPTION); this.duration = duration; defaultElementFieldDecorator = createFieldDecorator(new AppiumElementLocatorFactory( contextReference, duration, new DefaultElementByBuilder(platform, automation) )); - widgetLocatorFactory = new AppiumElementLocatorFactory( contextReference, duration, new WidgetByBuilder(platform, automation) ); } + @Nullable + private String readStringCapability(SearchContext searchContext, String capName) { + if (searchContext == null) { + return null; + } + return unpackObjectFromSearchContext(searchContext, HasCapabilities.class) + .map(HasCapabilities::getCapabilities) + .map(caps -> CapabilityHelpers.getCapability(caps, capName, String.class)) + .orElse(null); + } + private DefaultFieldDecorator createFieldDecorator(ElementLocatorFactory factory) { return new DefaultFieldDecorator(factory) { @Override diff --git a/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java b/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java index 01cc25a7e..190f9c4ae 100644 --- a/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java +++ b/src/main/java/io/appium/java_client/pagefactory/utils/WebDriverUnpackUtility.java @@ -26,9 +26,10 @@ import javax.annotation.Nullable; +import java.util.Optional; + import static io.appium.java_client.pagefactory.bys.ContentType.HTML_OR_DEFAULT; import static io.appium.java_client.pagefactory.bys.ContentType.NATIVE_MOBILE_SPECIFIC; -import static java.util.Optional.ofNullable; public final class WebDriverUnpackUtility { private static final String NATIVE_APP_PATTERN = "NATIVE_APP"; @@ -37,35 +38,53 @@ private WebDriverUnpackUtility() { } /** - * This method extract an instance of {@link WebDriver} from the given {@link SearchContext}. + * This method extracts an instance of the given interface from the given {@link SearchContext}. + * It is expected that the {@link SearchContext} itself or the object it wraps implements it. + * * @param searchContext is an instance of {@link SearchContext}. It may be the instance of * {@link WebDriver} or {@link org.openqa.selenium.WebElement} or some other * user's extension/implementation. * Note: if you want to use your own implementation then it should implement * {@link WrapsDriver} or {@link WrapsElement} - * @return the instance of {@link WebDriver}. - * Note: if the given {@link SearchContext} is not - * {@link WebDriver} and it doesn't implement - * {@link WrapsDriver} or {@link WrapsElement} then this method returns null. - * + * @param cls interface whose instance is going to be extracted. + * @return Either an instance of the given interface or Optional.empty(). */ - @Nullable - public static WebDriver unpackWebDriverFromSearchContext(SearchContext searchContext) { - if (searchContext instanceof WebDriver) { - return (WebDriver) searchContext; + public static Optional unpackObjectFromSearchContext(@Nullable SearchContext searchContext, Class cls) { + if (searchContext == null) { + return Optional.empty(); } + if (cls.isAssignableFrom(searchContext.getClass())) { + return Optional.of(cls.cast(searchContext)); + } if (searchContext instanceof WrapsDriver) { - return unpackWebDriverFromSearchContext(((WrapsDriver) searchContext).getWrappedDriver()); + return unpackObjectFromSearchContext(((WrapsDriver) searchContext).getWrappedDriver(), cls); } - // Search context it is not only WebDriver. WebElement is search context too. // RemoteWebElement implements WrapsDriver if (searchContext instanceof WrapsElement) { - return unpackWebDriverFromSearchContext(((WrapsElement) searchContext).getWrappedElement()); + return unpackObjectFromSearchContext(((WrapsElement) searchContext).getWrappedElement(), cls); } - return null; + return Optional.empty(); + } + + /** + * This method extract an instance of {@link WebDriver} from the given {@link SearchContext}. + * @param searchContext is an instance of {@link SearchContext}. It may be the instance of + * {@link WebDriver} or {@link org.openqa.selenium.WebElement} or some other + * user's extension/implementation. + * Note: if you want to use your own implementation then it should implement + * {@link WrapsDriver} or {@link WrapsElement} + * @return the instance of {@link WebDriver}. + * Note: if the given {@link SearchContext} is not + * {@link WebDriver} and it doesn't implement + * {@link WrapsDriver} or {@link WrapsElement} then this method returns null. + * + */ + @Nullable + public static WebDriver unpackWebDriverFromSearchContext(@Nullable SearchContext searchContext) { + return unpackObjectFromSearchContext(searchContext, WebDriver.class).orElse(null); } /** @@ -83,20 +102,17 @@ public static WebDriver unpackWebDriverFromSearchContext(SearchContext searchCon * {@link SearchContext} instance doesn't implement {@link ContextAware} and {@link WrapsDriver} */ public static ContentType getCurrentContentType(SearchContext context) { - return ofNullable(unpackWebDriverFromSearchContext(context)).map(driver -> { - if (driver instanceof HasBrowserCheck && !((HasBrowserCheck) driver).isBrowser()) { - return NATIVE_MOBILE_SPECIFIC; - } + var browserCheckHolder = unpackObjectFromSearchContext(context, HasBrowserCheck.class); + if (browserCheckHolder.filter(hbc -> !hbc.isBrowser()).isPresent()) { + return NATIVE_MOBILE_SPECIFIC; + } - if (ContextAware.class.isAssignableFrom(driver.getClass())) { //it is desktop browser - ContextAware contextAware = (ContextAware) driver; - var currentContext = contextAware.getContext(); - if (currentContext != null && currentContext.toUpperCase().contains(NATIVE_APP_PATTERN)) { - return NATIVE_MOBILE_SPECIFIC; - } - } + var contextAware = unpackObjectFromSearchContext(context, ContextAware.class); + if (contextAware.map(ContextAware::getContext) + .filter(c -> c.toUpperCase().contains(NATIVE_APP_PATTERN)).isPresent()) { + return NATIVE_MOBILE_SPECIFIC; + } - return HTML_OR_DEFAULT; - }).orElse(HTML_OR_DEFAULT); + return HTML_OR_DEFAULT; } }