package org.edx.nativeapp;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.NetworkConnectionSetting;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.edx.Config;
import org.edx.basetest.UniqueTestId;
import org.edx.utils.PropertyLoader;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.ElementNotVisibleException;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Reporter;
/**
* @author divakarpatil
*
*/
public class NativeAppDriver extends RemoteWebDriver {
protected AppiumDriver appiumDriver;
private final long maxWaitTime = Long.parseLong(PropertyLoader
.loadProperty("max.wait.time").get());
private final long downloadWaitTime = Long.parseLong(PropertyLoader
.loadProperty("download.wait.time").get());
private final String testPackageName = PropertyLoader.loadProperty(
"test.package.name").get();
private final String deviceOS = PropertyLoader.loadProperty("deviceOS")
.get();
private final String appPath = PropertyLoader.loadProperty("appPath").get();
private final String deviceName = PropertyLoader.loadProperty("deviceName")
.get();
private final String appPackage = PropertyLoader.loadProperty("appPackage")
.get();
private final String sauceUserName = PropertyLoader.loadProperty(
"sauceUserName").get();
private String sauceKey = PropertyLoader.loadProperty("sauceKey").get();
URL serverAddress;
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
/**
* Constructor which sets the required desired capabilities for the Appium
* driver
*
* @throws Throwable
*/
public NativeAppDriver() throws Throwable {
super();
if (sauceKey.isEmpty()) {
serverAddress = new URL("http://0.0.0.0:4723/wd/hub");// appium:
// 4723,
// 4475
} else {
serverAddress = new URL("http://" + sauceUserName + ":" + sauceKey
+ "@ondemand.saucelabs.com:80/wd/hub");
}
try {
if (isiOS()) {
desiredCapabilities = Config.iOS.getCapabilities();
appiumDriver = new IOSDriver(serverAddress, desiredCapabilities);
} else if (isAndroid()) {
desiredCapabilities = Config.Android.getCapabilities();
appiumDriver = new AndroidDriver(serverAddress,
desiredCapabilities);
}
} catch (Throwable e) {
System.out.println(">> Error while initiating Driver " + deviceOS);
throw e;
}
}
/**
* Find element by the given locator after proper wait
*/
@Override
public NativeAppElement findElement(final By locator) {
WebElement appElement = null;
try {
appElement = (new WebDriverWait(appiumDriver, maxWaitTime))
.until(ExpectedConditions.presenceOfElementLocated(locator));
} catch (Throwable te) {
Reporter.log("Unable to find the element by locator: "
+ locator.toString() + " within " + maxWaitTime + " secs");
captureScreenshot();
throw new TimeoutException(te);
}
return new NativeAppElement(this, locator, appElement);
}
public boolean verifyElementText(By by, String text) {
boolean result = false;
String actualText = "";
int attempt = 0;
NativeAppElement webElment = findElement(by);
while (!result && attempt <= 5) {
try {
attempt++;
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime / 5, 500);
wait.until(ExpectedConditions.presenceOfElementLocated(by));
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
// Adding support for textarea
if (webElment.getTagName().equalsIgnoreCase("input")
|| webElment.getTagName().equalsIgnoreCase("textarea")) {
actualText = webElment.getAttribute("value");
} else {
actualText = webElment.readInnerText();
}
} catch (Exception e) {
System.err.println("attempt " + attempt + "...");
if (attempt >= 5) {
Reporter.log("Unable to get the text by locator "
+ by.toString());
captureScreenshot();
throw new WebDriverException(e);
}
}
result = actualText.equals(text);
}
if (result) {
return true;
} else {
Reporter.log("Expected: \"" + text + "\" but found: \""
+ actualText + "\"");
captureScreenshot();
throw new AssertionError("Expected: \"" + text + "\" but found: \""
+ actualText + "\"" + "\n"
+ Thread.currentThread().getStackTrace().toString());
}
}
/**
* To check the given text contains by the element located by the locator
*
* @param by
* @param text
* @return
*/
public boolean verifyElementTextContains(By by, String text) {
boolean result = false;
String actualText = "";
int attempt = 0;
NativeAppElement webElment = findElement(by);
while (!result && attempt <= 5) {
try {
attempt++;
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime, 500);
wait.until(ExpectedConditions.presenceOfElementLocated(by));
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
if (webElment.getTagName().equalsIgnoreCase("input")
|| webElment.getTagName().equalsIgnoreCase("textarea")) {
actualText = webElment.getAttribute("value");
} else {
actualText = webElment.readInnerText();
}
} catch (Exception e) {
System.err.println("attempt " + attempt + "...");
if (attempt > 5) {
Reporter.log("Unable to get the text by locator "
+ by.toString());
captureScreenshot();
throw new WebDriverException(e);
}
}
result = actualText.contains(text);
}
if (result) {
return true;
} else {
Reporter.log("\"" + text + "\" not found in \"" + actualText + "\"");
captureScreenshot();
throw new AssertionError("\"" + text + "\" not found in \""
+ actualText + "\"" + "\n"
+ Thread.currentThread().getStackTrace().toString());
}
}
/**
* Captures the screenshot
*/
public void captureScreenshot() {
String outputPath = PropertyLoader.loadProperty("output.path").get();
String screenShotPath = outputPath + "/ScreenShots/";
String fileName = generateFileName() + ".jpg";
// Take the screenshot
File scrFile = ((TakesScreenshot) (this.appiumDriver))
.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(scrFile, new File(screenShotPath + fileName));
Reporter.log("<br> Module name: " + getCurrentTestClassName()
+ "<br>");
Reporter.log(" Refer to <a href=\"ScreenShots/" + fileName
+ "\" target = \"_blank\"><img src=\"ScreenShots/"
+ fileName + "\" width=\"50\" height=\"50\"></a><br>");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Generates the filename for screenshot
*
* @return
*/
public String generateFileName() {
return new UniqueTestId().id + "_" + getCurrentTestClassName() + "_"
+ getCurrentTestMethodName() + "_"
+ getCurrentTestMethodLineNumber();
}
/**
* This will return the current test method name it is executing
*
* @return String Test case name
*/
public String getCurrentTestMethodName() {
int stackIndex = 0;
while (stackIndex < Thread.currentThread().getStackTrace().length) {
if (Thread.currentThread().getStackTrace()[stackIndex]
.getMethodName().endsWith("Test")
&& Thread.currentThread().getStackTrace()[stackIndex]
.getClassName().startsWith(testPackageName)) {
break;
}
stackIndex++;
}
return Thread.currentThread().getStackTrace()[stackIndex]
.getMethodName();
}
/**
* This will return the current test class name it is executing
*
* @return String test class name
*/
public String getCurrentTestClassName() {
int stackIndex = 0;
while (stackIndex < Thread.currentThread().getStackTrace().length) {
if (Thread.currentThread().getStackTrace()[stackIndex]
.getMethodName().endsWith("Test")
&& Thread.currentThread().getStackTrace()[stackIndex]
.getClassName().startsWith(testPackageName)) {
break;
}
stackIndex++;
}
return Thread.currentThread().getStackTrace()[stackIndex]
.getClassName();
}
/**
* This will return the current test method line number it is executing
*
* @return int: Test method line number
*/
public int getCurrentTestMethodLineNumber() {
int stackIndex = 0;
while (stackIndex < Thread.currentThread().getStackTrace().length) {
if (Thread.currentThread().getStackTrace()[stackIndex]
.getMethodName().endsWith("Test")
&& Thread.currentThread().getStackTrace()[stackIndex]
.getClassName().startsWith(testPackageName)) {
break;
}
stackIndex++;
}
return Thread.currentThread().getStackTrace()[stackIndex]
.getLineNumber();
}
/**
* AppiumDriver close
*/
@Override
public void close() {
appiumDriver.quit();
}
/**
* Find element by ID
*
* @param id
* @return
*/
public NativeAppElement findElementById(String id) {
return findElement(By.id(id));
}
/**
* Find element by Name
*
* @param name
* @return
*/
public NativeAppElement findElementByName(String name) {
return findElement(By.name(name));
}
/**
* Find element by Xpath
*
* @param xpath
* @return
*/
public NativeAppElement findElementByXpath(String xpath) {
return findElement(By.xpath(xpath));
}
/**
* Find element by Class name
*
* @param className
* @return
*/
public NativeAppElement findElementByClassName(String className) {
return findElement(By.className(className));
}
public boolean verifyElementTextByName(String name, String text) {
return this.verifyElementText(By.name(name), text);
}
public boolean verifyElementTextById(String id, String text) {
return this.verifyElementText(By.id(id), text);
}
public boolean verifyElementNotPresntById(String id) {
boolean found = false;
try {
appiumDriver.findElementById(id);
found = true;
Reporter.log("Element is present with id " + id);
throw new AssertionError("Element is present with id " + id);
} catch (Exception e) {
}
return found;
}
public boolean verifyElementNotPresntByName(String name) {
boolean found = false;
try {
this.findElementByName(name);
found = true;
} catch (Exception e) {
}
return found;
}
/**
* Overridden webDriver find Elements with proper wait.
*/
@Override
public List<WebElement> findElements(By locator) {
try {
(new WebDriverWait(appiumDriver, maxWaitTime))
.until(ExpectedConditions
.presenceOfAllElementsLocatedBy(locator));
} catch (ElementNotVisibleException e) {
Reporter.log("Element not found: " + locator.toString());
captureScreenshot();
throw e;
}
return appiumDriver.findElements(locator);
}
@Override
public List<WebElement> findElementsById(String using) {
return findElements(By.id(using));
}
public void clickElementWithIndexById(String id, int index) {
// List<NativeAppElement> elements = findAllElements(By.id(id));
try {
findElementsById(id).get(index).click();
// elements.get(index - 1).click();
} catch (Exception exp) {
captureScreenshot();
Reporter.log("Unable to click element index: " + index
+ " with id: " + id);
throw exp;
}
}
public void clickElement(WebElement element) {
element.click();
}
public void clickElement(NativeAppElement element) {
((JavascriptExecutor) appiumDriver).executeScript(
"arguments[0].scrollIntoView();", element.getWebElement());
element.getWebElement().click();
}
public void clickElementById(String id) {
findElementById(id).click();
}
public void clickElementByXpath(String xpath) {
findElementByXpath(xpath).click();
}
public void clickElementByName(String name) {
findElementByName(name).click();
}
public void enterTextToElementById(String id, String text) {
findElementById(id).sendKeys(text);
}
public void enterTextToElementByName(String name, String text) {
findElementByName(name).sendKeys(text);
}
public void enterTextToElementByClassname(String className, String text) {
findElementByClassName(className).sendKeys(text);
}
public void verifyElementPresentByName(String name) {
findElementByName(name);
}
public void verifyElementPresentByXpath(String name) {
findElementByXpath(name);
}
public void verifyElementPresentById(String id) {
findElementById(id);
}
public void verifyElementPresentByClassName(String className) {
findElementByClassName(className);
}
public void verifyPresenceOfTextLocatedById(String id, String text) {
// findElementById(id);
try {
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime, 500);
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.id(id), text));
} catch (Throwable t) {
Reporter.log("text " + text + " not present in id " + id);
captureScreenshot();
throw t;
}
}
public void verifyPresenceOfTextLocatedByName(String name, String text) {
// findElementByName(name);
try {
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime, 500);
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.name(name), text));
} catch (Throwable t) {
Reporter.log("text " + text + " not present in name " + name);
captureScreenshot();
throw t;
}
}
/**
* Accept the alert
*/
public void acceptAlert() {
WebDriverWait wait = (WebDriverWait) new WebDriverWait(appiumDriver,
maxWaitTime, 500);
wait.until(ExpectedConditions.alertIsPresent());
Alert alrt = appiumDriver.switchTo().alert();
alrt.accept();
}
/**
* Verify the element is visible
*
* @param element
*/
public void verifyVisibilityOfElement(WebElement element) {
try {
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime, 500);
wait.until(ExpectedConditions.visibilityOf(element));
} catch (Throwable t) {
captureScreenshot();
throw t;
}
}
/**
* Verifies element is visible
*
* @param id
*/
public void verifyVisibilityOfElementLocatedById(String id) {
try {
WebDriverWait wait = (WebDriverWait) new WebDriverWait(
appiumDriver, maxWaitTime, 500);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(id)));
} catch (Throwable t) {
Reporter.log("Elment by Id " + id + " not visible");
captureScreenshot();
throw t;
}
}
public void clearInputById(String id) {
findElementById(id).clear();
}
public void clearInputByName(String name) {
findElementByName(name).clear();
}
public void clearInputByClassname(String className) {
findElementByClassName(className).clear();
}
/**
* Method returns the appium driver instance
*
* @return
*/
public WebDriver getDriver() {
return appiumDriver;
}
/**
* Switch between data and wifi(specific to android).
*
* @param wifi
* @param data
* @throws InterruptedException
*/
public void setNetworkConnection(boolean wifi, boolean data,
boolean airplane) throws InterruptedException {
if (deviceOS.equalsIgnoreCase("android")) {
NetworkConnectionSetting connectionSetting = new NetworkConnectionSetting(
0);
connectionSetting.setWifi(wifi);
connectionSetting.setData(data);
connectionSetting.setData(airplane);
((AndroidDriver) appiumDriver)
.setNetworkConnection(connectionSetting);
connectionSetting = ((AndroidDriver) appiumDriver)
.getNetworkConnection();
} else {
HashMap<String, Double> coords = new HashMap<String, Double>();
JavascriptExecutor js = (JavascriptExecutor) appiumDriver;
Dimension devicediam = appiumDriver.manage().window().getSize();
int height = devicediam.getHeight();
// int width = devicediam.getWidth();
if (deviceName.equals("iPhone 6")) {
appiumDriver.swipe(100, height, 100, 100, 500);
Thread.sleep(3 * 1000);
coords.put("x", new Double("" + 120));
coords.put("y", new Double("" + 290));
coords.put("duration", 0.5);
js.executeScript("mobile: tap", coords);
Thread.sleep(3 * 1000);
coords.put("x", new Double("" + 100));
coords.put("y", new Double("" + 100));
coords.put("duration", 0.5);
js.executeScript("mobile: tap", coords);
} else {// For iPhone 5
appiumDriver.swipe(100, height, 100, 100, 500);
Thread.sleep(3 * 1000);
coords.put("x", new Double("" + 100));
coords.put("y", new Double("" + 180));
coords.put("duration", 0.5);
js.executeScript("mobile: tap", coords);
Thread.sleep(3 * 1000);
coords.put("x", new Double("" + 100));
coords.put("y", new Double("" + 100));
coords.put("duration", 0.5);
js.executeScript("mobile: tap", coords);
}
}
}
/**
* Navigating back
*/
public void back() {
appiumDriver.navigate().back();
}
/**
* Launch the app
*/
public void launchApp() {
appiumDriver.closeApp();
appiumDriver.launchApp();
}
/**
* Remove app(Uninstall)
*/
public void uninstallApp() {
appiumDriver.removeApp(appPackage);
}
/**
* Wait till the element is displayed and swipe till the particular time
* slot
*
* @param id
* - id of the element
*/
public void swipe(String id) {
try {
if (isAndroid()) {
new WebDriverWait(appiumDriver, 20).until(ExpectedConditions
.presenceOfElementLocated(By.id(id)));
Dimension dimension = appiumDriver.manage().window().getSize();
int ht = dimension.height;
int width = dimension.width;
appiumDriver.swipe((width / 2), (ht / 4), (width / 2),
(ht / 2), 1000);
} else {
new WebDriverWait(appiumDriver, 20).until(ExpectedConditions
.presenceOfElementLocated(By.id(id)));
if (deviceName.equalsIgnoreCase("iphone 5")) {
appiumDriver.swipe((int) 0.1, 557, 211, 206, 500);
} else if (deviceName.equalsIgnoreCase("iphone 6")) {
appiumDriver.swipe((int) 0.1, 660, 50, 50, 500);
}
}
} catch (Throwable e) {
Reporter.log("Element by Id " + id + " not visible");
captureScreenshot();
throw e;
}
}
/**
* Hide Keyboard (specific for android)
*/
public void hideKeyboard() {
// if (sauceKey.equals("")) {
appiumDriver.hideKeyboard();
// }
}
/**
* verifying that text is present or not
*
* @param id
* - id of the element whose text is to be checked for.
* @param text
* - text use for comparison.
* @return
*/
public boolean verifyElementText(String id, String text) {
try {
(new WebDriverWait(appiumDriver, 20)).until(ExpectedConditions
.presenceOfElementLocated(By.id(id)));
} catch (Exception e) {
Reporter.log("Element not found: " + e.toString());
captureScreenshot();
throw e;
}
try {
if (!(appiumDriver.findElementById(id).getText()).equals(text)) {
return true;
} else {
return false;
}
} catch (Exception e) {
Reporter.log("Element not found: " + e.toString());
captureScreenshot();
throw e;
}
}
/**
* Inserting wait till the element to be found is present.
*
* @param id
* : id of the element.
*/
public void insertWait(String id) {
try {
new WebDriverWait(appiumDriver, downloadWaitTime)
.until(ExpectedConditions.invisibilityOfElementLocated(By
.id(id)));
} catch (Throwable e) {
Reporter.log("Element found: " + e.toString());
captureScreenshot();
throw e;
}
}
/**
* Scroll the list till item found by name
*
* @param name
* - name of the element till the scroll is to be performed
* @throws Throwable
*/
public void scrollList(String name) throws Throwable {
int i = 0;
Dimension dimension = appiumDriver.manage().window().getSize();
int ht = dimension.height;
int width = dimension.width;
if (isAndroid()) {
while (!verifyElementIsDisplayedById(name)) {
appiumDriver.swipe((width / 2), (ht / 2), (width / 4),
(ht / 4), 500);
}
} else {
try {
if (appPath.contains(".ipa")) {
while (!(verifyElementIsDisplayedById(name))) {
appiumDriver.swipe((width / 2), (ht / 2), (width / 4),
(ht / 4), 500);
i++;
if (i > 20) {
throw new Exception();
}
}
}
} catch (Throwable e) {
Reporter.log("Element not found by name:" + name);
throw e;
}
}
}
public boolean verifyElementIsDisplayedById(String id) {
boolean found = false;
try {
found = appiumDriver.findElementById(id).isDisplayed();
} catch (Exception e) {
}
return found;
}
/**
* Tap on wifi(only for ios)
*/
public void tapOnWifi() {
System.out.println("Size of iphone 6 is "
+ appiumDriver.manage().window().getSize());
if (deviceName.equalsIgnoreCase("iphone 5")) {
appiumDriver.tap(85, 85, 175, 0);
} else if (deviceName.equalsIgnoreCase("iphone 6")) {
appiumDriver.tap(120, 120, 278, 0);
}
}
/**
* Verify that whether the text is present or not
*
* @param courseWareErrorText
* @param text
* @return
*/
public boolean textIsPresent(String courseWareErrorText, String text) {
if (courseWareErrorText.equals(text)) {
return true;
} else {
return false;
}
}
/**
* Verify that whether the element is present or not
*
* @param id
* @return
*/
public boolean verifyElementId(String id) {
boolean found = false;
try {
appiumDriver.findElementById(id);
found = true;
} catch (Exception e) {
}
return found;
}
private boolean isAndroid() {
return deviceOS.equalsIgnoreCase(Config.Android.OS_NAME);
}
private boolean isiOS() {
return deviceOS.equalsIgnoreCase(Config.iOS.OS_NAME);
}
}