Table of Contents

Extensions (Static Methods)

Due to the way we structure our framework, we can create static methods that we can use as extensions on methods involving different object types, such as tapping or long-pressing on a MobileElement, swiping from one element to another, explicitly waiting for an AppiumDriver, or waiting for a MobileElement to be displayed or clickable etc.

MobileElement Extensions

We will begin each method name with ‘me’ (for MobileElement).

meWaitForSeconds() Static Method

  1. Open up the ‘MobileElementExtensions’ class in the ‘utils.extensions’ package and add the following static method for making the Driver wait for a specified number of seconds…
    public static WebDriverWait meWaitForSeconds() {
        WebDriverWait wait = new WebDriverWait(appDriver(), sec);
        return wait;
    }
  2. Make sure the import for ‘WebDriverWait’ and the static import for appDriver() method have been added above the class…
    import org.openqa.selenium.support.ui.WebDriverWait;
    
    import static utils.appium.Driver.appDriver;
  3. Add a static integer variable set to a value of 10 for ‘sec’, and make it private to the class it is in, just above the weWaitForSeconds() method…
    private static int sec = 10;

meElementIsDisplayed() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to check that a given MobileElement is displayed, after waiting the specified amount of time from the weWaitForSeconds() method …
    public static boolean meElementIsDisplayed(MobileElement element) {
        meWaitForSeconds().until(ExpectedConditions.visibilityOf(element));
        return element.isDisplayed();
    }
  2. Also make sure the import for ‘ExpectedConditions’ is added above the class, as well as an import for ‘WebElement’…
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import io.appium.java_client.MobileElement;

meElementToBeClickable() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to check that a given MobileElement is clickable, after waiting the specified amount of time from the meWaitForSeconds() method…
    public static boolean meElementToBeClickable(MobileElement element) {
        meWaitForSeconds().until(ExpectedConditions.elementToBeClickable(element));
        return element.isEnabled();
    }

meTap() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to wait for a given MobileElement to be clickable/tappable, highlight the clickable/tappable MobileElement, and then tap the MobileElement (this is better than simply clicking as on mobile the more realistic action would actually be tapping)…
    public static void meTap(MobileElement mobileElement) {
        meElementToBeClickable(mobileElement);
        touchAction.tap(tapOptions().withElement(element(mobileElement))).perform();
    }

    For the above to work, we need to add the following static imports…

    import static io.appium.java_client.touch.TapOptions.tapOptions;
    import static io.appium.java_client.touch.offset.ElementOption.*;

    We also need to make sure we import the TouchAction class…

    import io.appium.java_client.TouchAction;

    We then need to add a a private static TouchAction object in the top of our class (this way we can reuse the TouchAction class in other similar methods)…

    private static TouchAction touchAction = new TouchAction(appDriver());

meLongPress() Static Method

  1. Add the following method for long pressing on a Mobile element for two seconds, and then releasing your finger from it…
    public static void meLongPress(MobileElement mobileElement) {
            meElementToBeClickable(mobileElement);
            touchAction.longPress(longPressOptions().withElement(element(mobileElement))
                    .withDuration(ofSeconds(2))).release().perform();
        }

    For the above to work, we need to add two more static imports for all Duration methods (* is wildcard for all) and LongPressOptions longPressOptions method

    import static io.appium.java_client.touch.LongPressOptions.longPressOptions;
    import static java.time.Duration.*;

meSwipeFromElementToElement() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to check that the elements to swipe to and from are clickable and highlighted, and then press down on one element and swipe it to another element and then release the finger / touch…
    public static void meSwipeFromElementToElement(MobileElement fromMobileElement, MobileElement toMobileElement) {
        meElementToBeClickable(fromMobileElement);
        meElementToBeClickable(toMobileElement);
        touchAction.longPress(longPressOptions().withElement(element(fromMobileElement))
                .withDuration(ofSeconds(2))).moveTo(element(toMobileElement)).release().perform();
    }

