My Blog List

Sunday, October 30, 2011

PageObjects and PageFactory - Selenium 2.0

Hi all,
Today I am going to speak about an important and well equipped feature of Selenium2.0 PageObjects and PageFactory.
Advantages:
1. Max re-usabality
2. Easy to modify the elements locator strings as it will have only one place for each element
3. Well understandable framework
4. Neatly designed and easy to code.
Page Objects:
Elements on a page or parts of a page called page objects. We can design a class which consists constructors, WebElements including locators, Getters, Setters of some web elements of a page. ie. Services done by part of a page (application).
By using this class, we will instantiate using Page Factories and use the instance to do the services.
So for every element, our full test script contains the locators in one place only.
Likewise, we have design classes for every operations done by particular page and use the instances to do the operations.


Example:
You can build your Page Object of the Common Web Elements (just invented this name :)) - each CWE will represent a "widget" that is used on different pages. In your example this will be a some sort of Date Widget - it contains the Year, Month and a Day. Basically it will be a Page Object

PageFactory requires the string constants to be used in @FindBy annotations.
To resolve this limitation we created our own ElementLocators..




You can use the DateWidget in your test:

....
DateWidget widget = new DateWidget(driver, "yearId", "monthId", "dayId");
....

public void testYearNumeric() {
        widget.setYear("aa");
        widget.submit();
        //Logic to determine Error message shows up

        // ... and day
        widget.setDay("bb");
        widget.submit();
        //Logic to determine Error message shows up
    }

The DateWidget class, which contains custom locators and annotation parsers is:

package pagefactory.test;
import java.lang.reflect.Field;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.Annotations;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

public class DateWidget {

    // These constants are used to identify that they should be changed to the actual IDs
    private static final String YEAR_ID = "$YEAR_ID$";
    private static final String MONTH_ID = "$MONTH_ID$";
    private static final String DAY_ID = "$DAY_ID$";

    // Elements whose ids will be replaced during run-time
    /** Year element */
    @FindBy(id = YEAR_ID)
    private WebElement year;

    /** Month element */
    @FindBy(id = MONTH_ID)
    private WebElement month;

    /** day element */
    @FindBy(id = DAY_ID)
    private WebElement day;

    // The ids of the elements
    /** ID of the year element */
    private String yearId;

    /** ID of the month element */
    private String monthId;

    /** ID of the day element */
    private String dayId;

    public DateWidget(WebDriver driver, String yearId, String monthId,
            String dayId) {
        this.yearId = yearId;
        this.monthId = monthId;
        this.dayId = dayId;

        PageFactory.initElements(new CustomLocatorFactory(driver, 15), this);
    }

    public String getYear() {
        return year.getValue();
    }

    public void setYear(String year) {
        setValue(this.year, year);
    }

    public String getMonth() {
        return month.getValue();
    }

    public void setMonth(String month) {
        setValue(this.month, month);
    }

    public String getDay() {
        return day.getValue();
    }

    public void setDay(String day) {
        setValue(this.day, day);
    }

    public void submit() {
        year.submit();
    }

    private void setValue(WebElement field, String value) {
        field.clear();
        field.sendKeys(value);
    }

    private class CustomLocatorFactory implements ElementLocatorFactory {
        private final int timeOutInSeconds;
        private WebDriver driver;

        public CustomLocatorFactory(WebDriver driver, int timeOutInSeconds) {
            this.driver = driver;
            this.timeOutInSeconds = timeOutInSeconds;
        }

        public ElementLocator createLocator(Field field) {
            return new CustomElementLocator(driver, field, timeOutInSeconds);
        }
    }

    private class CustomElementLocator implements ElementLocator {
        private WebDriver driver;
        private int timeOutInSeconds;
        private final By by;

        public CustomElementLocator(WebDriver driver, Field field,
                int timeOutInSeconds) {
            this.driver = driver;
            this.timeOutInSeconds = timeOutInSeconds;
            CustomAnnotations annotations = new CustomAnnotations(field);
            this.by = annotations.buildBy();
        }

        @Override
        public WebElement findElement() {
            ExpectedCondition<Boolean> e = new ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver d) {
                    d.findElement(by);
                    return Boolean.TRUE;
                }
            };
            Wait<WebDriver> w = new WebDriverWait(driver, timeOutInSeconds);
            w.until(e);

            return driver.findElement(by);
        }
    }

    private class CustomAnnotations extends Annotations {

        public CustomAnnotations(Field field) {
            super(field);
        }

        @Override
        protected By buildByFromShortFindBy(FindBy findBy) {

            if (!"".equals(findBy.id())) {
                String id = findBy.id();
                if (id.contains(YEAR_ID)) {
                    id = id.replace(YEAR_ID, yearId);
                    return By.id(id);
                } else if (id.contains(MONTH_ID)) {
                    id = id.replace(MONTH_ID, monthId);
                    return By.id(id);
                } else if (id.contains(DAY_ID)) {
                    id = id.replace(DAY_ID, dayId);
                    return By.id(id);
                }
            }

            return super.buildByFromShortFindBy(findBy);
        }

    }

}

