Skip to content

Commit 9526b6b

Browse files
committed
Merge branch 'master' into feature/Swipe-Actions
2 parents 997b2a9 + 9f5dba2 commit 9526b6b

29 files changed

+179
-216
lines changed

README.md

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,29 @@ We use interfaces where is possible, so you can implement your own version of ta
2222
<dependency>
2323
<groupId>com.github.aquality-automation</groupId>
2424
<artifactId>aquality-appium-mobile</artifactId>
25-
<version>LATEST</version>
25+
<version>${LATEST_VERSION}</version>
2626
</dependency>
2727
```
2828

29-
2. Configure path to your application at settings.json:
29+
2. Configure the path to your application at settings.json:
3030
- Copy [settings.json](./src/main/resources/settings.json) into the resources directory of your project.
3131
- Open settings.json and find `applicationPath` option under the `driverSettings` section of desired platform. Replace the value with full or relative path to your app, e.g. `./src/test/resources/apps/ApiDemos-debug.apk`.
3232

3333
3. Ensure that [Appium server](https://appium.io) is set up at your machine where the code would be executed, and the address/port match to set in your `settings.json` in `remoteConnectionUrl` parameter.
3434
If the parameter `isRemote` in your settings.json is set to `false`, this means that AppiumDriverLocalService would be used to setup Appium server using Node.js. This option requires specific version of node.js to be preinstalled on your machine (Please read more [here](http://appium.io/docs/en/contributing-to-appium/appium-from-source/#nodejs) )
3535

3636
4. (optional) Launch an application directly by calling `AqualityServices.getApplication();`.
37+
3738
> Note:
3839
If you don't start an Application directly, it would be started with the first call of any Aquality service or class requiring interacting with the Application.
3940

4041
5. That's it! Now you are able work with Application via AqualityServices or via element services.
4142
Please take a look at our example tests [here](./src/test/java/samples/)
4243

4344
6. To interact with Application's forms and elements, we recommend following the Page/Screen Objects pattern. This approach is fully integrated into our package.
44-
To start with that, you will need to create a separate class for each window/form of your application, and inherit this class from the [AndroidScreen](./src/main/java/aquality/appium/mobile/screens/AndroidScreen.java) or [IOSScreen](./src/main/java/aquality/appium/mobile/screens/IOSScreen.java) respectively.
45-
46-
> We recommend to use separate Screen class for each form of your application. You can take advantage of inheritance and composition pattern. We also suggest not to mix app different platforms in single class: take advantage of interfaces instead, adding the default implementation to them if is needed.
45+
To start with that, you will need to create a separate class for each window/form of your application, and inherit this class from the [Screen](./src/main/java/aquality/appium/mobile/screens/Screen.java).
4746

47+
> We recommend to use separate Screen class for each form of your application. You can take advantage of inheritance and composition pattern. We also suggest not to mix app different platforms in single class: take advantage of interfaces instead, adding the default implementation to them if it is needed.
4848
4949
7. From the Screen Object perspective, each Screen consists of elements on it (e.g. Buttons, TextBox, Labels and so on).
5050
To interact with elements, on your form class create fields of type IButton, ITextBox, ILabel, and initialize them using the `getElementFactory()`. Created elements have a various methods to interact with them. We recommend combining actions into a business-level methods:
@@ -77,72 +77,101 @@ public class InvokeSearchScreen extends AndroidScreen {
7777
return lblSearchResult.getText();
7878
}
7979
}
80-
8180
```
8281

8382
8. We use DI Guice to inject dependencies, so you can simply implement your MobileModule extended from [MobileModule](./src/main/java/aquality/appium/mobile/application/MobileModule.java) and inject it to `AqualityServices.initInjector(yourModule)`.
8483

8584
### ScreenFactory
8685

87-
When you automate tests for both iOS and Android platforms it is good to have only one set of tests and different implementations of screens. `ScreenFactory` allows to do this. You can define interfaces for your screens and have different implementations for iOS and Android platforms. And then use `ScreenFactory` to resolve necessary screen depending on the chosen platform.
86+
When you automate tests for both iOS and Android platforms it is good to have only one set of tests and different implementations of screens. `ScreenFactory` allows to do this. You can define abstract classes for your screens and have different implementations for iOS and Android platforms. After that you can use `ScreenFactory` to resolve a necessary screen depending on the chosen platform.
8887

8988
1. Set `screensLocation` property in `settings.json`. It is a name of package where you define screens.
9089

91-
2. Define interfaces for the screens:
90+
2. Define abstract classes for the screens:
9291

