Visitar URL original
java-client/docs/v10-to-v11-migration-guide.md at master · appium/java-client · GitHub
Skip to content

Latest commit

 

History

History
226 lines (181 loc) · 23 KB

v10-to-v11-migration-guide.md

File metadata and controls

226 lines (181 loc) · 23 KB

This is the list of main changes between major versions 10 and 11 of Appium java client. This list should help you to successfully migrate your existing automated tests codebase.

The main theme of this release is that Appium java client depends on selenium-api only. The rest of Selenium (selenium-remote-driver, selenium-http, selenium-json, selenium-os, selenium-support) is not a dependency anymore. The parts of it that the client needs are now maintained in this project, so changes in the browser oriented parts of Selenium cannot break Appium sessions anymore. See Transitive dependencies management for details.

The minimum supported Appium server version is now Appium 3, and the deprecated APIs that were kept for the older servers are removed.

For Appium 3 and newer servers nothing changes on the wire: the requests sent to the server, the capabilities and the options classes, the driver classes and their methods, and the exceptions thrown for server errors are the same as in v10. What you need to adapt are the imports, the few places where your code relied on Selenium classes that the client does not extend or expose anymore, and the usages of the removed deprecated APIs.

Migration steps

  1. Update the client to 11.x and Selenium to 4.50.0 or newer. Selenium versions below 4.50.0 won't work with Appium java client 11+. Check the Compatibility Matrix for more details. Use Appium server 3 or newer and the drivers and plugins that are compatible with it, see the Appium server.
  2. Declare the Selenium modules your own code uses. They are not brought by the client anymore: selenium-support (WebDriverWait, ExpectedConditions, Select, EventFiringDecorator, ...), selenium-remote-driver (RemoteWebDriver, Augmenter, ...), browser drivers (ChromeOptions, ...).
  3. Run the OpenRewrite recipe, it fixes most of the imports.
  4. Fix what is left of the compilation errors using the tables below.
  5. If you need RemoteWebDriver or Selenium BiDi modules, add the optional io.appium:java-client-selenium-bridge artifact, see Selenium interoperability.

Automated migration

The recipe file is a declarative OpenRewrite recipe. Run it before changing the client version, while the v10 types are still on the classpath. Save the file as rewrite.yml in the root of your project and run:

Maven

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.activeRecipes=io.appium.java_client.MigrateToV11

Gradle

plugins {
    id 'org.openrewrite.rewrite' version '7.39.0'
}

rewrite {
    activeRecipe('io.appium.java_client.MigrateToV11')
}
./gradlew rewriteRun
Recipe What it does
io.appium.java_client.MigrateToV11 Moves the types listed in the first table to their new packages.
io.appium.java_client.MigrateToV11PageObjectAnnotations Optional. Switches @FindBy, @FindBys, @FindAll, @CacheLookup and How to the Appium ones. Skip it if the same code also uses the Selenium PageFactory.
io.appium.java_client.FindV11Breakages Marks the usages that have no automatic replacement (RemoteWebDriver, HasBiDi, BiDi, DriverService, touch actions, AppiumFunction, the mobile command helpers) with /*~~>*/. Search for MobileCommand yourself, as only some of its constants are removed.

Several recipes can be activated at once, separated with commas. The recipe does not touch your build files and does not rewrite casts to RemoteWebDriver. Review the result before committing it. If the same codebase also automates browsers, check the changes of RemoteWebElement, as the recipe replaces every usage of it.

What moved

These types are covered by the recipe. The names stay the same, only the package changes.

