Sunday, 31 March 2013

Advanced captcha automation class

The intent of this blog is to show you an advanced captcha automation class using Selenium RC. So far you must have seen captcha's that only does addition or subtraction. What if in future, multiplication or division captcha's are also introduced. You regression automation script will fail under those circumstances.

This advanced captcha automation class hadles the following operators:

1) Addition (+)
2) Subtration (-)
3) Multiplication (*)
4) Division (/)
5) Modulus (%)

This class is quite easy to understand. Complete code is as follows:


package MyPack;

import java.lang.String;

//This class is responsible for performing the captcha calculations.
public class CalcCaptcha {

int firstWhiteSpaceIndex;
int nextWhiteSpaceIndex; 
int lastWhiteSpaceIndex;

String captchaString;

public CalcCaptcha()
{
firstWhiteSpaceIndex = 0;
nextWhiteSpaceIndex = 0; 
lastWhiteSpaceIndex = 0;
captchaString = "";
}

public CalcCaptcha(String str)
{
captchaString = str;
}

public String CalculateCaptchaResult()
{
firstWhiteSpaceIndex = captchaString.indexOf(" ");

//+1 from firstWhiteSpaceIndex to get operator
//+1 again for white space after operator so +2(1+1)
nextWhiteSpaceIndex = firstWhiteSpaceIndex + 2;
lastWhiteSpaceIndex = captchaString.lastIndexOf(" ");

String leftOperand = captchaString.substring(0, firstWhiteSpaceIndex);
String rightOperand = captchaString.substring((nextWhiteSpaceIndex+1),lastWhiteSpaceIndex);

int leftNumber = Integer.parseInt(leftOperand),
rightNumber    = Integer.parseInt(rightOperand),
result         = 0;

//The operator is always next to first white space, thus +1.
char  operator = captchaString.charAt((firstWhiteSpaceIndex+1));

switch(operator)
{
case '+':
{
result = leftNumber + rightNumber;
break;
}
case '-':
{
//assuming that leftNumber is always greater than rightNumber.
result = leftNumber - rightNumber;
break;
}
case '*':
{
result = leftNumber * rightNumber;
break;
}
case '/':
{
//division by zero exception handled.
result = leftNumber / rightNumber;
break;
}
case '%':
{
result = leftNumber % rightNumber;
break;
}
}
String final_value = String.valueOf(result);
return final_value;
}
}


How to use this class in Selenium RC is shown below:


package MyPack;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;


public class TestCaptcha
{

/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Selenium selenium = null;
try
{
selenium = new DefaultSelenium("localhost",4444,"firefox","http://timesofindia.indiatimes.com");

selenium.start();
selenium.open("/poll.cms");
selenium.windowMaximize();
selenium.windowFocus();

String str = selenium.getText("//*[@id='mathq2']");

CalcCaptcha obj = new CalcCaptcha(str);
String final_value = obj.CalculateCaptchaResult();

selenium.type("//*[@id='mathuserans2']", final_value);
}
catch(RuntimeException obj)
{
//Catch division by zero exception.
System.out.println("Exception raised is: " + obj);
}
//selenium.close();
//selenium.stop(); // RC Window
}
}

Comments are welcome.
Thanks :)



Automate jsf rich face calendar component in Selenium WebDriver

This blog is about automating the rich face calendar component using Selenium WebDriver. This is quite a useful component and is used where ever date needs to be filled in. Sample calendar component is shown below:


To start automating the above shown calendar component, we should know:-
1) Current date (we will fetch what the current date is)

2) Target date to set (specified by you)

3) Once we know the current and target date, we can calculate whether we need to click the month increment element or the month decrement element, shown above.

4) After we reach the correct month, the only task left is to calculate which day index to select. Day index starts at zero in the sample calendar shown above.

Code written to implement the above 4 points is:

Current Date


/*
* This method will fetch current date and updates currentDay, currentMonth & currentYear variables.
*/
static void GetCurrentDateMonthAndYear()
{
Calendar cal = Calendar.getInstance();

currentDay   = cal.get(Calendar.DAY_OF_MONTH);

//+1 because month values starts from 0. January = 0.
                currentMonth = cal.get(Calendar.MONTH)+1;

currentYear  = cal.get(Calendar.YEAR);
}



Target Date

Sample program uses the target date as:


String dateToSet = "15/07/2013";

