Monday, October 12, 2015

Start and Stop Appium Server Programmatically

Pre-requisites:
I assume you have installed the followings in your machine already.
Appium is downloaded (latest from here) and setup properly as mentioned here.
Java Development Kit(JDK) is downloaded(refer here) and installed in your machine.

How to Start or Stop appium server programmatically?
Please refer the below class for detailed usage, Hope it is self explanatory.

Method-1:
package appium.base;

import java.io.File;

import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;

/**
 * This page models Appium server
 * 
 * @author A. K. Sahu
 *
 */
public class AppiumServer {

 String appiumInstallationDir = "C:/Program Files (x86)";// e.g. in Windows
 //String appiumInstallationDir = "/Applications";// e.g. for Mac
 AppiumDriverLocalService service = null;

 public AppiumServer() {
  File classPathRoot = new File(System.getProperty("user.dir"));
  String osName = System.getProperty("os.name");

  if (osName.contains("Windows")) {
   service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder()
     .usingDriverExecutable(new File(appiumInstallationDir + File.separator + "Appium" + File.separator + "node.exe"))
     .withAppiumJS(new File(appiumInstallationDir + File.separator + "Appium" + File.separator
       + "node_modules" + File.separator + "appium" + File.separator + "bin" + File.separator + "appium.js"))
     .withLogFile(new File(new File(classPathRoot, File.separator + "log"), "androidLog.txt")));

  } else if (osName.contains("Mac")) {
   service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder()
     .usingDriverExecutable(new File(appiumInstallationDir + "/Appium.app/Contents/Resources/node/bin/node"))
     .withAppiumJS(new File(
       appiumInstallationDir + "/Appium.app/Contents/Resources/node_modules/appium/bin/appium.js"))
     .withLogFile(new File(new File(classPathRoot, File.separator + "log"), "androidLog.txt")));

  } else {
   // you can add for other OS, just to track added a fail message
   Assert.fail("Starting appium is not supporting the current OS.");
  }
 }

 /**
  * Starts appium server
  */
 public void startAppiumServer() {
  service.start();
 }

 /**
  * Stops appium server
  */
 public void stopAppiumServer() {
  service.stop();
 }
}

Usage of this class:
AppiumServer service = new AppiumServer();

// Starts appium server
service.startAppiumServer();

// ... your tests are here. 

// Stops appium server
service.stopAppiumServer();
Note: You can call start appium server in the @BeforeSuite configuration method and stop appium server at @AfterSuite configuration methods.

With the above approach of starting appium server, you can get total android log in the provided log file i.e. "androidlog.txt" file inside "log" directory of your project root directory

Method-2
Here is an another way of starting and killing appium server programmatically using command line executions with java's ProcessBuilder class. Refer below class for more details.
Define the below properties and change as per your appium installation.
 String appiumInstallationDir = "C:/Program Files (x86)";
 String appiumNode = appiumInstallationDir + File.separator + "Appium" + File.separator + "node.exe";
 String appiumNodeModule = appiumInstallationDir + File.separator + "Appium" + File.separator + "node_modules"
   + File.separator + "appium" + File.separator + "bin" + File.separator + "Appium.js";
 String appiumServicePort = "4723";
Execute the start and stop commands like we do from command line but here with using java's ProcessBuilder class.
 /**
  * Starts appium server
  */
 public void startAppiumServer() {
  executeCommand("\"" + appiumNode + "\" \"" + appiumNodeModule 
                      + "\" " + "--no-reset --local-timezone");
 }

 /**
  * Stops appium server
  */
 public void stopAppiumServer() {
 executeCommand("cmd /c echo off & FOR /F \"usebackq tokens=5\" %a in" 
  + " (`netstat -nao ^| findstr /R /C:\""
  + appiumServicePort + "\"`) do (FOR /F \"usebackq\" %b in"
  + " (`TASKLIST /FI \"PID eq %a\" ^| findstr /I node.exe`) do taskkill /F /PID %a)");
}
You may need the below class for executing a command line command. This you can do using Runtime.getRuntime().exec(command) also, but I would suggest to use ProcssBuilder as this is recommended.
 /**
  * Executes any command for Windows using ProcessBuilder of Java You can
  * change the first input parameter of ProcessBuilder constructor if your OS
  * is not windows operating system
  * 
  * @param aCommand
  */
 public void executeCommand(String aCommand) {
  File currDir = new File(System.getProperty("user.dir"));
  String line;
  try {
   ProcessBuilder probuilder = new ProcessBuilder("CMD", "/C", aCommand);
   probuilder.directory(currDir);
   Process process = probuilder.start();

   BufferedReader inputStream 
        = new BufferedReader(new InputStreamReader(process.getInputStream()));
   BufferedReader errorStream 
        = new BufferedReader(new InputStreamReader(process.getErrorStream()));

   // reading output of the command
   int inputLine = 0;
   while ((line = inputStream.readLine()) != null) {
    if (inputLine == 0) {
     System.out.printf("Output of the running command is: \n");
    }
    System.out.println(line);
    inputLine++;
   }

   // reading errors from the command
   int errLine = 0;
   while ((line = errorStream.readLine()) != null) {
    if (errLine == 0) {
     System.out.println("Error of the command is: \n");
    }
    System.out.println(line);
    errLine++;
   }

  } catch (IOException e) {
   System.err.println("Exception occured: \n");
   System.err.println(e.getMessage());
  }
 }