v10 (Selenium) v11
org.openqa.selenium.remote.{SessionId, Response, Command, CommandPayload, DriverCommand, ExecuteMethod, CommandExecutor, ErrorHandler, ErrorCodes, ScreenshotException, UnreachableBrowserException} io.appium.java_client.remote.*
org.openqa.selenium.remote.CapabilityType io.appium.java_client.remote.CapabilityType (PLATFORM_NAME and BROWSER_NAME only)
org.openqa.selenium.remote.RemoteWebElement io.appium.java_client.remote.AppiumWebElement
org.openqa.selenium.remote.HttpCommandExecutor io.appium.java_client.remote.AppiumCommandExecutor
org.openqa.selenium.remote.CommandInfo io.appium.java_client.AppiumCommandInfo
org.openqa.selenium.remote.http.{ClientConfig, HttpClient, HttpRequest, HttpResponse, HttpMethod, HttpHandler, HttpHeader, HttpMessage, Filter, Contents, WebSocket, Message, TextMessage, BinaryMessage, CloseMessage, ConnectionFailedException} io.appium.java_client.http.*
org.openqa.selenium.support.{PageFactory} and org.openqa.selenium.support.pagefactory.{ElementLocator, ElementLocatorFactory, FieldDecorator, DefaultFieldDecorator, AbstractAnnotations, ByAll} io.appium.java_client.support.* and io.appium.java_client.support.pagefactory.*
org.openqa.selenium.support.{FindBy, FindBys, FindAll, CacheLookup, How} (optional, the Selenium ones keep working while selenium-support is available) io.appium.java_client.support.*
io.appium.java_client.functions.ExpectedCondition org.openqa.selenium.support.ui.ExpectedCondition

What needs a manual change

v10 v11
AppiumDriver is a RemoteWebDriver; casts to it, Augmenter, RemoteWebDriver parameters AppiumDriver extends AppiumRemoteWebDriver. Type against WebDriver, or use SeleniumBridge.asRemoteWebDriver(driver) from the bridge.
HasBiDi, driver.getBiDi(), driver.maybeGetBiDi() Removed. Use the Selenium BiDi modules through the bridge. The webSocketUrl capability of the session is still available.
new ErrorHandler(codes, true) new ErrorHandler(codes)
new AppiumDriver(HttpCommandExecutor, caps) and the same for the platform drivers new AppiumDriver(AppiumCommandExecutor, caps)
new AndroidDriver(ClientConfig, caps), same for the other drivers new AndroidDriver(AppiumClientConfig, caps), create it with AppiumClientConfig.defaultConfig() or AppiumClientConfig.fromClientConfig(clientConfig)
ClientConfig.defaultConfig() ClientConfig is an interface now, use AppiumClientConfig.defaultConfig()
driver.getClientConfig() ((AppiumCommandExecutor) driver.getCommandExecutor()).getAppiumClientConfig()
AppiumCommandExecutor#setPrivateFieldValue, the public client field Removed, use getClient()
AppiumDriverLocalService/AppiumServiceBuilder as DriverService/DriverService.Builder They are standalone classes now. getExecutable, setExecutable, sendOutputTo, getDriverProperty, getDriverEnvironmentVariable, score and withLogOutput are removed.
TouchAction, MultiTouchAction, PerformsTouchActions, AndroidTouchAction, IOSTouchAction, io.appium.java_client.touch.*, functions.ActionSupplier Removed. Use W3C Actions or the mobile: gesture commands.
PageFactory.initElements(driver, page) (the shortcut that takes a WebDriver) Removed. Use PageFactory.initElements(new AppiumFieldDecorator(driver), page).
Wait/FluentWait/Sleeper of Selenium used with AppiumFluentWait AppiumFluentWait extends io.appium.java_client.support.ui.FluentWait. Change the declared type to AppiumFluentWait (or var), or to io.appium.java_client.support.ui.Wait.
AppiumFunction, MobileCommand.prepareArguments Removed. Use java.util.function.Function and a Map. Function#compose and Function#andThen pass null on, while the AppiumFunction ones skipped the next step, so the steps of a FluentWait chain must handle null themselves.
AndroidMobileCommandHelper, IOSMobileCommandHelper, the deprecated MobileCommand constants and *Command methods Removed. Send the mobile: extension with executeScript, see the removed commands.
CanRememberExtensionPresence, AppiumDriver#assertExtensionExists, AppiumDriver#markExtensionAbsence Removed, the fallbacks that needed them are removed.
AppiumProtocolHandshake, AppiumNewSessionCommandPayload Removed
RemoteWebDriver and RemoteWebElement features that Appium servers do not serve: downloads, file upload and FileDetector, web storage, DevTools, tracing, fireSessionEvent, setLogLevel, RemoteWebDriver.builder() Not available. Shadow roots, virtual authenticators, federated credential management and manage().logs() keep working.

