Swipe or Scroll Helper with Appium for iOS-Android Applications

ScrollSwipe

     We know that swipe or scroll actions are common interaction in mobile applications. One common requirement for testing is to scroll a list until an element is found or some other condition is reached. Scrolling can be achieved in a variety of ways, but the most low-level way of doing so is using the Actions API. In this article, I would like to share some convenience methods for accomplishing swipe or scroll actions. These methods will satisfy,

  • To scroll a list view.
  • To swipe in an arbitrary fashion, for an arbitrary amount of time.
  • To define a swipe in screen-relative terms.
  • Actions should do the same thing on both iOS and Android.

Below, we will start to build out our convenience methods,

Low-Level Swipe

     Everything we want to do (scrolling, swiping) can be conceived of at the root as an action with basically four steps:

  • Move the finger to a location above the screen.
  • Touch the finger to the screen.
  • While touching the screen, move the finger to another location, taking a certain amount of time to do so.
  • Lift the finger off the screen

     Encoding these steps will be our first task, and will result in the method we build on top of for everything else. Below is the implementation,

protected void swipe(Point start, Point end, Duration duration)

     We will call this method swipe, and allow the user to define the start point, end point, and duration.

AppiumDriver<MobileElement> driver = getDriver();
boolean isAndroid = driver instanceof AndroidDriver<?>;

     To perform the swipe, we need the driver instance, which we get, and also give a handy way of referring to whether the driver is iOS or Android. getDriver method you can implement in your Automation base class where you instantiating your driver.

     Next, we begin to construct the action sequence. First of all, we define the pointer input and the sequence object which will represent the swipe. The next two actions are common to both iOS and Android. We initially move the pointer to the start point (taking no explicit time duration to do so) and then lower the pointer to touch the screen,

PointerInput input = new PointerInput(Kind.TOUCH, “finger1”);
Sequence swipe = new Sequence(input, 0);
swipe.addAction(input.createPointerMove(Duration.ZERO, Origin.viewport(), start.x, start.y));
swipe.addAction(input.createPointerDown(MouseButton.LEFT.asArg()));

Below code helps to changes a bit depending on platform,

if (isAndroid) {
duration = duration.dividedBy(ANDROID_SCROLL_DIVISOR);
} else {
swipe.addAction(new Pause(input, duration));
duration = Duration.ZERO;
}
swipe.addAction(input.createPointerMove(duration, Origin.viewport(), end.x, end.y));

     For Android, the actual time taken by the action is always much greater than the amount of time actually specify. This may not be true across the board, but here defined a static variable ANDROID_SCROLL_DIVISOR  to represent this. On iOS, the duration of the pointer move is not actually encoded on the move action at all, it is encoded on a pause action which is inserted before the move action, which is why we are adding that action here if we are using iOS (before then setting the duration to zero for the actual move).

    Finally, lift the finger from the screen, and tell the driver to actually perform the sequence we have encoded. Below is the code snippet to do that action,

swipe.addAction(input.createPointerUp(MouseButton.LEFT.asArg()));
driver.perform(ImmutableList.of(swipe));

Swipe Relatively

The above swipe method is great, but it requires absolute screen coordinates. What if we are running the same test on different devices with different screen dimensions? It would be much better if we could specify our swipe in terms relative to the height and width of the screen. We will see now,

private Dimension getWindowSize() {
if (windowSize == null) {
windowSize = getDriver().manage().window().getSize();
}
return windowSize;
}

protected void swipe(double startXPct, double startYPct, double endXPct, double endYPct, Duration duration) {
Dimension size = getWindowSize();
Point start = new Point((int)(size.width * startXPct), (int)(size.height * startYPct));
Point end = new Point((int)(size.width * endXPct), (int)(size.height * endYPct));
swipe(start, end, duration);
}

     Here we have defined a new version of swipe, that takes start and end values in percentages, not absolute terms. To make this work, we need another helper function that retrieves (and caches) the window size. Using the window size (height and width), we are able to calculate absolute coordinates for the swipe.

Scrolling

     Now we are in a good position to build up the scroll-related methods. The scroll is technically a swipe where we don’t care about one of the dimensions. If scrolling a list down, it means performing a slow upwards swipe action in reality, where the only change in motion that matters is a change in the y-axis. We have 4 directions we can scroll, so we can create an enum to help us define that,

public enum ScrollDirection {
   UP, DOWN, LEFT, RIGHT
}

     We can construct a scroll method that takes one of these directions. It is also helpful to make the scroll amount configurable. We can allow the user to define a short scroll or long scroll in terms relative to the screen height or width. So If we want a scroll amount of 1, that means we should scroll the equivalent of a full screen. 0.5 would mean the equivalent of a half screen. We will see the implementation below,

protected void scroll(ScrollDirection dir, double distance) {
if (distance < 0 || distance > 1) {
throw new Error(“Scroll distance must be between 0 and 1”);
}
Dimension size = getWindowSize();
Point midPoint = new Point((int)(size.width * 0.5), (int)(size.height * 0.5));
int top = midPoint.y – (int)((size.height * distance) * 0.5);
int bottom = midPoint.y + (int)((size.height * distance) * 0.5);
int left = midPoint.x – (int)((size.width * distance) * 0.5);
int right = midPoint.x + (int)((size.width * distance) * 0.5);
if (dir == ScrollDirection.UP) {
swipe(new Point(midPoint.x, top), new Point(midPoint.x, bottom), SCROLL_DUR);
} else if (dir == ScrollDirection.DOWN) {
swipe(new Point(midPoint.x, bottom), new Point(midPoint.x, top), SCROLL_DUR);
} else if (dir == ScrollDirection.LEFT) {
swipe(new Point(left, midPoint.y), new Point(right, midPoint.y), SCROLL_DUR);
} else {
swipe(new Point(right, midPoint.y), new Point(left, midPoint.y), SCROLL_DUR);
}
}

     In the above logic, the start point of a scroll-swipe is going to be half the distance from the mid-point of the screen, along the appropriate axis. With all of these points defined, we can then simply start and end at the appropriate point corresponding to the direction that we want to scroll.

     You can use the above convenient methods with an iOS or Android test automation. Try to use the above helpers in your real automation world and enjoy swipe and scroll actions

