Appium 2.0 Workshop

WiFi: tgr_guest / Tbartok105113

Appium Philosophy

  • You’re testing the same app you are shipping
  • Write tests in any language and framework you want
  • Wrapped vendor-provided frameworks in WebDriver API (Webdriver Protocol)
  • Open source

Appium 2.0 Evolution & What's New

Appium 2.0

The main differences

  • Addition of Drivers & Plugin Support
  • CLI Support for Drivers and Plugins
  • No more JSONWP. Only W3C protocol.
  • Expose common driver and functionality. Makes it easier for anyone to write new Drivers
  • The core foundational modules have been moved to the 'Appium' monorepo

Appium 2.0 : Architecture

Android Architecture

W3C

Server Request

Server

Client

Appium Server

Driver(espresso)

Server Request

Plugins

iOS Architecture

W3C

Server Request

Client

Appium Server

Driver(xcuitest)

Server Request

Plugins

WDA

Appium 2.0 : Server

Server Installation

npm install -g appium
Not auto-installing any drivers or plugins

Server CLI

-ah APPIUMHOME	The path to the directory where Appium will keep installed drivers, plugins, 
	and any other metadata necessary for its operation

--use-drivers DRIVERS  A comma-separated list of installed driver names that should be active 
for this server. All drivers will be active by default.

--use-plugins PLUGINS  A comma-separated list of installed plugin names that should be active 
for this server. To activate all plugins, you can use the single string "all" as the value (e.g. --plugins=all)

Appium Drivers & Plugins

Driver CLI

appium driver list
appium driver install xcuitest
appium driver install --source=local 
	--package=your-driver /path/to/your/driver
appium driver update --installed
appium driver uninstall xcuitest

Plugin CLI

appium plugin list
appium plugin install images
appium --use-plugins=images,fake
appium plugin update 
appium plugin uninstall images

Appium Server : Advanced

Start Appium server programmatically

AppiumDriverLocalService service = AppiumDriverLocalService
       .buildService(new AppiumServiceBuilder()
             .usingPort(PORT)
             .withArgument(GeneralServerFlag.BASEPATH, "/wd/hub"));
service.start();

.......
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);

Starting Appium Server Programmatically With Plugins

AppiumDriverLocalService service = AppiumDriverLocalService
       .buildService(new AppiumServiceBuilder()
             .usingPort(PORT)
             .withArgument(GeneralServerFlag.BASEPATH, "/wd/hub")
             .withArgument(GeneralServerFlag.PLUGINS, "device-farm"));
service.start();

.......
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);

Platform-wise desired capabilities

Android iOS
skipUnlock usePrebuiltWDA
appWaitPackage derivedDataPath
appWaitActivity useJSONSource
androidInstallTimeout iosInstallPause
disableAndroidWatchers maxTypingFrequency
autoGrantPermission mjpegServerPort
avd wdaLaunchTimeout

Refer:-

https://appium.io/docs/en/writing-running-appium/caps/

https://caps.cloudgrey.io/

Android & iOS Options

UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
                .setDeviceName("Android Emulator")
                .setAutomationName(AutomationName.ANDROID_UIAUTOMATOR2)
                .setApp(System.getProperty("user.dir") + "/VodQA.apk");
          
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);
wait = new WebDriverWait(driver, Duration.ofSeconds(30));          

Gestures

Actions API for gestures

Horizontal Swipe using W3C Action API

x axis

y axis

(x1,y1)

(x2,y1)

Accessibility id: slider

getElementLocation - (X1,Y1)

pointerMove - (X1,Y1)

pointerDown

pointerMove - (X2,Y1)

pointerUp

pause

(0,0)

WebElement slider = driver.findElement(AppiumBy.accessibilityId("slider"));

Point source = slider.getLocation();
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence sequence = new Sequence(finger, 1);
sequence.addAction(finger.createPointerMove(ofMillis(0),
           PointerInput.Origin.viewport(), source.x, source.y));
sequence.addAction(finger.createPointerDown(PointerInput.MouseButton.MIDDLE.asArg()));
sequence.addAction(new Pause(finger, ofMillis(600)));
sequence.addAction(finger.createPointerMove(ofMillis(600),
           PointerInput.Origin.viewport(), source.x + 400, source.y));
sequence.addAction(finger.createPointerUp(PointerInput.MouseButton.MIDDLE.asArg()));
driver.perform(singletonList(sequence));

Horizontal Swipe

Do It Yourself

Try yourself to perform a Drag and Drop operation on Android

iOS specific gestures

