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

Repeatable annotations & HasSessionDetails API #497

Merged
merged 12 commits into from
Oct 31, 2016
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
124 changes: 122 additions & 2 deletions docs/Page-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ List<RemoteWebElement> someElements;

# Also it is possible to define chained or any possible locators.

- Chained
## - Chained

### If you use build versions < 5.x.x

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
Expand All @@ -98,7 +101,52 @@ RemoteWebElement someElement;
List<RemoteWebElement> someElements;
```

- Any possible
### If you use build versions >= 5.x.x

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
import org.openqa.selenium.support.FindBys;
import org.openqa.selenium.support.FindBy;

@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
RemoteWebElement someElement;

@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
List<RemoteWebElement> someElements;
```

or

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
import org.openqa.selenium.support.FindBys;
import org.openqa.selenium.support.FindBy;

import static io.appium.java_client.pagefactory.LocatorGroupStrategy.CHAIN;

@HowToUseLocators(androidAutomation = CHAIN, iOSAutomation = CHAIN)
@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
RemoteWebElement someElement;

@HowToUseLocators(androidAutomation = CHAIN, iOSAutomation = CHAIN)
@FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
List<RemoteWebElement> someElements;
```

## - Any possible

### If you use build versions < 5.x.x

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
Expand All @@ -116,6 +164,78 @@ RemoteWebElement someElement;
List<RemoteWebElement> someElements;
```

### If you use build versions >= 5.x.x

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.FindByAll;

import static io.appium.java_client.pagefactory.LocatorGroupStrategy.ALL_POSSIBLE;