9392
```java
94-
package aquality.appium.mobile.template.screens.interfaces;
93+
package aquality.appium.mobile.template.screens.login;
94+
95+
import aquality.appium.mobile.elements.interfaces.IButton;
96+
import aquality.appium.mobile.elements.interfaces.ITextBox;
97+
import aquality.appium.mobile.screens.Screen;
98+
import org.openqa.selenium.By;
99+
100+
public abstract class LoginScreen extends Screen {
101+
102+
private final ITextBox usernameTxb;
103+
private final ITextBox passwordTxb;
104+
private final IButton loginBtn;
105+
106+
protected LoginScreen(By locator) {
107+
super(locator, "Login");
108+
usernameTxb = getElementFactory().getTextBox(getUsernameTxbLoc(), "Username");
109+
passwordTxb = getElementFactory().getTextBox(getPasswordTxbLoc(), "Password");
110+
loginBtn = getElementFactory().getButton(getLoginBtnLoc(), "Login");
111+
}
112+
113+
protected abstract By getUsernameTxbLoc();
95114

96-
public interface ILoginScreen extends IScreen {
115+
protected abstract By getPasswordTxbLoc();
97116

98-
ILoginScreen setUsername(final String username);
117+
protected abstract By getLoginBtnLoc();
99118

100-
ILoginScreen setPassword(final String password);
119+
public LoginScreen setUsername(final String username) {
120+
usernameTxb.sendKeys(username);
121+
return this;
122+
}
123+
124+
public LoginScreen setPassword(final String password) {
125+
passwordTxb.typeSecret(password);
126+
return this;
127+
}
101128

102-
void tapLogin();
129+
public void tapLogin() {
130+
loginBtn.click();
131+
}
103132
}
104133
```
105134

106135
3. Implement interface (Android example):
107136

108137
```java
109-
package aquality.appium.mobile.template.screens.android;
138+
package aquality.appium.mobile.template.screens.login;
110139

111-
import aquality.appium.mobile.screens.AndroidScreen;
112-
import aquality.appium.mobile.template.screens.interfaces.ILoginScreen;
140+
import aquality.appium.mobile.application.PlatformName;
141+
import aquality.appium.mobile.screens.screenfactory.ScreenType;
142+
import org.openqa.selenium.By;
113143

114144
import static io.appium.java_client.MobileBy.AccessibilityId;
115145
import static org.openqa.selenium.By.xpath;
116146

117-
public class LoginScreen extends AndroidScreen implements ILoginScreen {
147+
@ScreenType(platform = PlatformName.ANDROID)
148+
public class AndroidLoginScreen extends LoginScreen {
118149

119-
public LoginScreen() {
120-
super(xpath("//android.widget.TextView[@text='Login']"), "Login");
150+
public AndroidLoginScreen() {
151+
super(xpath("//android.widget.TextView[@text='Login']"));
121152
}
122153

123154
@Override
124-
public ILoginScreen setUsername(final String username) {
125-
getElementFactory().getTextBox(AccessibilityId("username"), "Username").sendKeys(username);
126-
return this;
155+
protected By getUsernameTxbLoc() {
156+
return AccessibilityId("username");
127157
}
128158

129159
@Override
130-
public ILoginScreen setPassword(final String password) {
131-
getElementFactory().getTextBox(AccessibilityId("password"), "Password").typeSecret(password);
132-
return this;
160+
protected By getPasswordTxbLoc() {
161+
return AccessibilityId("password");
133162
}
134163

135164
@Override
136-
public void tapLogin() {
137-
getElementFactory().getButton(AccessibilityId("loginBtn"), "Login").click();
165+
protected By getLoginBtnLoc() {
166+
return AccessibilityId("loginBtn");
138167
}
139168
}
140169
```
141170

142171
4. Resolve screen in test:
143172

144173
```java
145-
ILoginScreen loginScreen = AqualityServices.getScreenFactory().getScreen(ILoginScreen.class);
174+
LoginScreen loginScreen = AqualityServices.getScreenFactory().getScreen(LoginScreen.class);
146175
```
147176

