Skip to content

Commit 665c7f5

Browse files
refactor!: Switch e2e tests to use Appium2 (#1603)
BREAKING CHANGE: Updated command line args to comply with Appium 2.0 BREAKING CHANGE: The default local service URL does not contain the /wd/hub prefix anymore
1 parent 82fb923 commit 665c7f5

File tree

12 files changed

+93
-95
lines changed

12 files changed

+93
-95
lines changed

.azure-templates/bootstrap_steps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ steps:
77
npm config set prefix $NVM_DIR/versions/node/`node --version`
88
node --version
99
10-
npm install -g appium@beta
10+
npm install -g appium@next

azure-pipelines.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ variables:
1414
XCODE_VERSION: 11.5
1515
IOS_PLATFORM_VERSION: 13.5
1616
IOS_DEVICE_NAME: iPhone X
17-
NODE_VERSION: 12.x
17+
NODE_VERSION: 14.x
1818
JDK_VERSION: 1.8
1919

2020
jobs:
2121
- job: Android_E2E_Tests
2222
steps:
2323
- template: .azure-templates/bootstrap_steps.yml
24+
- script: $NVM_DIR/versions/node/`node --version`/bin/appium driver install uiautomator2
25+
displayName: Install UIA2 driver
2426
- script: |
2527
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)'
2628
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n "$(ANDROID_EMU_NAME)" -k 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)' --force
@@ -50,6 +52,8 @@ jobs:
5052
sudo xcode-select -s /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer
5153
xcrun simctl list
5254
displayName: Simulator configuration
55+
- script: $NVM_DIR/versions/node/`node --version`/bin/appium driver install xcuitest
56+
displayName: Install XCUITest driver
5357
- task: Gradle@2
5458
inputs:
5559
gradleWrapperFile: 'gradlew'

docs/v7-to-v8-migration-guide.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,15 @@ for more details on how to properly apply W3C Actions to your automation context
9696
- AppiumDriver methods `resetApp`, `launchApp` and `closeApp` have been deprecated as
9797
they are going to be removed from future Appium versions. Check
9898
https://github.com/appium/appium/issues/15807 for more details.
99+
100+
## AppiumDriverLocalService
101+
102+
- The default URL the server is listening on has been changed, and it
103+
does not contain the `/wd/hub` suffix anymore (e.g. `http://0.0.0.0:4723/wd/hub`
104+
became `http://0.0.0.0:4723/`). This has been done in order
105+
to align the actual behavior with Appium v2. If you still would like to use
106+
v8 of the Java client with Appium v1.2x, where the server URL contains the `/wd/hub` suffix
107+
by default, then consider providing `--base-path` setting explicitly while
108+
building `AppiumServiceBuilder` instance (e.g. `.withArgument(GeneralServerFlag.BASEPATH, "/wd/hub")`).
109+
Older versions of Appium server (v1.19 and older) won't work with `AppiumDriverLocalService`,
110+
because they don't allow provisioning of base path in form of a command line argument.

src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import com.google.common.annotations.VisibleForTesting;
2525

26+
import lombok.SneakyThrows;
2627
import org.apache.commons.lang3.StringUtils;
2728

2829
import org.openqa.selenium.net.UrlChecker;
@@ -37,6 +38,7 @@
3738
import java.io.OutputStream;
3839
import java.lang.reflect.Field;
3940
import java.net.MalformedURLException;
41+
import java.net.URI;
4042
import java.net.URL;
4143
import java.time.Duration;
4244
import java.util.List;
@@ -52,7 +54,7 @@
5254