Details

The Appium server

  • The minimum supported Appium server version is Appium 3. Only the drivers and plugins that are compatible with Appium 3 or newer are supported.
  • A major release of the client guarantees support only for the Appium server API of the major server version that is current at the time of the release and of the previous major version. Endpoints and commands that were removed or deprecated in the server earlier may not be supported anymore. Check the Appium 2 to 3 migration guide for the endpoints that Appium 3 removed.

The removed deprecated commands

The driver interfaces (InteractsWithApps, LocksDevice, PullsFiles, PushesFiles, HidesKeyboard, HasClipboard, PressesKey, StartsActivity, ...) used to call the legacy command when the server did not have the mobile: extension. They call the mobile: extensions only now, so driver.installApp(...), driver.lockDevice() and the other API methods need no change, but UnsupportedCommandException and InvalidArgumentException of the server are not swallowed anymore. If your code sends the legacy commands with MobileCommand, switch to the extensions. The arguments of the extensions differ from the ones of the legacy commands, check the documentation of the drivers.

Removed (MobileCommand constants and *Command helpers) Use instead
RESET mobile: clearApp, then mobile: activateApp
LAUNCH_APP, CLOSE_APP mobile: activateApp or mobile: launchApp (iOS), mobile: terminateApp
RUN_APP_IN_BACKGROUND, GET_STRINGS mobile: backgroundApp, mobile: getAppStrings
IS_APP_INSTALLED, INSTALL_APP, ACTIVATE_APP, QUERY_APP_STATE, TERMINATE_APP, REMOVE_APP mobile: isAppInstalled, installApp, activateApp, queryAppState, terminateApp, removeApp
GET_CLIPBOARD, SET_CLIPBOARD mobile: getClipboard, mobile: setClipboard
HIDE_KEYBOARD, IS_KEYBOARD_SHOWN, hideKeyboardCommand, isKeyboardShownCommand mobile: hideKeyboard, mobile: isKeyboardShown
LOCK, UNLOCK, IS_LOCKED, lockDeviceCommand, unlockDeviceCommand, getIsDeviceLockedCommand mobile: lock, mobile: unlock, mobile: isLocked
GET_DEVICE_TIME mobile: getDeviceTime
pushFileCommand mobile: pushFile. PULL_FILE, PULL_FOLDER and PUSH_FILE stay, they are public now.
SHAKE, TOUCH_ID, TOUCH_ID_ENROLLMENT (iOS) mobile: shake, mobile: sendBiometricMatch, mobile: enrollBiometric
PRESS_KEY_CODE, LONG_PRESS_KEY_CODE, pressKeyCodeCommand, longPressKeyCodeCommand (Android) mobile: pressKey
CURRENT_ACTIVITY, GET_CURRENT_PACKAGE, START_ACTIVITY (Android) mobile: getCurrentActivity, mobile: getCurrentPackage, mobile: startActivity
GET_DISPLAY_DENSITY, GET_SYSTEM_BARS, OPEN_NOTIFICATIONS (Android) mobile: getDisplayDensity, mobile: getSystemBars, mobile: openNotifications
GET_NETWORK_CONNECTION, SET_NETWORK_CONNECTION, TOGGLE_WIFI, TOGGLE_DATA, TOGGLE_AIRPLANE_MODE (Android) mobile: getConnectivity, mobile: setConnectivity
TOGGLE_LOCATION_SERVICES, FINGER_PRINT (Android) mobile: toggleGps, mobile: fingerprint
SEND_SMS, GSM_CALL, GSM_SIGNAL, GSM_VOICE, NETWORK_SPEED, POWER_CAPACITY, POWER_AC_STATE (Android emulator) mobile: sendSms, gsmCall, gsmSignal, gsmVoice, networkSpeed, powerCapacity, powerAc
GET_PERFORMANCE_DATA, GET_SUPPORTED_PERFORMANCE_DATA_TYPES (Android) mobile: getPerformanceData, mobile: getPerformanceDataTypes
SET_VALUE, REPLACE_VALUE (Android) W3C sendKeys, mobile: replaceElementValue
END_TEST_COVERAGE No replacement, mobile: shell can run the instrumentation command