/*
*This method will bisect the target date and upadates  targetDay, targetMonth & targetYear variables.
*/
static void GetTargetDateMonthAndYear(String dateString)
{
int firstIndex = dateString.indexOf("/");
int lastIndex = dateString.lastIndexOf("/");
String month = dateString.substring(0, firstIndex);
targetDay = Integer.parseInt(month);
String day = dateString.substring(firstIndex+1, lastIndex);
targetMonth = Integer.parseInt(day);
String year = dateString.substring(lastIndex+1, dateString.length());
}




Calculate whether we need to click the month increment element or the month decrement object:


/*
* This method decides how many time this ">" element on the calendar component needs to be clicked to reach target month.
*/
static void CalculateHowManyMonthsToJump()
{
if((targetMonth - currentMonth) > 0 )
{
jumpMonthsBy = (targetMonth - currentMonth);
}
else
{
jumpMonthsBy = (currentMonth - targetMonth);
increment = false;
}
}



After we reach the correct month, the only task left is to calculate which day index to select. Day index starts at zero in the sample calendar shown above.


/*
* This calculates the current index that holds the targetDay value that we need to click in the calendar control.
*/
static void CalculateCorrectDayIndex(WebDriver driver)
{
int tempMonth = targetMonth - 1;
if(tempMonth == 0)
{
//if target month is January, control will come inside.
tempMonth = DECEMBER;
}

WebElement element = driver.findElement(By.xpath(dayZeroxPath));
String str = element.getText();
int dayValueAtZeroIndex = Integer.parseInt(str);

if(dayValueAtZeroIndex == 1)
{
index = targetDay - 1;
}
else
{
switch(tempMonth)
{
case JANUARY:
case MARCH:
case MAY:
case JULY:
case AUGUST:
case OCTOBER:
case DECEMBER:
{
index = ((MAX_DAY_IN_A_MONTH - dayValueAtZeroIndex) + targetDay);
break;
}
case FEBRUARY:
{
/*
* Separate case is needed for feb because Feb contains only 28(MAX_DAY_IN_A_MONTH - 3) days.
*/
index = (((MAX_DAY_IN_A_MONTH - 3) - dayValueAtZeroIndex) + targetDay);
break;
}
default:
{
//Control will come here for months that consists of 30(MAX_DAY_IN_A_MONTH - 1) days.

index = (((MAX_DAY_IN_A_MONTH - 1) - dayValueAtZeroIndex) + targetDay);
break;
}
}
}
}
}//end of class.





COMPLETE PROGRAM CODE :

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

interface Xpaths
{
/*
* This inteface contains all the xpath's that are used in the program
*/
String calendarPopUpButtonxPath = "//*[@id='j_id354:j_id355PopupButton']";
String monthIncrementerxPath = "//*[@id='j_id354:j_id355Header']/table/tbody/tr/td[4]/div";
String monthDecrementerxPath = "//*[@id='j_id354:j_id355Header']/table/tbody/tr/td[2]/div";
String dayxPathPart1 = "//*[@id='j_id354:j_id355DayCell";
String dayxPathPart2 = "']";
String dayZeroxPath = "//*[@id='j_id354:j_id355DayCell0']";
String applyButtonxPath = "//*[@id='j_id354:j_id355Footer']/table/tbody/tr/td[6]/div";
}

interface Months
{
int JANUARY   = 1,
FEBRUARY  = 2,
MARCH     = 3,
APRIL     = 4,
MAY       = 5,
JUNE      = 6,
JULY      = 7,
AUGUST    = 8,
SEPTEMBER = 9,
OCTOBER   = 10,
NOVEMBER  = 11,
DECEMBER  = 12;
int MAX_DAY_IN_A_MONTH = 31;
}