@HowToUseLocators(androidAutomation = ALL_POSSIBLE, iOSAutomation = ALL_POSSIBLE)
@FindAll{@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
RemoteWebElement someElement;

@HowToUseLocators(androidAutomation = ALL_POSSIBLE, iOSAutomation = ALL_POSSIBLE)
@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
List<RemoteWebElement> someElements;
```

## Also possible combined variants:

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.FindByAll;

import static io.appium.java_client.pagefactory.LocatorGroupStrategy.CHAIN;
import static io.appium.java_client.pagefactory.LocatorGroupStrategy.ALL_POSSIBLE;

@HowToUseLocators(androidAutomation = CHAIN, iOSAutomation = ALL_POSSIBLE)
@FindAll{@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
RemoteWebElement someElement;

@HowToUseLocators(androidAutomation = CHAIN, iOSAutomation = ALL_POSSIBLE)
@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2)
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
List<RemoteWebElement> someElements;
```

or

```java
import org.openqa.selenium.remote.RemoteWebElement;
import io.appium.java_client.pagefactory.*;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.FindByAll;

import static io.appium.java_client.pagefactory.LocatorGroupStrategy.ALL_POSSIBLE;

@HowToUseLocators(iOSAutomation = ALL_POSSIBLE)
@FindAll{@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) //this is the chain
//by default
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
RemoteWebElement someElement;

@HowToUseLocators(iOSAutomation = ALL_POSSIBLE)
@FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)})
@AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) //this is the chain
//by default
@iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2)
List<RemoteWebElement> someElements;
```

# Appium Java client is integrated with Selenium PageFactory by AppiumFieldDecorator.

Object fields are populated as below:
Expand Down
66 changes: 27 additions & 39 deletions src/main/java/io/appium/java_client/AppiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@
* limitations under the License.
*/


package io.appium.java_client;

import static com.google.common.base.Preconditions.checkNotNull;

import static io.appium.java_client.MobileCommand.GET_SESSION;

import com.google.common.collect.ImmutableMap;

import io.appium.java_client.remote.AppiumCommandExecutor;
Expand Down Expand Up @@ -58,18 +55,17 @@
import java.util.Map;
import java.util.Set;


/**
* @param <T> the required type of class which implement {@link org.openqa.selenium.WebElement}.
* Instances of the defined type will be returned via findElement* and findElements*
* Warning (!!!). Allowed types:
* {@link org.openqa.selenium.WebElement}
* {@link io.appium.java_client.TouchableElement}
* {@link org.openqa.selenium.remote.RemoteWebElement}
* {@link io.appium.java_client.MobileElement} and its subclasses that designed
* specifically
* for each target mobile OS (still Android and iOS)
*/
* @param <T> the required type of class which implement {@link org.openqa.selenium.WebElement}.
* Instances of the defined type will be returned via findElement* and findElements*
* Warning (!!!). Allowed types:
* {@link org.openqa.selenium.WebElement}
* {@link io.appium.java_client.TouchableElement}
* {@link org.openqa.selenium.remote.RemoteWebElement}
* {@link io.appium.java_client.MobileElement} and its subclasses that designed
* specifically
* for each target mobile OS (still Android and iOS)
*/
@SuppressWarnings("unchecked")
public abstract class AppiumDriver<T extends WebElement>
extends DefaultGenericMobileDriver<T> {
Expand All @@ -81,13 +77,13 @@ public abstract class AppiumDriver<T extends WebElement>
private ExecuteMethod executeMethod;

/**
* @param executor is an instance of {@link org.openqa.selenium.remote.HttpCommandExecutor}
* or class that extends it. Default commands or another vendor-specific
* commands may be specified there.
* @param capabilities take a look
* at {@link org.openqa.selenium.Capabilities}
* @param executor is an instance of {@link org.openqa.selenium.remote.HttpCommandExecutor}
* or class that extends it. Default commands or another vendor-specific
* commands may be specified there.
* @param capabilities take a look
* at {@link org.openqa.selenium.Capabilities}
* @param converterClazz is an instance of a class that extends
* {@link org.openqa.selenium.remote.internal.JsonToWebElementConverter}. It converts
* {@link org.openqa.selenium.remote.internal.JsonToWebElementConverter}. It converts
* JSON response to an instance of
* {@link org.openqa.selenium.WebElement}
*/
Expand All @@ -100,10 +96,10 @@ protected AppiumDriver(HttpCommandExecutor executor, Capabilities capabilities,
this.remoteAddress = executor.getAddressOfRemoteServer();
try {
Constructor<? extends JsonToWebElementConverter> constructor =
converterClazz.getConstructor(RemoteWebDriver.class);
converterClazz.getConstructor(RemoteWebDriver.class);
this.setElementConverter(constructor.newInstance(this));
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException
| InvocationTargetException e) {
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
Expand All @@ -116,7 +112,7 @@ public AppiumDriver(URL remoteAddress, Capabilities desiredCapabilities,

public AppiumDriver(URL remoteAddress, HttpClient.Factory httpClientFactory,
Capabilities desiredCapabilities,
Class<? extends JsonToWebElementConverter> converterClazz) {
Class<? extends JsonToWebElementConverter> converterClazz) {
this(new AppiumCommandExecutor(MobileCommand.commandRepository, remoteAddress,
httpClientFactory), desiredCapabilities, converterClazz);
}
Expand All @@ -129,7 +125,7 @@ public AppiumDriver(AppiumDriverLocalService service, Capabilities desiredCapabi

public AppiumDriver(AppiumDriverLocalService service, HttpClient.Factory httpClientFactory,
Capabilities desiredCapabilities,
Class<? extends JsonToWebElementConverter> converterClazz) {
Class<? extends JsonToWebElementConverter> converterClazz) {
this(new AppiumCommandExecutor(MobileCommand.commandRepository, service, httpClientFactory),
desiredCapabilities, converterClazz);
}
Expand All @@ -141,14 +137,14 @@ public AppiumDriver(AppiumServiceBuilder builder, Capabilities desiredCapabiliti

public AppiumDriver(AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory,
Capabilities desiredCapabilities,
Class<? extends JsonToWebElementConverter> converterClazz) {
Class<? extends JsonToWebElementConverter> converterClazz) {
this(builder.build(), httpClientFactory, desiredCapabilities, converterClazz);
}

public AppiumDriver(HttpClient.Factory httpClientFactory, Capabilities desiredCapabilities,
Class<? extends JsonToWebElementConverter> converterClazz) {
this(AppiumDriverLocalService.buildDefaultService(), httpClientFactory, desiredCapabilities,
converterClazz);
this(AppiumDriverLocalService.buildDefaultService(), httpClientFactory,
desiredCapabilities, converterClazz);
}

public AppiumDriver(Capabilities desiredCapabilities,
Expand All @@ -158,8 +154,8 @@ public AppiumDriver(Capabilities desiredCapabilities,

/**
* @param originalCapabilities the given {@link Capabilities}.
* @param newPlatform a {@link MobileCapabilityType#PLATFORM_NAME} value which has
* to be set up
* @param newPlatform a {@link MobileCapabilityType#PLATFORM_NAME} value which has
* to be set up
* @return {@link Capabilities} with changed mobile platform value
*/
protected static Capabilities substituteMobilePlatform(Capabilities originalCapabilities,
Expand Down Expand Up @@ -414,7 +410,7 @@ public void zoom(int x, int y) {
@Override public DeviceRotation rotation() {
Response response = execute(DriverCommand.GET_SCREEN_ROTATION);
DeviceRotation deviceRotation =
new DeviceRotation((Map<String, Number>) response.getValue());
new DeviceRotation((Map<String, Number>) response.getValue());
if (deviceRotation.getX() < 0 || deviceRotation.getY() < 0 || deviceRotation.getZ() < 0) {
throw new WebDriverException("Unexpected orientation returned: " + deviceRotation);
}
Expand All @@ -428,7 +424,7 @@ public void zoom(int x, int y) {

@Override public void rotate(ScreenOrientation orientation) {
execute(DriverCommand.SET_SCREEN_ORIENTATION,
ImmutableMap.of("orientation", orientation.value().toUpperCase()));
ImmutableMap.of("orientation", orientation.value().toUpperCase()));
}

@Override public ScreenOrientation getOrientation() {
Expand Down Expand Up @@ -464,12 +460,4 @@ private TouchAction createTap(int x, int y, int duration) {
public URL getRemoteAddress() {
return remoteAddress;
}

/**
* @return a map with values that hold session details.
*/
public Map<String, Object> getSessionDetails() {
Response response = execute(GET_SESSION);
return (Map<String, Object>) response.getValue();
}
}
38 changes: 38 additions & 0 deletions src/main/java/io/appium/java_client/HasSessionDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client;

import static io.appium.java_client.MobileCommand.GET_SESSION;

import org.openqa.selenium.remote.Response;

import java.util.Map;

public interface HasSessionDetails extends ExecutesMethod {
/**
* @return a map with values that hold session details.
*
*/
default Map<String, Object> getSessionDetails() {
Response response = execute(GET_SESSION);
return (Map<String, Object>) response.getValue();
}

default Object getSessionDetail(String detail) {
return getSessionDetails().get(detail);
}
}
3 changes: 2 additions & 1 deletion src/main/java/io/appium/java_client/MobileDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
public interface MobileDriver<T extends WebElement> extends WebDriver, PerformsTouchActions, ContextAware, Rotatable,
FindsByAccessibilityId<T>, LocationContext, DeviceActionShortcuts, TouchShortcuts,
InteractsWithFiles, InteractsWithApps, HasAppStrings, FindsByClassName, FindsByCssSelector, FindsById,
FindsByLinkText, FindsByName, FindsByTagName, FindsByXPath, FindsByFluentSelector<T>, ExecutesMethod {
FindsByLinkText, FindsByName, FindsByTagName, FindsByXPath, FindsByFluentSelector<T>, ExecutesMethod,
HasSessionDetails {

List<T> findElements(By by);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@
* series of {@link io.appium.java_client.pagefactory.AndroidFindBy} tags
* It will then search for all elements that match any criteria. Note that elements
* are not guaranteed to be in document order.
* It is deprecated. Set of {@link io.appium.java_client.pagefactory.AndroidFindBy}
* can be defined without this annotation. To define the correct way how to use
* the defined set please take a look at {@link HowToUseLocators}. The article.
* https://docs.oracle.com/javase/tutorial/java/annotations/repeating.html.
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE})
public @interface AndroidFindAll {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.appium.java_client.pagefactory;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand All @@ -30,6 +31,7 @@
* using Android UI selectors, accessibility, id, name, class name, tag and xpath
*/
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE})
@Repeatable(AndroidFindBySet.class)
public @interface AndroidFindBy {
/**
* It is an is Android UIAutomator string.
Expand Down
Loading