Skip to content

Commit b6017dd

Browse files
authored
[Selenium 4] [Appium 8] [Appium Server 2] Migration to Appium 8.0.0-beta2 (#27)
* [Selenium 4] [Appium 8] [Appium Server 2] Migration to Appium 8.0.0-beta2 - make AppiumDriver non-generic - replace deprecated MobileBy references to AppiumBy and By - get rid of MobileElement - fix ClientFactory at ApplicationFactory * Reworked TouchActions and ElementTouchActions * Add method getCenter() to Element with logging * Add ability to specify ChromeOptions and other options in settings.json * Update pipeline, migrate to appium@next * Disable element caching and w3c for android web session in unit tests * Update LICENSE and README.md, add migration notes * Rename log4j.properties to log4j2.properties
1 parent a1a54a5 commit b6017dd

24 files changed

+197
-171
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright 2019 Aquality Automation
189+
Copyright 2022 Aquality Automation
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

README.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ 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}</version>
25+
<version>3.0.0-beta</version>
2626
</dependency>
2727
```
2828

@@ -31,15 +31,32 @@ We use interfaces where is possible, so you can implement your own version of ta
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.
34-
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) )
34+
If the parameter `isRemote` in your settings.json is set to `false`, this means that AppiumDriverLocalService would be used to set up 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) )
35+
36+
> Note:
37+
After migration to Appium v.8, we started using Appium server v.2 in our [azure-pipelines](azure-pipelines.yml).
38+
> It has some breaking changes, described [here](https://github.com/appium/java-client/blob/master/docs/v7-to-v8-migration-guide.md).
39+
> In particular:
40+
> 1. Please install required driver manually:
41+
> ```yaml
42+
> npm install -g appium@next
43+
> appium driver install uiautomator2
44+
> ```
45+
> 2. As soon as we continue to use "remoteConnectionUrl": "http://127.0.0.1:4723/wd/hub" in our [settings.json](./src/main/resources/settings.json), we need to specify the `--base-path` when starting Appium server:
46+
> ```yaml
47+
> appium --allow-insecure chromedriver_autodownload --base-path /wd/hub &
48+
> ```
49+
>
50+
> 3. We also recommend disabling element caching and w3c in chromeOptions when you run Android Chrome session. Take a look at example here: [settings.androidwebsession.json](./src/test/resources/settings.androidwebsession.json).
51+
3552
3653
4. (optional) Launch an application directly by calling `AqualityServices.getApplication();`.
3754
3855
> Note:
3956
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.
4057
41-
5. That's it! Now you are able work with Application via AqualityServices or via element services.
42-
Please take a look at our example tests [here](./src/test/java/samples/)
58+
5. That's it! Now you are able to work with Application via AqualityServices or via element services.
59+
Please take a look at our example tests [here](./src/test/java/samples/.).
4360
4461
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.
4562
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).
@@ -141,7 +158,7 @@ import aquality.appium.mobile.application.PlatformName;
141158
import aquality.appium.mobile.screens.screenfactory.ScreenType;
142159
import org.openqa.selenium.By;
143160
144-
import static io.appium.java_client.MobileBy.AccessibilityId;
161+
import static io.appium.java_client.AppiumBy.AccessibilityId;
145162
import static org.openqa.selenium.By.xpath;
146163
147164
@ScreenType(platform = PlatformName.ANDROID)
@@ -171,7 +188,13 @@ public class AndroidLoginScreen extends LoginScreen {
171188
4. Resolve screen in test:
172189
173190
```java
174-
LoginScreen loginScreen = AqualityServices.getScreenFactory().getScreen(LoginScreen.class);
191+
public class DemoTest {
192+
@Test
193+
public void testScreenFactory() {
194+
LoginScreen loginScreen = AqualityServices.getScreenFactory().getScreen(LoginScreen.class);
195+
Assert.assertNotNull(loginScreen, "Screen must be resolved from factory");
196+
}
197+
}
175198
```
176199
177200
You can find an example in [aquality-appium-mobile-java-template](https://github.com/aquality-automation/aquality-appium-mobile-java-template) repository.
@@ -180,7 +203,7 @@ You can find an example in [aquality-appium-mobile-java-template](https://github
180203
181204
Our library allows you to run tests on different devices and store their settings (like udid, name, etc.) in JSON files.
182205
183-
You have to add [devices.json](./src/test/resources/devices.json) file to resources where you can define a set of devices which you use to run tests.
206+
You have to add [devices.json](./src/test/resources/devices.json) file to resources where you can define a set of devices which you use for the test run.
184207
185208
It is possible to set default device for each platform in [settings.json](./src/test/resources/settings.json) by defining `deviceKey` property which is a key of device settings from `devices.json` file.
186209

azure-pipelines.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ jobs:
5353
variables:
5454
ANDROID_EMU_NAME: test
5555
ANDROID_SDK_ID: system-images;android-28;google_apis_playstore;x86
56-
XCODE_VERSION: 10.2
57-
IOS_PLATFORM_VERSION: 12.2
58-
IOS_DEVICE_NAME: iPhone X
5956

6057
steps:
6158
- task: CmdLine@2
@@ -83,16 +80,21 @@ jobs:
8380
echo "Emulator started"
8481
8582
echo "Installing Appium"
86-
npm install -g appium@v1.17.0
83+
npm install -g appium@next
8784
ln -fs /usr/local/lib/node_modules/appium/build/lib/main.js /usr/local/bin/appium
8885
chmod +x /usr/local/bin/appium
8986
export PATH=$PATH:/usr/local/bin/appium
9087
appium --version
9188
echo "Appium installed"
9289
90+
echo "Installing UIA2 driver"
91+
appium driver install uiautomator2
92+
appium driver list
93+
echo "UIA2 driver installed"
94+
9395
echo "Installing and Running Appium doctor"
9496
npm install -g request@2.34.0
95-
npm install -g appium-doctor@1.15.1
97+
npm install @appium/doctor -g
9698
ln -fs /usr/local/lib/node_modules/appium-doctor/appium-doctor.js /usr/local/bin/appium-doctor
9799
chmod +x /usr/local/bin/appium-doctor
98100
export PATH=$PATH:/usr/local/bin/appium-doctor
@@ -106,7 +108,7 @@ jobs:
106108
displayName: 'Start Appium server'
107109
inputs:
108110
script: |
109-
appium --allow-insecure chromedriver_autodownload &
111+
appium --allow-insecure chromedriver_autodownload --base-path /wd/hub &
110112
echo "Appium server started"
111113
condition: eq(variables['isRemote'], 'true')
112114

pom.xml

Lines changed: 9 additions & 13 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>2.2.0</version>
9+
<version>3.0.0-beta</version>
1010

1111
<packaging>jar</packaging>
1212
<name>Aquality Appium Mobile</name>
@@ -48,20 +48,16 @@
4848
<name>Pavel Anihimovsky</name>
4949
</developer>
5050
<developer>
51-
<id>Nikikuzi</id>
52-
<name>Nikita Kuznetsov</name>
51+
<id>n-verbitsky</id>
52+
<name>Nikita Viarbitski</name>
5353
</developer>
5454
<developer>
5555
<id>mialeska</id>
5656
<name>Alaksiej Mialeška</name>
5757
</developer>
5858
<developer>
59-
<id>sunigos</id>
60-
<name>Igor Sontsa</name>
61-
</developer>
62-
<developer>
63-
<id>knysh</id>
64-
<name>Sergey Knysh</name>
59+
<id>shketok</id>
60+
<name>Artem Lebakov</name>
6561
</developer>
6662
</developers>
6763

@@ -176,26 +172,26 @@
176172
<dependency>
177173
<groupId>com.github.aquality-automation</groupId>
178174
<artifactId>aquality-selenium-core</artifactId>
179-
<version>1.0.1</version>
175+
<version>2.0.2</version>
180176
</dependency>
181177

182178
<dependency>
183179
<groupId>io.appium</groupId>
184180
<artifactId>java-client</artifactId>
185-
<version>7.3.0</version>
181+
<version>8.0.0-beta2</version>
186182
</dependency>
187183

188184
<dependency>
189185
<groupId>org.testng</groupId>
190186
<artifactId>testng</artifactId>
191-
<version>6.14.3</version>
187+
<version>7.5</version>
192188
<scope>test</scope>
193189
</dependency>
194190

195191
<dependency>
196192
<groupId>org.reflections</groupId>
197193
<artifactId>reflections</artifactId>
198-
<version>0.9.12</version>
194+
<version>0.10.2</version>
199195
</dependency>
200196
</dependencies>
201197
</project>

src/main/java/aquality/appium/mobile/actions/TouchActions.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,31 @@
33
import aquality.appium.mobile.application.AqualityServices;
44
import aquality.appium.mobile.configuration.ITouchActionsConfiguration;
55
import aquality.selenium.core.utilities.IElementActionRetrier;
6-
import io.appium.java_client.TouchAction;
7-
import io.appium.java_client.touch.offset.PointOption;
86
import org.openqa.selenium.Point;
9-
import java.util.function.UnaryOperator;
10-
import static io.appium.java_client.touch.WaitOptions.waitOptions;
7+
import org.openqa.selenium.interactions.PointerInput;
8+
import org.openqa.selenium.interactions.Sequence;
9+
10+
import java.time.Duration;
11+
import java.util.Collections;
1112

1213
public class TouchActions implements ITouchActions {
1314

15+
private Duration getSwipeDuration() {
16+
return AqualityServices.get(ITouchActionsConfiguration.class).getSwipeDuration();
17+
}
18+
19+
private PointerInput getFinger() {
20+
return new PointerInput(PointerInput.Kind.TOUCH, "finger");
21+
}
22+
23+
private Sequence getPressSequence(PointerInput finger, Point startPoint) {
24+
Sequence swipeDown = new Sequence(finger, 0);
25+
return swipeDown
26+
.addAction(finger.createPointerMove(
27+
Duration.ZERO, PointerInput.Origin.viewport(), startPoint.getX(), startPoint.getY()))
28+
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
29+
}
30+
1431
@Override
1532
public void swipe(Point startPoint, Point endPoint) {
1633
AqualityServices.getLocalizedLogger().info(
@@ -19,10 +36,8 @@ public void swipe(Point startPoint, Point endPoint) {
1936
startPoint.getY(),
2037
endPoint.getX(),
2138
endPoint.getY());
22-
performTouchAction(touchAction -> touchAction
23-
.press(PointOption.point(startPoint))
24-
.waitAction(waitOptions(AqualityServices.get(ITouchActionsConfiguration.class).getSwipeDuration())),
25-
endPoint);
39+
PointerInput finger = getFinger();
40+
performTouchAction(finger, getPressSequence(finger, startPoint), endPoint);
2641
}
2742

2843
@Override
@@ -33,12 +48,19 @@ public void swipeWithLongPress(Point startPoint, Point endPoint) {
3348
startPoint.getY(),
3449
endPoint.getX(),
3550
endPoint.getY());
36-
performTouchAction(touchAction -> touchAction.longPress(PointOption.point(startPoint)), endPoint);
51+
PointerInput finger = getFinger();
52+
Sequence sequence = getPressSequence(finger, startPoint)
53+
.addAction(finger.createPointerMove(
54+
getSwipeDuration(), PointerInput.Origin.viewport(), startPoint.getX(), startPoint.getY()));
55+
performTouchAction(finger, sequence, endPoint);
3756
}
3857

39-
protected void performTouchAction(UnaryOperator<TouchAction<?>> function, Point endPoint) {
40-
TouchAction<?> touchAction = new TouchAction<>(AqualityServices.getApplication().getDriver());
58+
protected void performTouchAction(PointerInput finger, Sequence actionsSequence, Point endPoint) {
59+
actionsSequence
60+
.addAction(finger.createPointerMove(getSwipeDuration(),
61+
PointerInput.Origin.viewport(), endPoint.getX(), endPoint.getY()))
62+
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
4163
AqualityServices.get(IElementActionRetrier.class).doWithRetry(() ->
42-
function.apply(touchAction).moveTo(PointOption.point(endPoint)).release().perform());
64+
AqualityServices.getApplication().getDriver().perform(Collections.singletonList(actionsSequence)));
4365
}
4466
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.openqa.selenium.remote.service.DriverService;
99

1010
import java.time.Duration;
11-
import java.util.concurrent.TimeUnit;
1211

1312
public class Application implements IApplication {
1413

@@ -32,7 +31,7 @@ public Application(AppiumDriver appiumDriver, DriverService driverService) {
3231
}
3332

3433
private void setImplicitlyWaitToDriver(Duration duration) {
35-
getDriver().manage().timeouts().implicitlyWait(duration.getSeconds(), TimeUnit.SECONDS);
34+
getDriver().manage().timeouts().implicitlyWait(duration);
3635
this.timeoutImpl = duration;
3736
}
3837

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
import aquality.selenium.core.utilities.ElementActionRetrier;
66
import io.appium.java_client.AppiumDriver;
77
import io.appium.java_client.android.AndroidDriver;
8-
import io.appium.java_client.android.AndroidElement;
98
import io.appium.java_client.ios.IOSDriver;
10-
import io.appium.java_client.ios.IOSElement;
119
import org.openqa.selenium.Capabilities;
1210
import org.openqa.selenium.SessionNotCreatedException;
11+
import org.openqa.selenium.remote.http.ClientConfig;
1312
import org.openqa.selenium.remote.http.HttpClient;
14-
import org.openqa.selenium.remote.http.HttpClient.Builder;
1513
import org.openqa.selenium.remote.http.HttpClient.Factory;
1614

1715
import java.net.URL;
@@ -43,18 +41,18 @@ protected AppiumDriver createSession(PlatformName platformName, URL serviceUrl,
4341
AppiumDriver driver;
4442
switch (platformName) {
4543
case ANDROID:
46-
driver = new AndroidDriver<AndroidElement>(serviceUrl, httpClientFactory, capabilities);
44+
driver = new AndroidDriver(serviceUrl, httpClientFactory, capabilities);
4745
break;
4846
case IOS:
49-
driver = new IOSDriver<IOSElement>(serviceUrl, httpClientFactory, capabilities);
47+
driver = new IOSDriver(serviceUrl, httpClientFactory, capabilities);
5048
break;
5149
default:
5250
throw getLoggedWrongPlatformNameException(platformName.name());
5351
}
5452
return driver;
5553
}
5654

57-
protected class CustomActionRetrier extends ElementActionRetrier {
55+
protected static class CustomActionRetrier extends ElementActionRetrier {
5856
private final List<Class<? extends Throwable>> handledExceptions;
5957

6058
CustomActionRetrier(List<Class<? extends Throwable>> handledExceptions) {
@@ -68,19 +66,20 @@ public List<Class<? extends Throwable>> getHandledExceptions() {
6866
}
6967
}
7068

71-
protected class ClientFactory implements Factory {
69+
protected static class ClientFactory implements Factory {
7270

7371
private final Factory defaultClientFactory = Factory.createDefault();
7472
private final Duration timeoutCommand = AqualityServices.get(ITimeoutConfiguration.class).getCommand();
7573

7674
@Override
77-
public Builder builder() {
78-
return defaultClientFactory.builder().readTimeout(timeoutCommand);
75+
public HttpClient createClient(URL url) {
76+
return defaultClientFactory.createClient(ClientConfig.defaultConfig().baseUrl(url).readTimeout(timeoutCommand));
7977
}
8078

8179
@Override
82-
public HttpClient createClient(URL url) {
83-
return this.builder().createClient(url);
80+
public HttpClient createClient(ClientConfig clientConfig) {
81+
clientConfig.readTimeout(timeoutCommand);
82+
return defaultClientFactory.createClient(clientConfig);
8483
}
8584

8685
@Override

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static boolean isApplicationStarted() {
4949

5050
/**
5151
* Resolves required service from DI container.
52-
* Note that the service should be binded in {@link MobileModule}.
52+
* Note that the service should be bound in {@link MobileModule}.
5353
*
5454
* @param type class of required service.
5555
* @param <T> type of required service.
@@ -111,7 +111,7 @@ public static void setApplication(Application application) {
111111
/**
112112
* Reinitializes the dependency injector with custom {@link MobileModule}.
113113
*
114-
* @param module {@link MobileModule} object with custom or overriden services.
114+
* @param module {@link MobileModule} object with custom or overridden services.
115115
*/
116116
public static void initInjector(MobileModule module) {
117117
if (INSTANCE_CONTAINER.get() != null) {
@@ -193,7 +193,7 @@ public static IConfiguration getConfiguration() {
193193
}
194194

195195
/**
196-
* Gets the the utility used to perform touch actions.
196+
* Gets the utility used to perform touch actions.
197197
*
198198
* @return instance of touch actions.
199199
*/

src/main/java/aquality/appium/mobile/configuration/DriverSettings.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ public DriverSettings(ISettingsFile settingsFile, PlatformName platformName) {
3131
public Capabilities getCapabilities() {
3232
Map<String, Object> capabilitiesFromSettings = getCapabilitiesFromSettings();
3333
DesiredCapabilities capabilities = new DesiredCapabilities();
34-
capabilitiesFromSettings.forEach(capabilities::setCapability);
34+
capabilitiesFromSettings.forEach((key, value) -> {
35+
if (key.toLowerCase().endsWith("options")) {
36+
value = settingsFile.getMap(getDriverSettingsPath("capabilities", key));
37+
}
38+
capabilities.setCapability(key, value);
39+
});
3540
if (hasApplicationPath()) {
3641
capabilities.setCapability(APP_CAPABILITY_KEY, getAbsolutePath(getApplicationPath()));
3742
}

0 commit comments

Comments
 (0)