Reference: Appium Pro

make it perfect!

 

 

Automation Digital Labs and Desired Capabilities

Automation Digital Labs and Desired Capabilities

    We know that automation testing is one of the important factors in the software testing world. Coverage of automation testing is another critical point, moreover the availability of different real devices, different brands of devices, different versions of devices, and different browsers. Modern DevOps and Quality environments require apps to be tested on hundreds of device/OS/browser combinations. Managing an in-house device-lab is expensive, resource-intensive, restrictive, and very manual. Most of the organizations are utilizing Automation Digital Labs to get all the real devices and browser under a single umbrella.

   A digital lab provides access to real devices and browsers through an automation interface with a guaranteed level of uptime, or service availability, to support Agile development of web and mobile apps. Various vendors are providing Digital Labs in the cloud. The testers and developers can use the cloud digital labs for manual as well as automation testing. In this article, I would like to share short information about various cloud digital labs like Perfecto, SeeTest, Browserstack, Sauce Labs, HeadSpin, Kobiton and TestingBot. Also, I like to share the desired capabilities used to automate and run the applications in such cloud digital labs.

     Desired Capabilities help to configure the Appium server and provide the criteria which we wish to use for running our automation script. For example, we can request the environment (emulator or real-device), which version of the operating system to run the test on, and more. Desired Capabilities are key/value pairs encoded in JSON format and are sent to the Appium Server by the Appium client when a new automation session is requested. Next, we will see the desired capabilities used in various cloud digital labs.

1. Perfecto Cloud

     Perfecto Cloud is a web-based cloud platform that helps to do manual and automation testing of mobile and web applications. Perfecto Cloud provides access to its real-devices in the lab. For that, you need to register an account with Perfecto Cloud. To get security token from your perfecto cloud > Profile > My Security. Following are the important desired capabilities:

String URL = “https:// ” + “YOUR_PERFECTO_CLOUD_URL” + “/nexperience/perfectomobile/wd/hub”;
String SECURITY_TOKEN = “YOUR_SECURITY_TOKEN”;

Desired Capabilities for Web Application

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“securityToken”, SECURITY_TOKEN );
capabilities.setCapability(“platformName”, “Windows”); //You can use MAC as platformName if you are running in MAC system
capabilities.setCapability(“platformVersion”, “10”);
capabilities.setCapability(“browserName”, “Internet Explorer”);
capabilities.setCapability(“browserVersion”, “11”);
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Web

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“securityToken”, SECURITY_TOKEN );
capabilities.setCapability(“platformName”, “Android”); //For iOS, you have to use iOS.
capabilities.setCapability(“udid”, “DEVICE_UDID_FROM_PERFECTO”);// You will get the udid of the device from perfecto cloud.
capabilities.setCapability(“browserName”, “chrome”);//For iOS, you have to use Safari.
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“securityToken”, SECURITY_TOKEN );
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“udid”, “DEVICE_UDID_FROM_PERFECTO”);// You will get the udid of the device from perfecto cloud.
capabilities.setCapability(“app”, “YOUR_APP_LOCATION”);// Application location in perfecto cloud. If you are using application location then below appActivity capabilty not needed.
capabilities.setCapability(“appPackage”, “YOUR_APP_PACKAGE”);// Your application package name
capabilities.setCapability(“appActivity”, “YOUR_APP_ACTIVITY”);//Your application activity name
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“securityToken”, SECURITY_TOKEN );
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“udid”, “DEVICE_UDID_FROM_PERFECTO”);// You will get the udid of the device from perfecto cloud.
capabilities.setCapability(“app”, “YOUR_APP_LOCATION”);// Application location in perfecto cloud. If you are using application location then below bundleId capabilty not needed.
capabilities.setCapability(“autoInstrument”, true);
capabilities.setCapability(“bundleId”, “YOUR_APP_BUNDLEID”);//Your application bundle ID.
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

2. SeeTest Cloud

     Expiritest provides SaaS digital assurance lab and access to hundreds of browsers & mobile devices (physical / emulated) hosted in Experitest data centers, from anywhere at any time. It helps to do automation for both web and mobile applications. You should register in SeeTest Cloud to get the device access and ACCESS KEY. To get ACCESS KEY, go to your SeeTest cloud, click on your profile → Get Access Key. Following are the important desired capabilities:

String URL = “https:// ” + “YOUR_SEETEST_CLOUD_URL” + “/wd/hub”;
String ACCESS_KEY = “YOUR_ACCESS_KEY_FROM_SEETEST_CLOUD”;

Desired Capabilities for Web Application

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“testName”, “Web Test Execution”);
capabilities.setCapability(“accessKey”, ACCESS_KEY);
capabilities.setCapability(CapabilityType.PLATFORM_NAME, “WIN8”);/You can use MAC as platformName if you are running in MAC system.
capabilities.setCapability(CapabilityType.BROWSER_NAME, BrowserType.CHROME);
capabilities.setCapability(CapabilityType.VERSION, “83”);
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Web