Map<String, Object> args = new HashMap<>();
args.put("direction", "up");
driver.executeScript("mobile: swipe", args);
args.put("direction", "down");
driver.executeScript("mobile: swipe", args);

mobile: swipe

Map<String, Object> args = new HashMap<>();
args.put("direction", "down");
driver.executeScript("mobile: scroll", args);
args.put("direction", "up");
driver.executeScript("mobile: scroll", args);

mobile: scroll

Map<String, Object> args = new HashMap<>();
args.put("scale", 5);
driver.executeScript("mobile: pinch", args);

mobile: pinch

iOS specific gestures

Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
args.put("x", 2);
args.put("y", 2);
driver.executeScript("mobile: tap", args);

mobile: tap

// double-tap the screen at a specific point
Map<String, Object> args = new HashMap<>();
args.put("x", 100);
args.put("y", 200);
driver.executeScript("mobile: doubleTap", args);

mobile: doubleTap

// two-finger-tap an element (assume element object already
 exists)
Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
driver.executeScript("mobile: twoFingerTap", args);

mobile: twoFingerTap

iOS specific gestures

// touch and hold an element
Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
args.put("duration", 1.5);
driver.executeScript("mobile: touchAndHold", args);

mobile: touchAndHold

// touch, hold, and drag based on coordinates
Map<String, Object> args = new HashMap<>();
args.put("duration", 1.5);
args.put("fromX", 100);
args.put("fromY", 100);
args.put("toX", 300);
args.put("toY", 600);
driver.executeScript("mobile: dragFromToForDuration", args);

mobile:dragFromToForDuration

Android specific gestures

https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/android-mobile-gestures.md

driver.executeScript("mobile: longClickGesture", ImmutableMap.of(
    "elementId", ((RemoteWebElement) element).getId());

mobile: longClickGesture

driver.executeScript("mobile: doubleClickGesture", ImmutableMap.of(
    "elementId", ((RemoteWebElement) element).getId());

mobile: doubleClickGesture

driver.executeScript("mobile: dragGesture", ImmutableMap.of(
    "elementId", element.getId(),
    "endX", 100,
    "endY", 100
));

mobile: dragGesture

Do It Yourself

Try yourself to perform a Drag & Drop Gesture on Android

Wait Plugin

appium plugin install --source=npm appium-wait-plugin
appium --plugins=element-wait
wait = new WebDriverWait(driver, 30);
wait.until(presenceOfElementLocated(MobileBy.AccessibilityId("login")))
	.click();
wait.until(presenceOfElementLocated(MobileBy.AccessibilityId("slider1")));
driver.findElementByAccessibilityId("login").click();
driver.findElementByAccessibilityId("slider1").click();

Before wait plugin

After wait plugin

Gestures Plugin

MobileElement source = (MobileElement) new WebDriverWait(driver, 30)
    .until(elementToBeClickable(MobileBy.AccessibilityId("dragMe")));
MobileElement destination = (MobileElement) new WebDriverWait(driver, 30)
    .until(elementToBeClickable(MobileBy.AccessibilityId("dropzone")));

driver.addCommand(HttpMethod.POST, 
	String.format("/session/%s/plugin/actions/dragAndDrop", 
    	driver.getSessionId()), "dragAndDrop");
        
driver.execute("dragAndDrop", ImmutableMap.of(
	"sourceId", source.getId(), 
    "destinationId", destination.getId()));

Appium Debugging & Maintenance

Android shell interactions

10057 ◯ : adb shell dumpsys battery
Current Battery Service state:
  AC powered: true
  USB powered: false
  Wireless powered: false
  Max charging current: 0
  Max charging voltage: 0
  Charge counter: 0
  status: 2
  health: 2
  present: true
  level: 100
  scale: 100
  voltage: 0
  temperature: 0
  technology: Li-ion

Get Battery Information

10078 ◯ : adb shell dumpsys cpuinfo
Load: 0.0 / 0.0 / 0.0
CPU usage from 312959ms to 312499ms ago (2018-10-07 10:49:37.267 to 2018-10-07 10:49:37.727):
  151% 1523/system_server: 51% user + 100% kernel / faults: 7233 minor 83 major
  15% 1365/surfaceflinger: 6.6% user + 8.8% kernel / faults: 1 minor
  15% 1375/installd: 2.2% user + 13% kernel / faults: 113 minor
  6.6% 1499/bootanimation: 4.4% user + 2.2% kernel
  4.4% 1358/android.hardware.graphics.composer@2.1-service: 2.2% user + 2.2% kernel
  2.2% 1288/kworker/0:1: 0% user + 2.2% kernel
  2.2% 1298/logd: 0% user + 2.2% kernel / faults: 5 minor
  2.2% 1299/servicemanager: 0% user + 2.2% kernel / faults: 1 minor
  2.2% 1360/android.hardware.sensors@1.0-service: 0% user + 2.2% kernel / faults: 20 minor
50% TOTAL: 13% user + 26% kernel + 10% iowait

Get cpu Information

10079 ◯ : adb shell service list

List all services

Android - Espresso

  • Provides automatic synchronization of test actions with UI
  • Powerful Matchers available
  • Access to App internals - WebViews
File appDir = new File("src/test/java/io/appium/java_client");
File app = new File(appDir, "ApiDemos-debug.apk");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("automationName", AutomationName.ESPRESSO);
capabilities.setCapability("deviceName", "Android Emulator");
capabilities.setCapability("app", app.getAbsolutePath());
driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), 
    capabilities);