With the above method, the server log will be printed in the console as I kept standard output and error statements. You can change to trace these log in a log file if you need it.

Hope it is useful !!

Thursday, August 27, 2015

Dealing with input fields in Selenium WebDriver

I got few requests from people saying few of the input fields are not clearing the existing data though we do clear using clear() method in WebDriver and few of the inputs doesn't accept single quote('), double quote("), slash(\), double slash(\\) etc. 
Please have a look on below examples for resolving such issues.
Lets take an example input web element as below :
WebDriver driver = new FirefoxDriver();
WebElement element = driver.findElement(By.cssSelector("#addressInput"));
Clear Input Fields in Selenium WebDriver:
For clearing an input filed you can use either of the following ways:

Case: 1
element.clear();
Case: 2
element.click();
element.clear();
Case: 3
String oldValue = element.getAttribute("value");
if (oldValue.length() > 0) {
 element.clear();
}
Case: 4
element.click();
element.sendKeys(Keys.CONTROL + "a");
element.sendKeys(Keys.BACK_SPACE);
Case: 5
oldValue = element.getAttribute("value");
for (int i = 0; i < oldValue.length() * 2; i++) {
 element.click();
 element.sendKeys(Keys.DELETE);
 element.sendKeys(Keys.CONTROL + "a");
 element.sendKeys(Keys.BACK_SPACE);
 element.click();
 if (element.getAttribute("value").length() == 0) {
  break;
 }
}

Enter Text to Input Fields in Selenium WebDriver:
For entering various types of inputs to an input web element you can use the following approaches:

Case: 1 - Normal Text
element.sendKeys("Some Text");
Case: 2 - Single quote
element.sendKeys("'");// single quote
Sometimes if you try to enter single quote('), it may not enter if you keyboard setting in the system is changes. For example some smart systems enters 2 single quotes on pressing once or sometimes 2 single quotes shown only after pressing 2 times on the single quote key(i.e. on first time press it won't show a single quote). In such cases try using 2 single quotes as your input to input web elements
element.sendKeys("''");
Case: 3 - Double quote
element.sendKeys("\"");// double quote

// similar to above issue for single quote, same applies to double quotes
element.sendKeys("\"\"'"); 
Case: 4 - Single Slash
element.sendKeys("\\");// single slash(\)
Case: 5 - Double Slash
element.sendKeys("\\\\");// double slash(\\) 
Case: 6 - Special Characters
element.sendKeys("!@#$%^&*()_+}?");// special characters
Case: 7 - Scripts
element.sendKeys("<script>alert('Hello');</script>");// script

Hope this helps!

Saturday, July 04, 2015

Drag and Drop tricks in Selenium

Many of the UI draggable elements can be easily draggable from one place to another place over the web page. 
In some cases though the web element is associated with draggable properties, they won't respond when we do drag action through our selenium code by using Actions class. Below are few of the scenario, which will help you to drag and drop your draggable web elements over the web page.

The draggable elements can be anything with draggable properties associated with. It could be a table header cell or can be draggable div or a frame or etc. Lets take an example of a source element(from which you will start the drag action) and a destination element(at which you will drop by releasing your mouse) as below:
WebDriver driver = new FirefoxDriver();

WebElement fromWebElement =
  driver.findElement(By.cssSelector(".ui-draggable div[title='DragMe']"));
WebElement toWebElement =
  driver.findElement(By.cssSelector(".ui-draggable div[title='DropMe']"));
(1) By using Actions class of Selenium webdriver

Case 1: 

Selenium web driver provides Actions class to do drag and drop actions in the web page.
Actions builder = new Actions(driver);
builder.dragAndDrop(fromWebElement, toWebElement);
This will work for most of the draggable web elements.

Case 2:

In case of some of the draggable web element the below works very well:
Actions builder = new Actions(driver);
Action dragAndDrop =
  builder.clickAndHold(fromWebElement).moveToElement(toWebElement)
    .release(toWebElement).build();
dragAndDrop.perform();
Make sure to call the perform() method at the end like above.

Case 3:

In few website the draggable elements doesn't work with either of the above cases. Then you can try the below:
Actions builder = new Actions(driver);
Action dragAndDrop =
  builder.clickAndHold(fromWebElement).moveToElement(toWebElement, 2, 2)
    .release(toWebElement).build();
dragAndDrop.perform();
You can adjust the xOffset and yOffset values(which is 2 in the above example) as per your requirements

Case 4:

This case is very interesting one which will work anyway for all the types of draggable UI elements in the webpage.
Actions builder = new Actions(driver);
builder.clickAndHold(fromWebElement).moveToElement(toWebElement).perform();
Thread.sleep(2000);// add 2 sec wait
builder.release(toWebElement).build().perform();
In the above code the 2 second wait during the drag and drop action is important. I would suggest use this way only if the above cases are not working for you.

(2) By using Robot class

Case 1:

If any of the above drag and drop actions using selenium doesn't work for you, do the drag and drop using Robot class as below:
Point coordinates1 = fromWebElement.getLocation();
Point coordinates2 = toWebElement.getLocation();

Robot robot = new Robot();

robot.mouseMove(coordinates1.getX(), coordinates1.getY());
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseMove(coordinates2.getX(), coordinates2.getY());
robot.mouseRelease(InputEvent.BUTTON1_MASK);
Thread.sleep(2000);// Give some wait if required(e.g. here 2 sec wait)
Hope it helps you! 

Friday, May 08, 2015

Dealing with <object> tags in selenium webdriver

Many web pages uses <object> tags for embedded object within an HTML document. This element is mainly used to embed SVG elements, multimedia, Java applets, Flash etc.
Many web pages also use the <object> tag to embed another webpage or frames into the HTML document.

Though <object> tags looks slimilar to <iframe>, it won't work if you do switch frame by webdriver (e.g. driver.switchTo().frame(objIDorIndex);)

Here are few scenarios that you can handle using selenium webdriver with javascript/jQuery.

Example - 1: 
widthmm=90.48768097536195 heightmm=49.38127063754128 ... ...
Example - 2: 

Content HTML Viewer
(Case 1) In example-1, if you want to click on a "g" elements of SVG element
WebElement objectTag= findElement(By.xpath("//div[@id='imageholder']//object"));
((JavascriptExecutor) driver).executeScript("return (arguments[0].contentDocument.getElementsByTagName('g')[0]).click()", objectTag);
(Case 2) In example-2, if you want to get the title for assertion, then try as an example given below
List<WebElement> objects = findElements(By.xpath("//div[@class='contentViewer']//object"));
String objectId = objects.get(0).getAttribute("id");
WebDriver driver = getDriver();
JavascriptExecutor js = (JavascriptExecutor) driver;
String text = (String) js.executeScript("return (((document.getElementById('" + objectId + "')).contentDocument).getElementsByClassName('titleText')[0]).innerHTML");
getDriver().switchTo().defaultContent();
(Case 3) In example-2, if you want to get any specific attribute, then try something like below
List<WebElement> objects = findElements(By.xpath("//div[@class='contentViewer']//object"));
String objectId = objects.get(0).getAttribute("id");
WebElement objectTag = findElement(By.id(objectId));
String btnTitle = ((JavascriptExecutor) driver).executeScript("return arguments[0].contentDocument.getElementById('closeText').getAttribute('title')", objectTag).toString();
Assert.assertEquals(btnTitle, "Cancel to close");
(Case 4) In example-2, if you want to do any action inside the iFrame, you can use as below given example
WebElement btnDone = (WebElement) js.executeScript("return ((((document.getElementById('" + objectId
            + "')).contentDocument).getElementsByTagName('iFrame')[0]).getElementById('done'))");
btnDone.click();
Note: I have just gave few examples actions w.r.t my example html code snippet. By referring the above examples you do actions inside <object> tag as per your requirements

Sunday, May 03, 2015

Scroll pages in Selenium Webdriver

Most of the cases Selenium automatically calls the javascript "scrollIntoView" function on any element that you try to interact with. If you know an element exists at the bottom of the page then doing anything with that element (including getting an attribute or hovering over it, etc) will cause the page to scroll.

If the above doesn't work, please do either of the followings:

Scroll to a WebElement:
WebElement element = driver.findElement(By.id("navPanel"));
JavascriptExecutor jsexecutor = (JavascriptExecutor) driver;
jsexecutor.executeScript("arguments[0].scrollIntoView();", element);
(Or)
WebElement element = driver.findElement(By.id("navPanel"));
Coordinates coordinate = ((Locatable) element).getCoordinates();
coordinate.onPage();
coordinate.inViewPort();
(Or)
Point point = element.getLocation();
((JavascriptExecutor) driver).executeScript("return window.title;");
Thread.sleep(6000);
((JavascriptExecutor) driver).executeScript("window.scrollBy(0," + (point.getY()) + ");");

Scroll up the web page:
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("scroll(300, 0)"); 
// here x value(horizontal value)'300' can be changed as needed
Scroll down the web page:
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("scroll(0, 300)"); 
// here y value(vertical value) '300' can be changed as needed
Scroll to end/bottom of page:
Actions actions = new Actions(driver);
actions.keyDown(Keys.CONTROL).sendKeys(Keys.END).perform();
(Or)
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;  
jsExecutor.executeScript("window.scrollTo(0,Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,document.documentElement.clientHeight));");
(Or)
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
for (int second = 0;; second++) {
  if(second >=60){
    break;
  }
  jsExecutor.executeScript("window.scrollBy(0,750)", ""); 
  Thread.sleep(3000);
}
//y value(vertical length) '750' can be changed as needed