public class AutomateJsfCalendar implements Xpaths,Months
{
/**Months
* @param args
*/
static int targetDay     = 0,
          targetMonth   = 0,
          targetYear    = 0;
static int currentDay    = 0,
  currentMonth  = 0,
      currentYear   = 0;
static int jumpMonthsBy  = 0,
  index         = 0;

static boolean increment = true;
public static void main(String[] args)
{
WebDriver driver = new FirefoxDriver();
driver.navigate().to("http://livedemo.exadel.com/richfaces-demo/richfaces/calendar.jsf");
driver.manage().timeouts().implicitlyWait(15L, TimeUnit.SECONDS);
driver.manage().window().maximize();
WebElement element = driver.findElement(By.xpath(calendarPopUpButtonxPath));
element.click();
//At this point, jsf calendar component is displayed.
//Today's date is 30/03/2013. Let us say we want to set the date as 15/07/2013
String dateToSet = "15/07/2013";

GetTargetDateMonthAndYear(dateToSet);
GetCurrentDateMonthAndYear();
CalculateHowManyMonthsToJump();
for(int i=0;i<jumpMonthsBy;i++)
{
if(increment)
element = driver.findElement(By.xpath(monthIncrementerxPath));
else
element = driver.findElement(By.xpath(monthDecrementerxPath));
element.click();
try
{
//sleep will let you see the automation happening.
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
CalculateCorrectDayIndex(driver);
String completePath = dayxPathPart1 + index + dayxPathPart2; 

element = driver.findElement(By.xpath(completePath));
element.click();
element = driver.findElement(By.xpath(applyButtonxPath));
element.click();

  //driver.quit();
}//end of main..
/*
*This method will bisect the target date and upadates  targetDay, targetMonth & targetYear variables.
*/
static void GetTargetDateMonthAndYear(String dateString)
{
int firstIndex = dateString.indexOf("/");
int lastIndex = dateString.lastIndexOf("/");
String month = dateString.substring(0, firstIndex);
targetDay = Integer.parseInt(month);
String day = dateString.substring(firstIndex+1, lastIndex);
targetMonth = Integer.parseInt(day);
String year = dateString.substring(lastIndex+1, dateString.length());
}
/*
* This method will fetch current date and updates currentDay, currentMonth & currentYear variables.
*/
static void GetCurrentDateMonthAndYear()
{
Calendar cal = Calendar.getInstance();
currentDay   = cal.get(Calendar.DAY_OF_MONTH);
currentMonth = cal.get(Calendar.MONTH)+1;//+1 because month values starts from 0. January = 0.
currentYear  = cal.get(Calendar.YEAR);
}
/*
* This method decides how many time this ">" element on the calendar component needs to be clicked to reach target month.
*/
static void CalculateHowManyMonthsToJump()
{
if((targetMonth - currentMonth) > 0 )
{
jumpMonthsBy = (targetMonth - currentMonth);
}
else
{
jumpMonthsBy = (currentMonth - targetMonth);
increment = false;
}
}

/*
* This calculates the current index that holds the targetDay value that we need to click in the calendar control.
*/
static void CalculateCorrectDayIndex(WebDriver driver)
{
int tempMonth = targetMonth - 1;
if(tempMonth == 0)
{
//if target month is January, control will come inside.
tempMonth = DECEMBER;
}
WebElement element = driver.findElement(By.xpath(dayZeroxPath));
String str = element.getText();
int dayValueAtZeroIndex = Integer.parseInt(str);

if(dayValueAtZeroIndex == 1)
{
index = targetDay - 1;
}
else
{
switch(tempMonth)
{
case JANUARY:
case MARCH:
case MAY:
case JULY:
case AUGUST:
case OCTOBER:
case DECEMBER:
{
index = ((MAX_DAY_IN_A_MONTH - dayValueAtZeroIndex) + targetDay);
break;
}
case FEBRUARY:
{
/*
* Separate case is needed for feb because Feb contains only 28(MAX_DAY_IN_A_MONTH - 3) days.
*/
index = (((MAX_DAY_IN_A_MONTH - 3) - dayValueAtZeroIndex) + targetDay);
break;
}
default:
{
//Control will come here for months that consists of 30(MAX_DAY_IN_A_MONTH - 1) days.
index = (((MAX_DAY_IN_A_MONTH - 1) - dayValueAtZeroIndex) + targetDay);
break;
}
}
}
}
}//end of class.

Simply copy paste, the above code in a java source file having the same name as the class name & run to see the calendar component automation.

In addition to the calendar automation, this article shows:

1) Interface that only contain variables.
2) How the class implements multiple interfaces using the <implements> keyword.
3) How to use switch statements.
4) Modular programming by implementing separate method for different tasks.

 Comments are welcome.
Thanks :)

Friday, 29 March 2013

How to automate the Windows dialogs when you click the browse button on some web page using AutoIt.

I had this challenge of automating the window dialogs that gets prompted when we click on browse button on some web page. Googling for hours, i managed to collect information in bits and pieces. I did not find any example that showed me how to do it, end to end. As a result, i decided to do it myself.

To automate windows dialogs, i have used AutoIt - a powerful scripting language. Few lines from the AutoIt website:


AutoIt v3 is a freeware BASIC-like scripting language designed for automating the Windows GUI and general scripting. It uses a combination of simulated keystrokes, mouse movement and window/control manipulation in order to automate tasks in a way not possible or reliable with other languages (e.g. VBScript and SendKeys). AutoIt is also very small, self-contained and will run on all versions of Windows out-of-the-box with no annoying “runtimes” required!