5355
public final class AppiumDriverLocalService extends DriverService {
5456

55-
private static final String URL_MASK = "http://%s:%d/wd/hub";
57+
private static final String URL_MASK = "http://%s:%d/";
5658
private static final Logger LOG = LoggerFactory.getLogger(AppiumDriverLocalService.class);
5759
private static final Pattern LOG_MESSAGE_PATTERN = Pattern.compile("^(.*)\\R");
5860
private static final Pattern LOGGER_CONTEXT_PATTERN = Pattern.compile("^(\\[debug\\] )?\\[(.+?)\\]");
@@ -66,11 +68,14 @@ public final class AppiumDriverLocalService extends DriverService {
6668
private final ReentrantLock lock = new ReentrantLock(true); //uses "fair" thread ordering policy
6769
private final ListOutputStream stream = new ListOutputStream().add(System.out);
6870
private final URL url;
71+
private String basePath;
6972

7073
private CommandLine process = null;
7174

72-
AppiumDriverLocalService(String ipAddress, File nodeJSExec, int nodeJSPort, Duration startupTimeout,
73-
List<String> nodeJSArgs, Map<String, String> nodeJSEnvironment) throws IOException {
75+
AppiumDriverLocalService(String ipAddress, File nodeJSExec,
76+
int nodeJSPort, Duration startupTimeout,
77+
List<String> nodeJSArgs, Map<String, String> nodeJSEnvironment
78+
) throws IOException {
7479
super(nodeJSExec, nodeJSPort, startupTimeout, nodeJSArgs, nodeJSEnvironment);
7580
this.nodeJSExec = nodeJSExec;
7681
this.nodeJSArgs = nodeJSArgs;
@@ -87,14 +92,34 @@ public static AppiumDriverLocalService buildService(AppiumServiceBuilder builder
8792
return builder.build();
8893
}
8994

95+
public AppiumDriverLocalService withBasePath(String basePath) {
96+
this.basePath = basePath;
97+
return this;
98+
}
99+
100+
public String getBasePath() {
101+
return this.basePath;
102+
}
103+
104+
@SneakyThrows
105+
private static URL addSuffix(URL url, String suffix) {
106+
return url.toURI().resolve("." + (suffix.startsWith("/") ? suffix : "/" + suffix)).toURL();
107+
}
108+
109+
@SneakyThrows
110+
@SuppressWarnings("SameParameterValue")
111+
private static URL replaceHost(URL source, String oldHost, String newHost) {
112+
return new URL(source.toString().replace(oldHost, newHost));
113+
}
114+
90115
/**
91116
* Base URL.
92117
*
93118
* @return The base URL for the managed appium server.
94119
*/
95120
@Override
96121
public URL getUrl() {
97-
return url;
122+
return basePath == null ? url : addSuffix(url, basePath);
98123
}
99124

100125
@Override
@@ -125,7 +150,7 @@ public boolean isRunning() {
125150

126151
private void ping(Duration timeout) throws UrlChecker.TimeoutException, MalformedURLException {
127152
// The operating system might block direct access to the universal broadcast IP address
128-
URL status = new URL(url.toString().replace(BROADCAST_IP_ADDRESS, "127.0.0.1") + "/status");
153+
URL status = addSuffix(replaceHost(getUrl(), BROADCAST_IP_ADDRESS, "127.0.0.1"), "/status");
129154
new UrlChecker().waitUntilAvailable(timeout.toMillis(), TimeUnit.MILLISECONDS, status);
130155
}
131156

src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
import io.appium.java_client.remote.AndroidMobileCapabilityType;
2626
import io.appium.java_client.remote.MobileBrowserType;
2727
import io.appium.java_client.remote.MobileCapabilityType;
28+
import io.appium.java_client.service.local.flags.GeneralServerFlag;
2829
import io.appium.java_client.service.local.flags.ServerArgument;
2930

31+
import lombok.SneakyThrows;
3032
import org.apache.commons.io.IOUtils;
3133
import org.apache.commons.lang3.StringUtils;
3234
import org.apache.commons.lang3.SystemUtils;
@@ -446,16 +448,15 @@ public AppiumServiceBuilder withLogFile(File logFile) {
446448
return super.withLogFile(logFile);
447449
}
448450

451+
@SneakyThrows
449452
@Override
450453
protected AppiumDriverLocalService createDriverService(File nodeJSExecutable, int nodeJSPort,
451454
Duration startupTimeout,
452455
List<String> nodeArguments,
453456
Map<String, String> nodeEnvironment) {
454-
try {
455-
return new AppiumDriverLocalService(ipAddress, nodeJSExecutable, nodeJSPort, startupTimeout, nodeArguments,
456-
nodeEnvironment);
457-
} catch (IOException e) {
458-
throw new RuntimeException(e);
459-
}
457+
String basePath = serverArguments.getOrDefault(
458+
GeneralServerFlag.BASEPATH.getArgument(), serverArguments.get("-pa"));
459+
return new AppiumDriverLocalService(ipAddress, nodeJSExecutable, nodeJSPort, startupTimeout, nodeArguments,
460+
nodeEnvironment).withBasePath(basePath);
460461
}
461462
}

src/main/java/io/appium/java_client/service/local/flags/GeneralServerFlag.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package io.appium.java_client.service.local.flags;
1818

19-
2019
/**
2120
* Here is the list of common Appium server arguments.
2221
*/
@@ -43,7 +42,10 @@ public enum GeneralServerFlag implements ServerArgument {
4342
* Pre-launch the application before allowing the first session
4443
* (Requires –app and, for Android, –app-pkg and –app-activity).
4544
* Default: false
45+
*
46+
* @deprecated This argument has been removed from Appium 2.0
4647
*/
48+
@Deprecated
4749
PRE_LAUNCH("--pre-launch"),
4850
/**
4951
* The message log level to be shown.
@@ -75,14 +77,6 @@ public enum GeneralServerFlag implements ServerArgument {
7577
* --nodeconfig /abs/path/to/nodeconfig.json
7678
*/
7779
CONFIGURATION_FILE("--nodeconfig"),
78-
/**
79-
* IP Address of robot. Sample: --robot-address 0.0.0.0
80-
*/
81-
ROBOT_ADDRESS("--robot-address"),
82-
/**
83-
* Port for robot. Sample: --robot-port 4242
84-
*/
85-
ROBOT_PORT("--robot-port"),
8680
/**
8781
* Show info about the Appium server configuration and exit. Default: false
8882
*/
@@ -140,33 +134,21 @@ public enum GeneralServerFlag implements ServerArgument {
140134
* Plugins are available with Appium as of Appium 2.0.
141135
* To activate all plugins, you can use the single string "all" as the value (e.g --plugins=all)
142136
* Default: []
143-
* Sample: --plugins=device-farm,images
137+
* Sample: --use-plugins=device-farm,images
144138
*/
145-
PLUGINS("--plugins"),
139+
USE_PLUGINS("--use-plugins"),
146140
/**
147141
* A comma-separated list of installed driver names that should be active for this server.
148142
* All drivers will be active by default.
149143
* Default: []
150-
* Sample: --drivers=uiautomator2,xcuitest
144+
* Sample: --use-drivers=uiautomator2,xcuitest
151145
*/
152-
DRIVERS("--drivers"),
146+
USE_DRIVERS("--use-drivers"),
153147
/**
154148
* Base path to use as the prefix for all webdriver routes running on this server.
155149
* Sample: --base-path=/wd/hub
156150
*/
157-
BASEPATH("--base-path"),
158-
/**
159-
* Set the default desired client arguments for a plugin.
160-
* Default: []
161-
* Sample: [ '{"images":{"foo1": "bar1", "foo2": "bar2"}}' | /path/to/pluginArgs.json ]
162-
*/
163-
PLUGINARGS("--plugin-args"),
164-
/**
165-
* Set the default desired client arguments for a driver.
166-
* Default: []
167-
* Sample: [ '{"xcuitest": {"foo1": "bar1", "foo2": "bar2"}}' | /path/to/driverArgs.json ]
168-
*/
169-
DRIVERARGS("--driver-args");
151+
BASEPATH("--base-path");
170152

171153
private final String arg;
172154

src/test/java/io/appium/java_client/android/BaseAndroidTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,29 @@
1818

1919
import io.appium.java_client.android.options.UiAutomator2Options;
2020
import io.appium.java_client.service.local.AppiumDriverLocalService;
21-
import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException;
2221

22+
import io.appium.java_client.service.local.AppiumServiceBuilder;
2323
import org.junit.AfterClass;
2424
import org.junit.BeforeClass;
2525

2626
import static io.appium.java_client.TestResources.apiDemosApk;
2727

2828
public class BaseAndroidTest {
2929
public static final String APP_ID = "io.appium.android.apis";
30+
protected static final int PORT = 4723;
31+
3032
private static AppiumDriverLocalService service;
3133
protected static AndroidDriver driver;
3234

3335
/**
3436
* initialization.
3537
*/
3638
@BeforeClass public static void beforeClass() {
37-
service = AppiumDriverLocalService.buildDefaultService();
39+
service = new AppiumServiceBuilder()
40+
.withIPAddress("127.0.0.1")
41+
.usingPort(PORT)
42+
.build();
3843
service.start();
39-
if (service == null || !service.isRunning()) {
40-
throw new AppiumServerHasNotBeenStartedLocallyException(
41-
"An appium server node is not started!");
42-
}
4344

4445
UiAutomator2Options options = new UiAutomator2Options()
4546
.setDeviceName("Android Emulator")
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package io.appium.java_client.ios;
22

33
import io.appium.java_client.ios.options.XCUITestOptions;
4-
import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException;
54
import org.junit.BeforeClass;
65
import org.openqa.selenium.SessionNotCreatedException;
76

8-
import java.net.URL;
97
import java.time.Duration;
108

119
import static io.appium.java_client.TestResources.testAppZip;
@@ -16,22 +14,19 @@ public class AppIOSTest extends BaseIOSTest {
1614

1715
@BeforeClass
1816
public static void beforeClass() throws Exception {
19-
final String ip = startAppiumServer();
20-
21-
if (service == null || !service.isRunning()) {
22-
throw new AppiumServerHasNotBeenStartedLocallyException("An appium server node is not started!");
23-
}
17+
startAppiumServer();
2418

2519
XCUITestOptions options = new XCUITestOptions()
20+
.setPlatformVersion(PLATFORM_VERSION)
2621
.setDeviceName(DEVICE_NAME)
2722
.setCommandTimeouts(Duration.ofSeconds(240))
2823
.setApp(testAppZip().toAbsolutePath().toString())
2924
.setWdaLaunchTimeout(WDA_LAUNCH_TIMEOUT);
3025
try {
31-
driver = new IOSDriver(new URL("http://" + ip + ":" + PORT + "/wd/hub"), options);
26+
driver = new IOSDriver(service.getUrl(), options);
3227
} catch (SessionNotCreatedException e) {
3328
options.useNewWDA();
34-
driver = new IOSDriver(new URL("http://" + ip + ":" + PORT + "/wd/hub"), options);
29+
driver = new IOSDriver(service.getUrl(), options);
3530
}
3631
}
3732
}

src/test/java/io/appium/java_client/ios/BaseIOSTest.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,33 @@
2020
import io.appium.java_client.service.local.AppiumServiceBuilder;
2121
import org.junit.AfterClass;
2222

23-
import java.net.SocketException;
24-
import java.net.UnknownHostException;
2523
import java.time.Duration;
2624

27-
import static io.appium.java_client.TestUtils.getLocalIp4Address;
28-
2925
public class BaseIOSTest {
3026

3127
protected static AppiumDriverLocalService service;
3228
protected static IOSDriver driver;
3329
protected static final int PORT = 4723;
3430
public static final String DEVICE_NAME = System.getenv("IOS_DEVICE_NAME") != null
35-
? System.getenv("IOS_DEVICE_NAME") : "iPhone 12";
31+
? System.getenv("IOS_DEVICE_NAME")
32+
: "iPhone 12";
3633
public static final String PLATFORM_VERSION = System.getenv("IOS_PLATFORM_VERSION") != null
37-
? System.getenv("IOS_PLATFORM_VERSION") : "14.5";
34+
? System.getenv("IOS_PLATFORM_VERSION")
35+
: "14.5";
3836
public static final Duration WDA_LAUNCH_TIMEOUT = Duration.ofSeconds(240);
3937

4038
/**
4139
* Starts a local server.
4240
*
43-
* @return ip of a local host
44-
* @throws UnknownHostException when it is impossible to get ip address of a local host
41+
* @return service instance
4542
*/
46-
public static String startAppiumServer() throws UnknownHostException, SocketException {
47-
service = new AppiumServiceBuilder().usingPort(PORT).build();
43+
public static AppiumDriverLocalService startAppiumServer() {
44+
service = new AppiumServiceBuilder()
45+
.withIPAddress("127.0.0.1")
46+
.usingPort(PORT)
47+
.build();
4848
service.start();
49-
return getLocalIp4Address();
49+
return service;
5050
}
5151

5252
/**

0 commit comments

Comments
 (0)