meSwipeDirection() Static Methods

  1. Add the following DIRECTION enum to the MobileExtensions class…
    public enum DIRECTION {
        DOWN, UP, LEFT, RIGHT;
    }
  2. Add the following method which uses a switch case with the enum we made to swipe in any given direction…
    public static void meSwipe(DIRECTION direction, long duration) {
        Dimension size = appDriver().manage().window().getSize();
        int startX = 0;
        int endX = 0;
        int startY = 0;
        int endY = 0;
    
        switch (direction) {
            case RIGHT:
                startY = (int) (size.height / 2);
                startX = (int) (size.width * 0.90);
                endX = (int) (size.width * 0.05);
                new TouchAction(appDriver())
                        .press(PointOption.point(startX, startY))
                        .waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
                        .moveTo(PointOption.point(endX, startY))
                        .release()
                        .perform();
                break;
    
            case LEFT:
                startY = (int) (size.height / 2);
                startX = (int) (size.width * 0.05);
                endX = (int) (size.width * 0.90);
                new TouchAction(appDriver())
                        .press(PointOption.point(startX, startY))
                        .waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
                        .moveTo(PointOption.point(endX, startY))
                        .release()
                        .perform();
                break;
    
            case UP:
                endY = (int) (size.height * 0.70);
                startY = (int) (size.height * 0.30);
                startX = (size.width / 2);
                new TouchAction(appDriver())
                        .press(PointOption.point(startX, startY))
                        .waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
                        .moveTo(PointOption.point(endX, startY))
                        .release()
                        .perform();
                break;
    
            case DOWN:
                startY = (int) (size.height * 0.70);
                endY = (int) (size.height * 0.30);
                startX = (size.width / 2);
                new TouchAction(appDriver())
                        .press(PointOption.point(startX, startY))
                        .waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
                        .moveTo(PointOption.point(startX, endY))
                        .release()
                        .perform();
                break;
        }
    }

meSendKeys() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to wait for a given textfield MobileElement to be displayed, clear the textfield if necessary (if ‘clearFirst’ is true), and then input a given String into the textfield…
    public static void meSendKeys(MobileElement element, String text, boolean clearFirst) {
        meElementIsDisplayed(element);
        if (clearFirst) meTap(element);
        element.sendKeys(text);
    }

meElementIsInvisible() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to wait for a given MobileElement to not be visible anymore…
    public static boolean meElementIsInvisible(MobileElement element) {
        meWaitForSeconds().until(ExpectedConditions.invisibilityOf(element));
        return !element.isDisplayed();
    }

meGetAttribute() Static Method

  1. Add the following static method in the ‘MobileElementExtensions’ class to find a specified attribute of a given element…
    public static String meGetAttribute(MobileElement element, String attribute) {
        return element.getAttribute(attribute);
    }

Espresso Specific Mobile Commands

Espresso is an Android test automation library maintained by Google. It has a number of advantages, for example built-in view synchronisation that ensures element finding happens during idle periods in your app.

We can use Espresso framework with our Appium driver when running tests. Espresso contains certain mobile commands / gestures which are specific to the framework. The ‘EspressoExtensions’ class will contain static methods for each of these Espresso specific mobile commands.

First things first, let’s make this ‘EspressoExtensions’ class extend ‘MobileElementExtensions’ class and also add a private static AndroidDriver variable and set it to what’s returned from our ‘appDriver()’ method (making sure we add the correct static import), like below…

package utils.extensions;

import io.appium.java_client.android.AndroidDriver;

import static utils.appium.Driver.appDriver;

public class EspressoExtensions extends MobileElementExtensions {

    private static AndroidDriver driver = appDriver();
}

We will being each method name with ‘es’ (as it is specific to Espresso mobile gestures).

esSwipe() Static Methods

esSwipe(String direction) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class in the ‘utils.extensions’ package to swipe the whole screen in a given direction…
    public static void esSwipe(String direction) {
        Map<String, Object> params = new HashMap<>();
        params.put("direction", direction);
        driver.executeScript("mobile: swipe", params);
    }
  2. Also make sure the following imports are added…
    import org.openqa.selenium.JavascriptExecutor;
    import java.util.HashMap;
    import java.util.Map;

esSwipe(String direction, MobileElement element) Static Method

This is an overloaded version of the above method that also takes a given element that can be swiped on specifically.

  1. Add the following overloaded static method in the ‘EspressoExtensions’ class to swipe a given element in a given direction…
    public static void esSwipe(String direction, MobileElement element) {
        meElementIsDisplayed(element);
        Map<String, Object> params = new HashMap<>();
        params.put("direction", direction);
        params.put("element", element.getId());
        driver.executeScript("mobile: swipe", params);
    }

    Also make sure the following import is added for MobileElement…

    import io.appium.java_client.MobileElement;

esIsToastVisible(String text, boolean isRegEx) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to check whether a toast message containing given text (and whether it is Regular Expression or not) is visible…
    public static void esIsToastVisible(String text, boolean isRegEx) {
        Map<String, Object> params = new HashMap<>();
        params.put("text", text);
        params.put("isRegexp", isRegEx);
        driver.executeScript("mobile: isToastVisible", params);
    }