DesiredCapabilities capabilities= new DesiredCapabilities();
capabilities.setCapability(“testName”, “Mobile Web Test Execution”);
capabilities.setCapability(“accessKey”, ACCESS_KEY);
capabilities.setCapability(“deviceQuery”, “@os=’android'”);If you are using iOS, then value should be ios for @os.
capabilities.setCapability(“deviceModel”, “samsung”);//Device model will get from SeeTest Cloud.
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “8.0.0”);
capabilities.setCapability(MobileCapabilityType.UDID, “DEVICE_UDID_FROM_SEETEST”);
capabilities.setBrowserName(MobileBrowserType.CHROMIUM);//For iOS, you can use SAFARI
driver = new AndroidDriver<>(new URL(URL), capabilities);//For iOS, you can use IOSDriver instead of AndroidDriver

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities= new DesiredCapabilities();
capabilities.setCapability(“testName”, “Android Mobile Native Test Execution”);
capabilities.setCapability(“accessKey”, ACCESS_KEY);
capabilities.setCapability(“deviceQuery”, “@os=’android'”);
capabilities.setCapability(“deviceModel”, “samsung”);//Device model will get from SeeTest Cloud.
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “8.0.0”);
capabilities.setCapability(MobileCapabilityType.UDID, “DEVICE_UDID_FROM_SEETEST”);
capabilities.setCapability(MobileCapabilityType.APP, “cloud:APP_PACKAGE_NAME/APP_ACTIVITY_NAME”);
capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, “YOUR_APP_PACKAGE”);
capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, “YOUR_APP_ACTIVITY”);
driver = new AndroidDriver<>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities= new DesiredCapabilities();
capabilities.setCapability(“testName”, “iOS Mobile Native Test Execution”);
capabilities.setCapability(“accessKey”, ACCESS_KEY);
capabilities.setCapability(“deviceQuery”, “@os=’ios'”);
capabilities.setCapability(“deviceModel”, “iphone 6”);//Device model will get from SeeTest Cloud.
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “11.0”);
capabilities.setCapability(MobileCapabilityType.UDID, “DEVICE_UDID_FROM_SEETEST”);
capabilities.setCapability(MobileCapabilityType.APP, “cloud:APP_BUNDLEID”);
capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID, “YOUR_APP_BUNDLE_ID”);
driver = new IOSDriver<>(new URL(URL), capabilities);

3. Browserstack

    BrowserStack is a cloud web and mobile testing platform that enables developers and testers to test their websites and mobile applications across on-demand browsers, operating systems, and real mobile devices. Browserstack provides different ways of testing facilities includes LIVE – interactive cross-browser testing, AUTOMATE – for automated web testing, APP LIVE – interactive mobile app testing, and APP AUTOMATE – automate mobile app testing. You have to register with the Browserstack platform. Following are the important desired capabilities:

You will get USERNAME and ACCESS_KEY from https://www.browserstack.com/accounts/settings
String USERNAME = “YOUR_BROWSERSTACK_USERNAME”;
String ACCESS_KEY = “YOUR_BROWSERSTACK_ACCESS_KEY”;
String URL = “https:// ” + USERNAME + “:” + ACCESS_KEY + “@hub-cloud.browserstack.com/wd/hub”;

Desired Capabilities for Web Application

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“os”, “OS X”);//To run on Windows platform, you can use Windows as value for os.
capabilities.setCapability(“os_version”, “Catalina”);//You will get OS version from Browserstack cloud.
capabilities.setCapability(“browser”, “Safari”);
capabilities.setCapability(“browser_version”, “13.0”);
capabilities.setCapability(“browserstack.local”, “false”);
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Web

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“device”, “iPhone XS”);//You will get device details from Browserstack cloud
capabilities.setCapability(“os_version”, “13”);
capabilities.setCapability(“real_mobile”, “true”);
capabilities.setCapability(“browserstack.local”, “false”);
capabilities.setCapability(“browserName”, “Safari”);//In case of Android, you can use Chrome as the browserName value.
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);//For Android, you can use AndroidDriver instead of IOSDriver.

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“device”, “Samsung Galaxy S20”);//You will get device details from Browserstack cloud
capabilities.setCapability(“os_version”, “10.0”);
capabilities.setCapability(“real_mobile”, “true”);
capabilities.setCapability(“app”, “bs://<hashed app-id>”);//You have to upload the app into Browserstack repostory and you will get the hashed app-id. 
capabilities.setCapability(“browserstack.local”, “false”);
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“device”, “iPhone XS”);//You will get device details from Browserstack cloud
capabilities.setCapability(“os_version”, “13”);
capabilities.setCapability(“real_mobile”, “true”);
capabilities.setCapability(“app”, “bs://<hashed app-id>”);//You have to upload the app into Browserstack repostory and you will get the hashed app-id. 
capabilities.setCapability(“browserstack.local”, “false”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

You can find the code snippet to upload the application into Browserstack repository and retrieve the application URL at https://journeyofquality.com/2018/03/24/browserstack-supports-mobile-native-automation/

4. Sauce Labs

     A cloud-based automated testing platform enables developers and testers to perform functional and manual tests with Selenium or Appium on web and mobile apps. It supports real and virtual devices. You have to register with Sauce Labs to get access to the devices and their capabilities. Following are the important desired capabilities:

You will get USERNAME and ACCESS_KEY from https://app.saucelabs.com/user-settings
String USERNAME = “YOUR_SAUCELABS_USERNAME”;
String ACCESS_KEY = “YOUR_SAUCELABS_ACCESS_KEY”;
String URL = “https:// ” + USERNAME + “:” + ACCESS_KEY + “@ondemand.saucelabs.com:443/wd/hub”;

Desired Capabilities for Web Application

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platform”, “macOS 10.15”);//You will get platform details from Sauce Lab cloud.
capabilities.setCapability(“browserName”, “Safari”);
capabilities.setCapability(“version”, “13.0”);
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Web

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“deviceName”, “iPhone XS Simulator”); //You will get device name details from Sauce Lab cloud.
capabilities.setCapability(“platformVersion”, “13.2”);
capabilities.setCapability(“platformName”, “iOS”);//You can use Android if you are running in an Android device.
capabilities.setCapability(“browserName”, “Safari”);//In case of Android, you can use Chrome as value for browserName.
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);//You can use AndroidDriver instead of IOSDriver if you are running on Android device.

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“deviceName”, “Android GoogleAPI Emulator”);//You will get device details from Sauce Lab cloud.
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“platformVersion”, “10.0”);
capabilities.setCapability(“app”, “sauce-storage:my_app.apk”);//You need to upload the Android application .APK file into Sauce Labs repository. 
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“deviceName”, “iPhone XS Simulator”);//You will get device details from Sauce Lab cloud.
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“platformVersion”, “13.2”);
capabilities.setCapability(“app”, “sauce-storage:my_app.zip”);//You need to upload the iOS application into Sauce Labs repository. 
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