148177
You can find an example in [aquality-appium-mobile-java-template](https://github.com/aquality-automation/aquality-appium-mobile-java-template) repository.

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.github.aquality-automation</groupId>
88
<artifactId>aquality-appium-mobile</artifactId>
9-
<version>1.2.0</version>
9+
<version>2.0.0</version>
1010

1111
<packaging>jar</packaging>
1212
<name>Aquality Appium Mobile</name>
@@ -176,7 +176,7 @@
176176
<dependency>
177177
<groupId>com.github.aquality-automation</groupId>
178178
<artifactId>aquality-selenium-core</artifactId>
179-
<version>1.0.0</version>
179+
<version>1.0.1</version>
180180
</dependency>
181181

182182
<dependency>
@@ -198,4 +198,4 @@
198198
<version>0.9.12</version>
199199
</dependency>
200200
</dependencies>
201-
</project>
201+
</project>

src/main/java/aquality/appium/mobile/application/AqualityServices.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import aquality.appium.mobile.configuration.ILocalServiceSettings;
77
import aquality.appium.mobile.elements.interfaces.IElementFactory;
88
import aquality.appium.mobile.screens.screenfactory.IScreenFactory;
9-
import aquality.appium.mobile.screens.screenfactory.IScreenFactoryProvider;
109
import aquality.selenium.core.localization.ILocalizedLogger;
1110
import aquality.selenium.core.logging.Logger;
1211
import aquality.selenium.core.waitings.IConditionalWait;
@@ -163,7 +162,7 @@ public static IElementFactory getElementFactory() {
163162
* @return Factory of screens.
164163
*/
165164
public static IScreenFactory getScreenFactory() {
166-
return get(IScreenFactoryProvider.class).getScreenFactory();
165+
return get(IScreenFactory.class);
167166
}
168167

169168
/**

src/main/java/aquality/appium/mobile/application/MobileModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import aquality.appium.mobile.configuration.ITouchActionsConfiguration;
1010
import aquality.appium.mobile.elements.IElementsModule;
1111
import aquality.appium.mobile.elements.interfaces.IElementFactory;
12-
import aquality.appium.mobile.screens.screenfactory.IScreenFactoryProvider;
12+
import aquality.appium.mobile.screens.screenfactory.IScreenFactory;
1313
import aquality.appium.mobile.screens.screenfactory.IScreensModule;
1414
import aquality.selenium.core.applications.AqualityModule;
1515
import com.google.inject.Provider;
@@ -28,7 +28,7 @@ protected void configure() {
2828
bind(ILocalServiceSettings.class).to(getLocalServiceSettingsImplementation()).in(Singleton.class);
2929
bind(IConfiguration.class).to(getConfigurationImplementation());
3030
bind(IElementFactory.class).to(getElementFactoryImplementation());
31-
bind(IScreenFactoryProvider.class).to(getScreenFactoryProviderImplementation());
31+
bind(IScreenFactory.class).to(getScreenFactoryImplementation());
3232
bind(ITouchActionsConfiguration.class).to(getTouchActionsConfigurationImplementation()).in(Singleton.class);
3333
bind(ITouchActions.class).to(getTouchActionsImplementation()).in(Singleton.class);
3434
}

src/main/java/aquality/appium/mobile/elements/ElementType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ public enum ElementType {
1111
RADIOBUTTON(IRadioButton.class),
1212
TEXTBOX(ITextBox.class);
1313

14-
private Class<? extends IElement> clazz;
14+
private final Class<? extends IElement> clazz;
1515

1616
<T extends IElement> ElementType(Class<T> clazz){
1717
this.clazz = clazz;
1818
}
1919

20+
@SuppressWarnings("unchecked")
2021
public <T extends IElement> Class<T> getClazz() {
2122
return (Class<T>) clazz;
2223
}

src/main/java/aquality/appium/mobile/screens/AndroidScreen.java

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/main/java/aquality/appium/mobile/screens/IOSScreen.java

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package aquality.appium.mobile.screens;
22

33
import aquality.appium.mobile.application.AqualityServices;
4-
import aquality.appium.mobile.application.PlatformName;
54
import aquality.appium.mobile.elements.interfaces.IElementFactory;
65
import aquality.appium.mobile.elements.interfaces.ILabel;
76
import aquality.selenium.core.elements.interfaces.IElementStateProvider;
8-
import io.appium.java_client.AppiumDriver;
97
import org.openqa.selenium.By;
108
import org.openqa.selenium.Dimension;
11-
import org.openqa.selenium.InvalidArgumentException;
129

13-
public abstract class Screen<T extends AppiumDriver> implements IScreen {
10+
public abstract class Screen implements IScreen {
1411

1512
private final By locator;
1613
private final String name;
@@ -25,27 +22,6 @@ protected Screen(By locator, String name) {
2522
this.screenLabel = getElementFactory().getLabel(locator, name);
2623
}
2724

28-
protected IElementFactory getElementFactory(){
29-
return AqualityServices.getElementFactory();
30-
}
31-
32-
@SuppressWarnings("unchecked")
33-
protected T getDriver() {
34-
ensureApplicationPlatformCorrect(getPlatform());
35-
return (T) AqualityServices.getApplication().getDriver();
36-
}
37-
38-
protected abstract PlatformName getPlatform();
39-
40-
private void ensureApplicationPlatformCorrect(PlatformName targetPlatform) {
41-
PlatformName currentPlatform = AqualityServices.getApplication().getPlatformName();
42-
if (targetPlatform != currentPlatform) {
43-
throw new InvalidArgumentException(String.format(
44-
"Cannot perform this operation. Current platform %s don't match to target %s",
45-
currentPlatform, targetPlatform));
46-
}
47-
}
48-
4925
public By getLocator() {
5026
return locator;
5127
}
@@ -61,4 +37,8 @@ public Dimension getSize() {
6137
public IElementStateProvider state() {
6238
return screenLabel.state();
6339
}
40+
41+
protected IElementFactory getElementFactory(){
42+
return AqualityServices.getElementFactory();
43+
}
6444
}

src/main/java/aquality/appium/mobile/screens/screenfactory/AndroidScreenFactory.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/main/java/aquality/appium/mobile/screens/screenfactory/IOSScreenFactory.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)