Android Espresso Locator Strategy

Data Matcher

String selector = new Json().toJson(ImmutableMap.of(
        "name", "hasEntry",
        "args", ImmutableList.of("title", "Sweep")));

driver.findElement(MobileBy.androidDataMatcher(selector)).click();

http://appium.io/docs/en/writing-running-appium/android/espresso-datamatcher-selector/

iOS Locator Strategy

http://appium.io/docs/en/writing-running-appium/ios/ios-predicate/

https://github.com/facebook/WebDriverAgent/wiki/Predicate-Queries-Construction-Rules

Predicates

driver.findElementByIosNsPredicate("name like 'Answer'")

class chain

driver.findElementByIosClassChain("**/XCUIElementTypeTable
                [`name == "table"`]/XCUIElementTypeCell[`visible == 1`]")

https://github.com/facebook/WebDriverAgent/wiki/Class-Chain-Queries-Construction-Rules

https://github.com/facebook/WebDriverAgent/wiki/How-To-Achieve-The-Best-Lookup-Performance#select-the-most-effective-lookup-strategy

 

Accessibility Identifier

driver.findElementByAccessibilityId("ComputeSumButton")

Appium Advanced usecases

Parallel Test Execution

Android iOS
UDID - Device ID UDID - Real device/SimID
chromedriverport - incase of webviews or chrome wdalocalport - unique port no as WDA defaults to 8100
systemPort - If you are using UIA2 mode, set a different system port for each Appium instance set with systemPort capability since sometimes there can be a port conflict if different ports aren't used, such as in this issue.(https://github.com/appium/appium/issues/7745) deviceName - Simulator Name (sim only)
platformVersion -Simulator OS version (sim only)

Refer: http://appium.io/docs/en/advanced-concepts/parallel-tests/

Device Farm Plugin

appium plugin install --source=npm appium-device-farm
appium --use-plugins=device-farm
  • Automatically detects connected Android, iOS Simulators, and Real devices before session creation and maintains it in the device pool.
  • Dynamically allocates a free device from the device pool while creating a driver session.
  • Updates the device pool with a new device during test execution.
  • Queues the test cases when no free device is available for session creation.
  • Allocates random ports for parallel execution.

Automating Hybrid Apps - Tips & Tricks

Set<String> contextNames = driver.getContextHandles();
for (String contextName : contextNames) {
    System.out.println(contextName); //prints out something like NATIVE_APP \n WEBVIEW_1
}
driver.context(contextNames.toArray()[1]); // set context to WEBVIEW_1

//do some web testing
String myText = driver.findElement(By.cssSelector(".green_button")).click();

driver.context("NATIVE_APP");

Automating Geo Location

driver.setLocation(new Location(49, 123, 10));

SMS Simulation (Android)

driver.sendSMS("555-555-5555", "Your message here!");

Video Recording API

driver.startRecordingScreen();
driver.startRecordingScreen(new AndroidStartScreenRecordingOptions(...));
driver.stopRecordingScreen();
driver.stopRecordingScreen(new BaseStopScreenRecordingOptions(....));

Seeding Photos on Android & iOS Simulators

driver.pushFile("/mnt/sdcard/Pictures/myPhoto.jpg", 
	"/path/to/photo/locally/myPhoto.jpg");

Appium 2.0 for users

What you should do next?

  • ​Read the documentation, understand the drivers and plugins way of working.
  • Update client frameworks to the latest
  • Validate whether they use W3C protocol
  • The appium:automationName capability will be required
  • Test your current scripts with 'appium@next' to make sure they are ready for an upgrade to Appium 2.0
  • Drivers and plugins must be installed

Thank you

Appium 2.0 Workshop

By Srinivasan Sekar

Appium 2.0 Workshop

  • 21