5. TestingBot Cloud

    TestingBot has been providing one of the cloud-based Selenium and Appium and has been improving its service ever since. TestingBot providing access to over 2000 browsers and devices that you can instantly start using for Live and Automated testing. You need to register in TestingBot to get access to the TestingBot dashboard, Key, and Secret values. Following are the important desired capabilities:

You will get KEY and SECRET from https://testingbot.com/members/user/edit
String KEY= “YOUR_TESTINGBOT_KEY”;
String SECRET= “YOUR_TESTINGBOT_SECRET”;
String URL = “https:// ” + KEY+ “:” + SECRET+ “@hub.testingbot.com/wd/hub”;

Desired Capabilities for Web Application

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platform”, “WIN10”);//You will get platform details from TestingBot cloud
capabilities.setCapability(“browserName”, “IE”);
capabilities.setCapability(“version”, “11”);
capabilities.setCapability(“name”, “Web Test”);
driver = new RemoteWebDriver(new URL(URL), capabilities);

Desired Capabilities for Mobile Web

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);//You need to use Android in case if you are running the script on Android device.
capabilities.setCapability(“deviceName”, “iPhone 11”);//You will get device details from TestingBot cloud
capabilities.setCapability(“version”, “13.4”);
capabilities.setCapability(“browserName”, “safari”);//In case of Android, you have to use Chrome as browserName.
capabilities.setCapability(“name”, “Mobile Web Test”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);//You have to use AndroidDriver instead of IOSDriver if you are starting session on Android devices.

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“deviceName”, “Galaxy S10”);//You will get the device details from TestingBot cloud
capabilities.setCapability(“version”, “9.0”);
capabilities.setCapability(“app”, “tb://<hashed app-id>”);//You need to upload the APK into TestingBot repository and you will get the hashed_app_id
capabilities.setCapability(“name”, “Mobile Native Android Test”);
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“deviceName”, “iPhone 11”);//You will get the device details from TestingBot cloud
capabilities.setCapability(“version”, “13.4”);
capabilities.setCapability(“app”, “tb://<hashed app-id>”);//You need to upload the iOS app as ZIP format into TestingBot repository and you will get the hashed_app_id
capabilities.setCapability(“name”, “Mobile Native iOS Test”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

6. HeadSpin Device Farm 

       HeadSpin is a service that provides device farm, test automation environments, and monitoring the performance of mobile applications. You need to register in HeadSpin to get access to the device farm and the API token. Following are the important desired capabilities:

String HEADSPIN_APITOKEN = “your-api-token-here”;
String URL = “https://appium-dev.headspin.io/v0/ ” + HEADSPIN_APITOKEN + “/wd/hub”;

Desired Capabilities for Mobile Web – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“automationName”, “UiAutomator2”);
capabilities.setCapability(“deviceName”, “ANDROID_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“udid”, “ANDROID_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“browserName”, “chrome”);
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Web – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“automationName”, “XCUITest”);
capabilities.setCapability(“deviceName”, “IOS_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“udid”, “IOS_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“browserName”, “safari”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“automationName”, “UiAutomator2”);
capabilities.setCapability(“deviceName”, “ANDROID_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“udid”, “ANDROID_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“appPackage”, “YOUR_APP_PACKAGE_NAME”);
capabilities.setCapability(“appActivity”, “YOUR_APP_ACTIVITY_NAME”);
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“automationName”, “XCUITest”);
capabilities.setCapability(“deviceName”, “IOS_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“udid”, “IOS_DEVICE_UDID_FROM_HEADSPIN”);
capabilities.setCapability(“bundleID”, “YOUR_IOS_APP_BUNDLEID”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

7. Kobiton Mobile Device Cloud

      Kobiton enables developers and testers to perform automated and manual testing of mobile apps and websites on real devices. Kobiton allows for instant provisioning of real devices for testing with automated or manual scripts, and also allows current on-premise devices to be plugged in to form a holistic testing cloud. You need to register in Kobiton Cloud to get access to devices and the API key. Following are the important desired capabilities:

You will get USERNAME and API_KEY from https://portal.kobiton.com/settings/keys
String USERNAME = “YOUR_KOBITON_CLOUD_USERNAME”;
String API_KEY = “YOUR_KOBITON_CLOUD_API_KEY”;
String URL = “https:// ” + USERNAME + “:” + API_KEY + “@api.kobiton.com/wd/hub”;

Desired Capabilities for Mobile Web – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“deviceName”, “Galaxy A20”);//You will get device name details from Kobiton device cloud.
capabilities.setCapability(“platformVersion”, “9”);
capabilities.setCapability(“browserName”, “chrome”);
capabilities.setCapability(“deviceGroup”, “KOBITON”);
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Web – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“deviceName”, “iPhone XS”);//You will get device name details from Kobiton device cloud.
capabilities.setCapability(“platformVersion”, “12.4”);
capabilities.setCapability(“browserName”, “safari”);
capabilities.setCapability(“deviceGroup”, “KOBITON”);
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – Android

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “Android”);
capabilities.setCapability(“deviceName”, “Galaxy A20”);//You will get device name details from Kobiton device cloud.
capabilities.setCapability(“platformVersion”, “9”);
capabilities.setCapability(“deviceGroup”, “KOBITON”);
capabilities.setCapability(“app”, ” kobiton-store:<your_app_name>”);//You need to upload the application into Kobiton repository and assign kobiton-store:<your_app_name> value.
driver = new AndroidDriver<WebElement>(new URL(URL), capabilities);

Desired Capabilities for Mobile Native – iOS

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(“platformName”, “iOS”);
capabilities.setCapability(“deviceName”, “iPhone XS”);//You will get device name details from Kobiton device cloud.
capabilities.setCapability(“platformVersion”, “12.4”);
capabilities.setCapability(“deviceGroup”, “KOBITON”);
capabilities.setCapability(“app”, ” kobiton-store:<your_app_name>“);//You need to upload the application into Kobiton repository and assign kobiton-store:<your_app_name> value.
driver = new IOSDriver<WebElement>(new URL(URL), capabilities);

     I hope you got some knowledge of various digital labs and device farm. Try to utilize all the desired capabilities of various digital labs in your automation and enjoy the script execution in the cloud.