Wednesday, October 19, 2011

Selenium - Custom ExceptionHandling to Control the script execution


Hi all,


Every time exception throws while running the selenium test, we can't know whats the original place its thrown. So you can write an exception handler by own so that it will throw the type.


Here is the ExceptionHandler.Java file.


package SeleniumUtils;
import java.lang.Exception;


@SuppressWarnings("serial")
public class ExceptionHandler extends Exception { 
 public String functionName;
 public String errorMessage; 

 public ExceptionHandler(String functionName, String errorMessage) {
  this.functionName = functionName;
  this.errorMessage = errorMessage; 
 }

 public String toString(){
        return "\nFunction Name: " +this.functionName+ "\nError Message: " +this.errorMessage+ "\n" ;
 }
}


So, just write your handler in your script like below to control your script.


function abc() {
   if (isElementPresent(locator))
         sel.click()
   else
      throw new ExceptionHandler ("function111", "Error while doing click.")
}

Selenium - LogPicture in ReportNG generated report

Hi all,


Here is the method you can add under your package to log the screenshot in report generated by ReportNG.
Before that, just remove the lines from your class files under your ReportNG JAR.


Under org.uncommons.reportng.templates.html, edit the below files by extracting the JAR. ie.Remove the lines which is doing some replacement for some characters .(line which have shouldEscaped)
1. class-results.html.vm
2. output.html.vm
Then, add the modified file to JAR.


Then, use the below function to log the screenshot. Add this function to TestNG JAR under the Reporter class which already have log method.


//if I am using webdriver instance
public static void logPicture(Webdriver driver, String imagePath, String errorMessage) { 


  final File tmpScreenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
  FileUtils.copyFile(tmpScreenshot, new File(imagePath));


   String s = "<a href=\"file:///" + imagePath + "\">" + errorMessage+ "</a>";
   log(s, getCurrentTestResult());
}


//if I am using defaultSelenium instance
public static void logPicture(Selenium sel, String imagePath, String errorMessage) { 


  sel.captureScreenshot(imagePath); 


   String s = "<a href=\"file:///" + imagePath + "\">" + errorMessage+ "</a>";
   log(s, getCurrentTestResult());
}




So just use the function like below in your selenium script,
Reporter.logPicture("C:\\error1.png", "Error while doing click.")

Pass keys in Selenium Tests


Hi all,

Here is the simple way to pass any KeyBoard keys to selenium. You can use below methods for your required purpose.

sel.keyDownNative(java.awt.event.KeyEvent.VK_SHIFT + "");  //To keep pressing a key
sel.keyPressNative(java.awt.event.KeyEvent.VK_F1 + "");  //To just type a key
sel.keyUpNative(java.awt.event.KeyEvent.VK_SHIFT + ""); //To Release a key being in pressed state

For WebDriver instance, just pass using

driver.sendKeys()

Thursday, October 13, 2011

Sikuli - Handling Dialogs across Platforms

Hi all,


I am writing this post as I came to know this feature is some bit useful for Selenium Users who are running scripts across Windows, Linux and Mac platforms.


It is some difficult to handle dialogs while automating browser applications using selenium. Though its easy to automate those dialogs using native supported EXEs like AutoIt for Windows, dogTail for linux, ATOmac(http://pypi.python.org/pypi/atomac) and AppleScript for Mac, we need a common tool which can do the same operations in all platforms to ease the work.


So we are going to Sikuli
As its supporting all platforms, its not using any API to automate the dialogs and controls rather its using images of controls to do the operations.
It provides IDE to do the scripting on all OSs. Also no need for typing and own scriting. IDE itself provides the flow like


1. Find / Wait / Exists for some image
2. Do KeyBoard or Mouse Operations on that image


Finally you can export or save the script by .sikuli extension. This .sikuli folder contains the images and scripts for the specific test.


Now you can run the specific script from IDE. Also you can run the scripts from commadLine. This way you can call the sikuli scripts from Java Code as part of your selenium test.


java -jar sikuli-script.jar path-to-your-sikuli-script


For more information, visit their website http://www.sikuli.org
Also you can try SQUISH by FrogLogic


Also you can try below apps:
White - Windows Application Automation API (by Thoughtworks)
Frankenstein - Java Swing Application Automation (by Thoughtworks)
SWTBot - Java SWT based application automation (by Eclipse Group)