setPowerAC requested mobile: powerAC, which the driver does not register (it is mobile: powerAc), so it always used the legacy command. It requests the right extension now.

WindowsDriver#launchApp and WindowsDriver#closeApp send windows: launchApp and windows: closeApp. The Windows driver has no extensions to transfer files, so WindowsDriver keeps sending the driver commands for pullFile, pullFolder and pushFile.

AppiumDriver is not a RemoteWebDriver anymore

  • AppiumDriver extends io.appium.java_client.remote.AppiumRemoteWebDriver, and the elements it returns are io.appium.java_client.remote.AppiumWebElement. Both implement WebDriver and WebElement from selenium-api, so code that is typed against the interfaces does not need any change.
  • AppiumCommandExecutor does not extend HttpCommandExecutor anymore. Its constructors accept AppiumCommandInfo maps, and AppiumDriverLocalService instead of DriverService.
  • AppiumW3CHttpCommandCodec does not extend the Selenium W3C codec anymore, and ErrorCodesMobile extends io.appium.java_client.remote.ErrorCodes.

The HTTP client is maintained in this project

  • Custom HTTP clients and filters must implement the new HttpClient, Filter and HttpHandler interfaces, which have the same shape as the Selenium ones. The default implementation is based on java.net.http, as before. The Selenium client implementations (Netty, OkHttp) do not plug in anymore.
  • AppiumClientConfig does not extend the Selenium ClientConfig anymore. The instances are immutable, and the methods that change a setting return a new instance, as before.
  • AppiumClientConfig#withFilter keeps the Appium user agent and idempotency filters, and the WebSocket timeout is not reset to the default anymore when other settings are changed.
  • AppiumClientConfig.directConnect, the default timeouts and the default HTTP version are not changed.

The local server service

  • AppiumDriverLocalService#close stops the server.
  • A server that fails to start now always throws AppiumServerHasNotBeenStartedLocallyException.

Page objects and waits

  • The page factory and FluentWait engine is now io.appium.java_client.support. Page objects that use the Selenium @FindBy, @FindBys, @FindAll and @CacheLookup annotations keep working if selenium-support is available, but it is recommended to switch to the annotations from io.appium.java_client.support. See Page objects.

Touch actions

The deprecated touch actions API is removed. A tap with W3C Actions:

var finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
var tap = new Sequence(finger, 0)
        .addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), x, y))
        .addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
        .addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(tap));

The gestures can also be performed with the commands of the Appium drivers, for example driver.executeScript("mobile: clickGesture", Map.of("x", x, "y", y)) for UiAutomator2 and driver.executeScript("mobile: tap", Map.of("x", x, "y", y)) for XCUITest. See the driver documentation for the full list of the gesture commands.

BiDi

  • AppiumDriver does not implement HasBiDi anymore and the getBiDi and maybeGetBiDi methods are removed. Selenium deprecated them for removal and changed the BiDi API in an incompatible way in the recent releases.
  • To use the Selenium BiDi modules (for example LogInspector), add the io.appium:java-client-selenium-bridge artifact and wrap the driver with SeleniumBridge.asRemoteWebDriver(driver). See Selenium interoperability.

Dependencies

  • The runtime classpath of the client is selenium-api, Gson, ByteBuddy, SLF4J API and JSpecify. Guava, OpenTelemetry, selenium-manager, selenium-remote-driver, selenium-http, selenium-json and selenium-os are not brought transitively anymore. Declare the ones your code uses.
  • Pinning Selenium: only selenium-api has to be pinned, see Transitive dependencies management.