make it perfect!

Kickstart Appium with Kotlin

kotappium

     In my previous article, we discussed the Kotlin, its advantages, and how it will work with Selenium. We know that Kotlin is a general-purpose, open-source, statically typed programming language that combines object-oriented and functional programming features.

   In this article, I would like to share how can we use Kotlin programming language for Appium script development and start the Appium server. It also covered the extent report logic written in Kotlin language.

Kickstart Your Appium with Kotlin

Prerequisites

Following are the prerequisites to start Appium with Kotlin,

  • Install Java SDK 8 and above.
  • Install Eclipse or IntelliJ IDEA IDEs.
  • Install the Kotlin plugin in IDEs. Here I am using Eclipse as IDE and Kotlin plugin for Eclipse downloaded from https://marketplace.eclipse.org/content/kotlin-plugin-eclipse
  • The latest version of following maven dependencies:
    • testng
    • selenium-java
    • selenium-server
    • java-client
    • kotlin-test
    • kotlin-stdlib-jdk8
    • extentreports

Step-by-step procedure

Step 1: Create a maven project and add the above dependencies in pom.xml of the project.

Step 2: Create a Kotlin class to keep the logic to initiate the driver and start the Appium server. Let say the class name as AutomationBase. I am defining this class as an abstract class. So it is easy to extend by other classes. I have implemented startApplication, startAppiumServer, getPort, getNodePath, getJSPath, getDriverInstance and setDriverInstance methods in the AutomationBase class. Below are the sample code snippets:

fun startApplication() {
try {
var service: AppiumDriverLocalService = startAppiumServer()
var capabilities = DesiredCapabilities()
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, “One Plus”)
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, “Android”)
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “10”)
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2)
capabilities.setCapability(AndroidMobileCapabilityType.AUTO_GRANT_PERMISSIONS, true)
capabilities.setCapability(“newCommandTimeout”, 180)
capabilities.setCapability(“udid”, “e6916f40”)
capabilities.setCapability(MobileCapabilityType.APP, “YOUR_APP_PATH”)
capabilities.setCapability(MobileCapabilityType.NO_RESET, true)
if(!service.toString().isEmpty()){
driver = AndroidDriver(service.getUrl(), capabilities)
}
} catch (e: Exception) {
e.printStackTrace()
}
}

private fun startAppiumServer(): AppiumDriverLocalService {
var IP_ADDRESS: String = “127.0.0.1”
var bootStrapPort: String
var chromePort: String
var port: Int

if (!File(System.getProperty(“user.dir”) + “\\Logs\\Appium_logs”).exists()) {
(File(System.getProperty(“user.dir”) + “\\Logs”)).mkdir()
(File(System.getProperty(“user.dir”) + “\\Logs\\Appium_logs”)).mkdir()
}
port = getPort()
bootStrapPort = Integer.toString(getPort())
chromePort = Integer.toString(getPort())
var service = AppiumDriverLocalService.buildService(
AppiumServiceBuilder().withAppiumJS(File(getJSPath()))
.usingDriverExecutable(File(getNodePath())).withIPAddress(IP_ADDRESS).usingPort(port)
.withArgument(AndroidServerFlag.BOOTSTRAP_PORT_NUMBER, bootStrapPort)
.withArgument(AndroidServerFlag.CHROME_DRIVER_PORT, chromePort)
.withLogFile(File(System.getProperty(“user.dir”) + “\\Logs\\Appium_logs\\appiumLogs.txt”)))
service.start()
if (service.isRunning()) {
System.out.println(“Server is running…..” + service)
} else {
System.out.println(“Server startup failed…..”)
System.exit(0)
}
return service
}

private fun getPort(): Int {
var port: Int = 0
try {
var socket = ServerSocket(0)
socket.setReuseAddress(true)
port = socket.getLocalPort()
socket.close()
} catch (e: Exception) {
e.printStackTrace()
}
return port
}

private fun getNodePath(): String {
var nodePath: String
var p: Process
var reader: BufferedReader
var command: String
var operatingSystem: String = System.getProperty(“os.name”)
if (operatingSystem.contains(“Win”)) {
command = “where” + ” ” + “node”
} else {
command = “which ” + “node”
}
p = Runtime.getRuntime().exec(command)
reader = BufferedReader(InputStreamReader(p.getInputStream()))
nodePath = reader.readLine()
p.waitFor()
p.destroy()
return nodePath
}

fun getJSPath(): String {
var jsPaths: String
lateinit var actualJSPath: String
var command: String
var operatingSystem: String = System.getProperty(“os.name”)
if (operatingSystem.contains(“Win”)) {
command = “where” + ” ” + “appium”
var p: Process = Runtime.getRuntime().exec(command)
var stdInput: BufferedReader = BufferedReader(InputStreamReader(p.getInputStream()))
jsPaths = stdInput.readLine()
actualJSPath = jsPaths.replace(“appium”, “node_modules\\appium\\build\\lib\\main.js”)
p.waitFor()
p.destroy()
} else {
actualJSPath = “//usr//local//lib//node_modules//appium//build//lib//main.js”
}
return actualJSPath
}

fun getDriverInstance(): WebDriver {
return this.driver
}

fun setDriverInstance(driver: WebDriver) {
this.driver = driver
}

     startApplication method helps to instantiate driver sessions based on the specified capabilities. startAppiumServer method helps to start the Appium server and also create and write server logs to a text file. getPort method helps to create the dynamic network port that supporting the startAppiumServer method. getNodePath method helps to get the node path and it supporting the startAppiumServer method. getJSPath method helps to get the Appium main.js path and it supporting the startAppiumServer method.

Step 3: Create a TestRunner class and it extends AutomationBase class. This class holds TestNG annotations @Listeners, @BeforeClass, and @AfterSuite. Below are the complete code snippets:

@Listeners(AutomationReport::class)
open class TestRunner : AutomationBase() {

@BeforeClass
fun setUp() {
startApplication()
}

@AfterSuite
fun tearDown() {
Thread.sleep(3000)
driver.quit()
}
}