esOpenDrawer() Static Methods

esOpenDrawer(MobileElement element) Static Method

  1. Add the following overloaded static method in the ‘EspressoExtensions’ class to open the drawer for a given element…
    public static void esOpenDrawer(MobileElement element) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        driver.executeScript("mobile: openDrawer", params);
    }

esOpenDrawer(MobileElement element, int gravity) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to open the drawer for a given element with a given amount of gravity
    public static void esOpenDrawer(MobileElement element, int gravity) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("gravity", gravity);
        driver.executeScript("mobile: openDrawer", params);
    }

esCloseDrawer() Static Methods

esCloseDrawer(MobileElement element) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to close the drawer for a given element…
    public static void esCloseDrawer(MobileElement element) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        driver.executeScript("mobile: closeDrawer", params);
    }

esCloseDrawer(MobileElement element, int gravity) Static Method

  1. Add the following overloaded static method in the ‘EspressoExtensions’ class to close the drawer for a given element with a given amount of gravity
    public static void esCloseDrawer(MobileElement element, int gravity) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("gravity", gravity);
        driver.executeScript("mobile: closeDrawer", params);
    }

esSetDate(MobileElement element, int year, int monthOfYear, int dayOfMonth) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to set the date on a given DatePicker element…
    public static void esSetDate(MobileElement element, int year, int monthOfYear, int dayOfMonth) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("year", year);
        params.put("monthOfYear", monthOfYear);
        params.put("dayOfMonth", dayOfMonth);
        driver.executeScript("mobile: setDate", params);
    }

esSetTime(MobileElement element, int hours, int minutes) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to set the time on a given TimePicker element…
    public static void esSetTime(MobileElement element, int hours, int minutes) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("hours", hours);
        params.put("minutes", minutes);
        driver.executeScript("mobile: setTime", params);
    }

esNavigateTo(MobileElement element, int menuItemId) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to navigate to a given menu item in a given navigation element (the navigation element must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen)…

esScrollToPage() Static Methods

esScrollToPage(MobileElement element, String scrollTo, boolean smoothScroll) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to scroll to a given page (first, last, left or right) smoothly or not, using a given element…
    public static void esScrollToPage(MobileElement element, String scrollTo, boolean smoothScroll) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("scrollTo", scrollTo);
        params.put("smoothScroll", smoothScroll);
        driver.executeScript("mobile: scrollToPage", params);
    }

esScrollToPage(MobileElement element, int ScrollToPage) Static Method

  1. Add the following static method in the ‘EspressoExtensions’ class to scroll to a given page number, using a given element…
    public static void esScrollToPage(MobileElement element, int scrollToPage) {
        Map<String, Object> params = new HashMap<>();
        params.put("element", element);
        params.put("scrollToPage", scrollToPage);
        driver.executeScript("mobile: scrollToPage", params);
    }

Note: The whole list of Espresso mobile commands can be found at http://appium.io/docs/en/commands/mobile-command/#android-espresso-only

WebDriver Extensions

We will begin each method name with ‘wd’ (for WebDriver).

wdHighlight() Static Method

  1. Open up the ‘WebDriverExtensions’ class in the ‘utils.extensions’ package and add the following static method for highlighting a given locator using JavaScript…
    public static Object wdHighlight(By locator) {
        WebDriver driver = appDriver();
        WebElement myLocator = driver.findElement(locator);
        JavascriptExecutor js = (JavascriptExecutor) appDriver();
        return js.executeScript(wdHighlightedColour, myLocator);
    }
  2. You will notice that an error is thrown due to not knowing what ‘wdHighlightedColour’ is, so let’s add that in the ‘Settings’ class of the ‘utils.selenium’ package (of course you can also customise this to change the colour and size of the border etc.)…
    public static String wdHighlightedColour = "arguments[0].style.border='5px solid blue";
  3. Next, go back to the ‘WebDriverExtensions’ class and add a static import for ‘wdHighlightedColour’ in the ‘Settings’ class…
    import static utils.appium.Settings.wdHighlightedColour;
  4. Of course, also check that the imports for ‘WebDriver’, ‘WebElement’, ‘JavaScriptExecutor’ and ‘By’ have been added above the class, as well as a static import again for the browser() method in the ‘Drivers’ class of the ‘utils.selenium’ package…
    import org.openqa.selenium.By;
    import org.openqa.selenium.JavascriptExecutor;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import static utils.appium.Driver.appDriver;