Following are the steps, that i have taken to achieve this functionality:

Step 1: Downloaded AutoIt setup from here
            There are two important tools that comes as a part of the download. They are: SciTE Script
            Editor and AutoIt Window Info.

            SciTE is the script editor that i used to write the script &
            AutoIt Windows Info is the tool that i used to find the class types of the controls on the
            windows dialog (such as edit box & open button) shown below marked as red.

        
            

Step 2: Wrote the script using the SciTE Script Editor. Following is the script code. It is not very
            clean but does the job efficiently.

; Require variable declaration to increase script maintainability.
Opt("MustDeclareVars", 1);0=no, 1=require pre-declare

Main()

; Avoid global variables by implementing everything in functions
; and using the "Local" keyword.  In this script it doesn't matter (there
; is only one function), but this is a good practice to maintain to make
; your scripts less mysterious.
Func Main()
    ;Local Const $dialogTitle = "Select file(s) to upload by www.imageshack.us"
    Local Const $dialogTitle = $CmdLine[2]
    Local Const $timeout = 5
   ; Printer name should be a command line argument.
    ;Local Const $printerName = "Microsoft XPS Document Writer"
    Local $windowFound = WinWait($dialogTitle, "", $timeout)
   ; For some reason if the print dialog contains the list view (see below)
   ; and the window did not exist before starting WinWait, then the
   ; window returned seems to be something transient that we can't work
   ; with.  Therefore, try to find the window again.  Unclear why this
   ; would be necessary, but this seems peculiar to this type of print
   ; dialog.
  
    ;MsgBox(0, "", $dialogTitle)
    ;MsgBox(0, "", $CmdLine[1])
    ;MsgBox(0, "", $CmdLine[2])
   
  
    $windowFound = WinWait($dialogTitle, "", $timeout)
    Local $windowHandle

    If $windowFound Then
       ; Get the last window used by an AutoIt function, namely the window
       ; we were waiting for.
        $windowHandle = WinGetHandle("[LAST]")
        ;MsgBox(0, "", "Window found.")
       
        ;Now write code to set the filename editable drop down with the filepath which you want to upload.
        ;Local $outputFileName = "C:\Users\Davinder\Desktop\Personal\Davinder.png"
        WinActivate($windowHandle)
        ;Sleep($defaultSleepMs)
        ControlSetText($windowHandle, "", "[CLASS:Edit; INSTANCE:1]", $CmdLine[1])
        ControlClick($windowHandle, "", "[CLASS:Button; TEXT:&Open]")       
       
    Else
        ;ConsoleWrite("Could not find window " & $dialogTitle & @CRLF)
        MsgBox(0, "", "Could not find window.")
        Exit(1)
     EndIf
EndFunc   


Step 3: The script is then compiled to generate the exe file.
Step 4: Wrote a simple java source file and called the exe generated in step 3 from the java source
             file. The source code inside the java files is shown below:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;


public class AutoItAutomation
{
    /**
     * @param args
     */
    public static void main(String[] args)
    {
        // TODO Auto-generated method stub
        String browseButtonXpath = "//*[@id='SWFUpload_0']";
       
        WebDriver driver = new FirefoxDriver();
        driver.navigate().to("http://www.imageshack.us/");

        try
        {
            WebElement element = driver.findElement(By.xpath(browseButtonXpath));
            element.click();
           
            /*
             * First arg  - is the path where you would keep the exe. Modify path as per your requirement.
             * Second arg - is the path of the file that you want to upload. Modify path as per your requirement.
             * Third arg  - is the title of the window / dialg that gets open up when you click on <browse> button. Modify it to the title that you get.
             */
            Process process = new ProcessBuilder("C:/Users/Davinder/Desktop/AutoIt Scripts/automate_windows_dialog.exe",
                               "C:\\Users\\Davinder\\Desktop\\Personal\\Davinder.png", "Select file(s) to upload by www.imageshack.us").start();           
        }
        catch(Exception e)
        {
            System.out.println("No such element found. Exiting smoothly.");
        }
        finally
        {
            //whether or not an exception is thrown, finally block will be executed
            //ensuring that all the resources are released.
           
            //driver.quit();
        }
    }//end of main method.
}

The browse button example that is used in this article for automation purposes is at : http://imageshack.us/

Comments are welcome.
Thanks :)