Step 4: Create a sample test class to keep the test cases. Let say SampleTests and it extends TestRunner class. Following are the sample code snippets:

class SampleTests : TestRunner() {

@Test
fun TC001_testEnterPicode() {
SampleTestsHelper.enterPinCode(driver, “682030”)
}

@Test
fun TC002_testSearchFail() {
Assert.fail()
}
}

Step 5: Create a Kotlin class which should act as a helper class of test class. Let say SampleTestsHelper and defined this SampleTestsHelper as object. So Kotlin will create an instance automatically when we invoke the methods of SampleTestsHelper, no need to create a separate instance to invoke the methods of the helper class. Below is the complete code snippets:

object SampleTestsHelper {
/**
* This method used to enter pincode value
*
* @author sanojs
* @since 05-06-2020
*/
fun enterPinCode(driver: WebDriver, pincode: String) {
try {
driver.findElement(By.id(“in.dmart:id/et_activity_pincode_pincode”)).sendKeys(pincod)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 6: Create a Kotlin class to keep the logic to generate an Automation test execution report in HTML format. Let say the class name as AutomationReport. Here I am using extentreports library to generate an HTML report. Also, using ITestListener interface to control the executions and results. Below is the complete code snippet of the AutomationReport class,

class AutomationReport : ITestListener {

public lateinit var sparkReporter: ExtentSparkReporter
public lateinit var extentReport: ExtentReports
public lateinit var extentTest: ExtentTest
/**
* This override method used to create HTML template for the test report
*
* @author sanojs
* @since 05-06-2020
*/
override fun onStart(testContext: ITestContext) {
try {
sparkReporter = ExtentSparkReporter(System.getProperty(“user.dir”) + “/AutomationReport/”)
sparkReporter.config().setDocumentTitle(“Appium Kotlin Automation”)
sparkReporter.config().setReportName(“Automation Execution Report”)
sparkReporter.config().setTheme(com.aventstack.extentreports.reporter.configuration.Theme.DARK)
extentReport = ExtentReports()
extentReport.attachReporter(sparkReporter)
extentReport.setSystemInfo(“Application Name”, “Kotlin Appium Demo”)
extentReport.setSystemInfo(“Platform”, System.getProperty(“os.name”))
extentReport.setSystemInfo(“Environment”, “QA”)

} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to collect current test case name add to the report
*
* @author sanojs
* @since 05-06-2020
*/
override fun onTestStart(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
extentTest = extentReport.createTest(testName)
}
/**
* This override method used to add pass status to the report
*
* @author sanojs
* @since 05-06-2020
*/
override fun onTestSuccess(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.PASS,
MarkupHelper.createLabel(testName + ” Test Case PASSED”, ExtentColor.GREEN)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add fail status to the report
*
* @author sanojs
* @since 05-06-2020
*/
override fun onTestFailure(result: ITestResult) {
var driver: WebDriver
var currentClass = result.getInstance()
var testName: String = result.getMethod().getMethodName()
try {
driver = (currentClass as AutomationBase).getDriverInstance()
var screenshotPath = Utilities().screenshotCapture(driver, result.getName())
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(testName + ” Test Case FAILED”, ExtentColor.RED)
)
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(“Reason for Failure: ” + result.getThrowable().toString(), ExtentColor.RED)
)
extentTest.addScreenCaptureFromPath(screenshotPath)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add skip status to the report
*
* @author sanojs
* @since 05-06-2020
*/
override fun onTestSkipped(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.SKIP,
MarkupHelper.createLabel(testName + ” Test Case SKIPPED”, ExtentColor.ORANGE)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to store HTML report to the specified path and flush extent report instance
*
* @author sanojs
* @since 05-06-2020
*/
override fun onFinish(testContext: ITestContext) {
try {
extentReport.flush()
val dateFormat = SimpleDateFormat(“dd-MMM-yyyy_HH-mm-ss”)
val date = Date()
val filePathdate: String = dateFormat.format(date).toString()
var actualReportPath: String = System.getProperty(“user.dir”) + “/AutomationReport/” + “index.html”
File(actualReportPath).renameTo(
File(
System.getProperty(“user.dir”) + “/AutomationReport/”
+ “Automation_Report_” + filePathdate + “.html”
)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 7: Create another class called Utilities to keep common utility functions required for automation. Here I just added one utility to capture the screenshot. Below is the code snippet to capture the screenshot,

fun screenshotCapture(driver: WebDriver, fileName: String): String {
var destination: String = “”
try {
var scrFile = (driver as TakesScreenshot).getScreenshotAs(OutputType.FILE)
var dateFormat = SimpleDateFormat(“yyyyMMddHHmmss”)
var cal = Calendar.getInstance()
var path = File(“Failure_Screenshots”).getAbsolutePath()
destination = path + “/” + fileName + dateFormat.format(cal.getTime()) + “.png”
scrFile.copyTo(File(destination))
} catch (e: Exception) {
e.printStackTrace()
}
return destination
}

Step 8: Add a testng.xml file into the project structure and map your test classes into the testng.xml to run the test cases. Once execution complete the HTML report will generate inside the AutomationReport folder in the project structure.

Below is the overall project structure of Appium with Kotlin,

Kotlin Appium Structure

     Try to use the above step-by-step procedure in your Appium automation world and enjoy the automation.

make it perfect!

Android Activities and Intents for Appium Engineers

Android Activities and Intents

    The Activity is a core concept of Android applications and fundamentally shapes application architecture and UI design. Therefore, our testing strategy can benefit from understanding and incorporating activities. In this article, I would like to share some concepts of Android Activity and Intents.

     An Android app is a collection of activities that can be entered and left at will. UI designers think of applications in terms of screens, which is a pretty close analog to activities. An activity provides the developer with a window in which to draw the UI, and each screen of an application typically maps to a single activity. As a user moves from one screen to another, the app launches the next activity from the current one. All activities in an Android app must be declared in the app’s AndroidManifest.xml file. Applications launch their own activities the same as they launch activities in other apps, using the startActivity() method.

     Next, we will discuss Intents. Activities are launched via Intents. There are two types of intents: explicit and implicit. An explicit intent uses the name of the app along with the name of the activity to launch the intended activity. Explicit intents are used by developers for moving between activities within a single app. Activities can be launched on connected devices from the command-line using ADB and the am command (short for Activity Manager). The following is an example of launching the default camera app using an explicit intent. We specify the package name of the camera app and the name of the activity,

adb shell am start -n com.android.camera2/com.android.camera.CaptureActivity

     When developers build their activities to be used by other apps, they label their activities so they may be targeted by implicit intents. As the name implies, an implicit intent doesn’t specify a particular app or activity to launch instead it specifies the sort of thing the user would like to do. When an app launches an activity using an implicit intent the Android operating system looks through all the activities of all the apps on the device (using their manifest files) and finds the activities which declared that they respond to the action (and category) specified in the intent. Next, we will launch the camera app using implicit intents, specifying the IMAGE_CAPTURE action,

adb shell am start -a android.media.action.IMAGE_CAPTURE

We can also launch the camera app directly to video capture,

adb shell am start -a android.media.action.VIDEO_CAMERA

    If multiple applications support the same activity, the user is given a choice as to which activity they would like to handle the intent. This decision is probably familiar to all Android users. For example, if you try to set the wallpaper in your device then it will prompt with different sources to choose the image like from Live Wallpapers, Photos, and Wallpapers. You can use below command to trigger that options,

adb shell am start -a android.intent.action.SET_WALLPAPER

    A data URI can also be specified this is the way we can pass the information along with the intent. We will see an example to open a webpage on the device. This is how deep linking is implemented, the deep links are intents which are matched to activities. Below is the command,

adb shell am start -a android.intent.action.VIEW -d https://journeyofquality.com/

The following URI doesn’t start with https like a web URL does, but Google Maps will recognize it and open the given coordinates,

adb shell am start -a android.intent.action.VIEW -d “geo:10.0077154,76.3374621”

Below is an example to sends some optional text which we would like to share,

adb shell am start -a “android.intent.action.SEND” -e “android.intent.extra.TEXT” “welome to journey of quality” -t “text/plain”

      I believe that now you got some ideas about Android Activities, Intents, and have seen how to launch activities and send intents using ADB. In the Appium Automation flow, by default, Appium parses the AndroidManifest.xml file in the .apk file to find the main activity and package name. Appium then launches the app under test using these parameters. If you would like to launch a different activity in your app, you can use the appPackage and appActivity desired capabilities. Intents can also be described using the capabilities: intentAction, intentCategory, optionalIntentArguments, intentFlags.

   optionalIntentArguments are the extra parameters that can be sent with intents, outlined in the last ADB example about sharing text. intentFlags are a complex set of extra commands which can alter the way activities are launched. They mostly have to do with how the newly launched activity relates to a task.

    Activities can also be launched in the middle of test execution, rather than at the beginning of a session. This can be done using the startActivity command,

driver.startActivity(new Activity(“com.example”, “ActivityName”));

     If you have the .apk file, you can find the manifest by opening any android project in Android Studio and choosing Build > Analyze APK from the top navigation bar. Choose your APK file from the file system and then view the AndroidManifest.xml file. All activities and the intents they respond to will be listed in nodes.

    I hope you enjoyed this article and learned about Activities, Intents and some ADB commands. Try to use the above ADB commands in your automation scripts. You can execute your ADB commands using Runtime.getRuntime().exec(“command”). Enjoy your automation!!!

Reference: Appium Pro

make it perfect!

Kickstart Selenium with Kotlin

SelKotlin

    We know that Kotlin is a general-purpose, open-source, statically typed programming language initially designed for the JVM (Java Virtual Machine) and Android that combines object-oriented and functional programming features. It is focused on interoperability, safety, clarity, and tooling support. Kotlin originated at JetBrains and has been open-source since 2012. JetBrains uses Kotlin in many of its products including its flagship IntelliJ IDEA.

    In this article, I would like to explain some advantages of Kotlin and the Selenium script development using Kotlin (also covered the extent report logic written in Kotlin language). 

Advantages of Kotlin

  • Code written in Kotlin works with Java as well as it does natively.
  • Kotlin offers first-class support for functional programming, even though it is mainly object-oriented.
  • Kotlin has a good compiler.
  • Kotlin supports seamless integration with the existing infrastructure. Kotlin’s compatible with all Java frameworks and libraries, and it’s designed to integrate easily with Maven and Gradle build systems.
  • Kotlin provides enhanced run-time performance. 
  • Kotlin is easier and cheaper to maintain. 

Kickstart Your Selenium with Kotlin

Prerequisites

Following are the prerequisites to start Selenium with Kotlin,

  • Install Java SDK 8 and above.
  • Install Eclipse or IntelliJ IDEA IDEs.
  • Install the Kotlin plugin in IDEs. Here I am using Eclipse as IDE and Kotlin plugin for Eclipse downloaded from https://marketplace.eclipse.org/content/kotlin-plugin-eclipse 
  • The latest version of following maven dependencies:
    • testng
    • selenium-java
    • selenium-server
    • kotlin-test
    • kotlin-stdlib-jdk8
    • extentreports 

Step-by-step procedure

Step 1: Create a maven project and add the above dependencies in pom.xml of the project.

Step 2: Create a Kotlin class to keep the logic to initiate the driver. Let say AutomationBase. I am defining this class as an abstract class. So it is easy to extend by other classes. In this class, I have implemented three methods startBrowser, getDriverInstance, and setDriverInstance. Below are the sample code snippets:

public lateinit var driver: WebDriver
fun startBrowser() {
try {
System.setProperty(“webdriver.chrome.driver”, “.//chromedriver.exe”)
driver = ChromeDriver()
driver.manage().window().maximize()
} catch (e: Exception) {
e.printStackTrace()
}
}

fun getDriverInstance(): WebDriver {
return this.driver
}

fun setDriverInstance(driver: WebDriver) {
this.driver = driver
}

     The startBrowser helps to create a driver session, here I used chromedriver to start the session and chromdriver.exe added in the project structure. 

Step 3: Create a TestRunner class and it extends AutomationBase class. This class holds TestNG annotations @Listeners, @BeforeClass, and @AfterSuite. Below are the complete code snippets:

@Listeners(AutomationReport::class)
open class TestRunner : AutomationBase() {

@BeforeClass
fun setUp() {
startBrowser()
}

@AfterSuite
fun tearDown() {
Thread.sleep(3000)
driver.quit()
}
}

Step 4: Create a sample test class to keep the test cases. Let say SampleTests and it extends TestRunner class. Following are the sample code snippets:

class SampleTests : TestRunner() {

@Test
fun TC001_testLoadBrowser() {
var appURL: String = “https://www.google.com/&#8221;
driver.get(appURL)
}

@Test
fun TC002_testSearch() {
SampleTestsHelper.searchValue(driver, “journeyofquality.wordpress.com”)
}

@Test
fun TC003_testSearchFail() {
Assert.fail()
}
}

Step 5: Create a Kotlin class which should act as a helper class of test class. Let say SampleTestsHelper and defined this SampleTestsHelper as object. So Kotlin will create an instance automatically when we invoke the methods of SampleTestsHelper, no need to create a separate instance to invoke the methods of the helper class. Below is the complete code snippets:

object SampleTestsHelper {
/**
* This method used to search value
*
* @author sanojs
* @since 24-05-2020
*/
fun searchValue(driver: WebDriver, valueToSearch: String) {
try {
driver.findElement(By.name(“q”)).sendKeys(valueToSearch)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 6: Create a Kotlin class to keep the logic to generate an Automation test execution report in HTML format. Let say the class name as AutomationReport. Here I am using extentreports library to generate an HTML report. Also, using ITestListener interface to control the executions and results. Below is the complete code snippet of the AutomationReport class,

class AutomationReport : ITestListener {

public lateinit var sparkReporter: ExtentSparkReporter
public lateinit var extentReport: ExtentReports
public lateinit var extentTest: ExtentTest
/**
* This override method used to create HTML template for the test report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onStart(testContext: ITestContext) {
try {
sparkReporter = ExtentSparkReporter(System.getProperty(“user.dir”) + “/AutomationReport/”)
sparkReporter.config().setDocumentTitle(“Selenium Kotlin Automation”)
sparkReporter.config().setReportName(“Automation Execution Report”)
sparkReporter.config().setTheme(com.aventstack.extentreports.reporter.configuration.Theme.DARK)
extentReport = ExtentReports()
extentReport.attachReporter(sparkReporter)
extentReport.setSystemInfo(“Application Name”, “Kotlin Selenium Demo”)
extentReport.setSystemInfo(“Platform”, System.getProperty(“os.name”))
extentReport.setSystemInfo(“Environment”, “QA”)

} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to collect current test case name add to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestStart(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
extentTest = extentReport.createTest(testName)
}
/**
* This override method used to add pass status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestSuccess(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.PASS,
MarkupHelper.createLabel(testName + ” Test Case PASSED”, ExtentColor.GREEN)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add fail status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestFailure(result: ITestResult) {
var driver: WebDriver
var currentClass = result.getInstance()
var testName: String = result.getMethod().getMethodName()
try {
driver = (currentClass as AutomationBase).getDriverInstance()
var screenshotPath = Utilities().screenshotCapture(driver, result.getName())
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(testName + ” Test Case FAILED”, ExtentColor.RED)
)
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(“Reason for Failure: ” + result.getThrowable().toString(), ExtentColor.RED)
)
extentTest.addScreenCaptureFromPath(screenshotPath)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add skip status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestSkipped(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.SKIP,
MarkupHelper.createLabel(testName + ” Test Case SKIPPED”, ExtentColor.ORANGE)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to store HTML report to the specified path and flush extent report instance
*
* @author sanojs
* @since 25-05-2020
*/
override fun onFinish(testContext: ITestContext) {
try {
extentReport.flush()
val dateFormat = SimpleDateFormat(“dd-MMM-yyyy_HH-mm-ss”)
val date = Date()
val filePathdate: String = dateFormat.format(date).toString()
var actualReportPath: String = System.getProperty(“user.dir”) + “/AutomationReport/” + “index.html”
File(actualReportPath).renameTo(
File(
System.getProperty(“user.dir”) + “/AutomationReport/”
+ “Automation_Report_” + filePathdate + “.html”
)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 7: Create another class called Utilities to keep common utility functions required for automation. Here I just added one utility to capture the screenshot. Below is the code snippet to capture the screenshot,

fun screenshotCapture(driver: WebDriver, fileName: String): String {
var destination: String = “”
try {
var scrFile = (driver as TakesScreenshot).getScreenshotAs(OutputType.FILE)
var dateFormat = SimpleDateFormat(“yyyyMMddHHmmss”)
var cal = Calendar.getInstance()
var path = File(“Failure_Screenshots”).getAbsolutePath()
destination = path + “/” + fileName + dateFormat.format(cal.getTime()) + “.png”
scrFile.copyTo(File(destination))
} catch (e: Exception) {
e.printStackTrace()
}
return destination
}

Step 8: Add a testng.xml file into the project structure and map your test classes into the testng.xml to run the test cases. Once execution complete the HTML report will generate inside the AutomationReport folder in the project structure. 

Below is the overall project structure of Selenium with Kotlin,

Selenium with Kotlin Structure

NOTE: 

  • Kotlin Runtime Library will automatically add to the build path of the project when you create a Kotlin class under any package. 
  • Kotlin class extension is .kt
  • The semicolon is not mandatory to end the statements in the class.
  • While inherting a class from a parent class you have to declare the parent class as open using an open keyword or you can declare the parent class as abstract using abstract. You can’t inherit a singleton class. So avoid to extend the class which declared as object in Kotlin.
  • If you declare a class with an object keyword then no need to create an instance, Kotlin will create an instance to access the methods or variables of that class. 

Try to use the above step-by-step procedure in your automation world and enjoy it. 

make it perfect!