wdElementIsDisplayed() Static Method

  1. Open up the ‘WebDriverExtensions’ class in the ‘utils.extensions’ package and add the following static method for checking that a highlighted locator is displayed…
    public static Object wdElementIsDisplayed(By locator) {
        wdHighlight(locator);
        WebDriverWait wait = new WebDriverWait(appDriver(), sec);
        return wait.until(ExpectedConditions.visibilityOf((WebElement) locator));
    }
  2. Make sure the import for ‘WebDriverWait’ and ‘ExpectedConditions’ have been added above the class…
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import org.openqa.selenium.support.ui.WebDriverWait;
  3. Add a static integer variable set to a value of 10 for ‘sec’, and make it private to the class it is in, just above the weWaitForSeconds() method…
    private static int sec = 10;

wdElementIsInvisible() Static Method

  1. Add the following static method in the ‘WebDriverExtensions’ class to wait for a given locator to not be visible…
    public static void wdElementToDisappear(By locator) {
        WebDriverWait wait = new WebDriverWait(appDriver(), 5);
        wait.until(ExpectedConditions.invisibilityOfElementLocated(locator));
    }

wdFindElement() Static Method

  1. Add the following static method in the ‘WebDriverExtensions’ class to find a displayed element by its locator…
    public static WebElement wdFindElement(By locator) {
        wdElementIsDisplayed(locator);
        return appDriver().findElement(locator);
    }

wdClick() Static Method

  1. Add the following static method in the ‘WebDriverExtensions’ class to click on an element which has been found by its locator…
    public static void wdClick(By locator) {
        wdFindElement(locator).click();
    }

wdSendKeys() Static Method

  1. Add the following static method in the ‘WebDriverExtensions’ class to wait for a given textfield element (which has been found by its locator) to be displayed, clear the textfield if necessary (if ‘clearFirst’ is true), and then input a given String into the textfield…
    public static void wdSendKeys(By locator, String text, boolean clearFirst) {
        if (clearFirst) wdClick(locator);
        wdFindElement(locator).sendKeys(text);
    }

Refactoring our Test Scenario to use the Extensions

We can now use our extensions (static methods) to replace previous lines of code in our test methods.  Over time, this helps us follow DRY principles (Don’t Repeat Yourself) as well as make our code easier to read 🙂

‘skipWelcomeScreen()’ method

  1. Open up the ‘NavigationScenarios.feature’ file, right-click on the ‘And the welcome screen is skipped without logging in’ step, and select ‘Go To’ –> ‘Declaration’
  2. Right-click on the ‘.skipWelcomeScreen()’ method call and select ‘Go To’ –> ‘Declaration’ again
  3. Edit the ‘skipWelcomeScreen()’ method so it uses the meTap() static method from the ‘MobileElementExtensions’ class (you will have to add a static import to access all the static methods in the MobileElementExtensions class)…
    import static utils.extensions.MobileElementExtensions.*;
    public PopularTabPage skipWelcomeScreen() {
        meTap(btnSkipForNow);
        log.info(":: We click the 'Skip For Now' button on the Welcome screen");
        return instanceOf(PopularTabPage.class);
    }

‘navToHomeTab()’ method

  1. Open up the ‘NavigationScenarios.feature’ file again, right-click on the ‘When I view the Home tab’ step, and select ‘Go To’ –> ‘Declaration’
  2. Right-click on the ‘.navToHomeTab()’ method call and select ‘Go To’ –> ‘Declaration’ again
  3. Edit the ‘navToHomeTab()’ method so it uses the meTap() static method from the ‘MobileElementExtensions’ class again (you will have to add a static import to access all the static methods in the MobileElementExtensions class)…
    import static utils.extensions.MobileElementExtensions.*;
    public HomeTabPage navToHomeTab() {
        meTap(homeTab);
        log.info(":: We navigate to the 'Home' tab");
        return instanceOf(HomeTabPage.class);
    }

Running the Test Scenario

Running the Test

  1. Open up ‘NavigationScenarios.feature’ file and make sure you have the ‘@Nexus5xOreo’ tag at the top of the feature file or above each test scenario you want to run
  2. Right-click on the test scenario and run it

It should pass 😀

Digiprove sealCopyright secured by Digiprove © 2018
Liked it? Take a second to support Thomas on Patreon!

Previous Article

Next Article

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.