Sunday, 2 December 2012

Mapping web elements

      Guess everyone earlier or later starts thinking about some generic solution for Page Object classes. You may have 50 different elements on your form, and, I hope, you won't create method for each.
What you have to do to initiate mapping?
  • to my regret, define 50 locators 
  • create a "view" class with all bloody fields mentions ( why I call it view class? generally because it regards an html view )
  • put all matching pairs to a map
I advise use of Map<Sting, By> rather than Map<String, WebElement>. Even though you defined your web-elements through page factory, by fact they are getting initiated while populating a map. Ans that slows down iteraction with page due to each elements gets focus.

Negative side is that those elements may be different, or even futhermore, custom - non standard control elements. Then you have to extend "have to" list with elements object layer.

Friday, 23 November 2012

Get type of control element by WebElement

I reckon that is useful class:
it gets WebElement and returns the type of html control corresponding to it. Implemented using W3C, some tags are absent, yet you're free to extend

public class ControlElement {
  private final Type type;
  
  public enum Type {
    BUTTON, RADIO, TEXT_AREA, SELECT, OPTION, TEXT, LINK, CHECKBOX
  }

  private static final String BUTTON = "button";
  private static final String RADIO = "radio";
  private static final String TEXT_AREA = "textarea";
  private static final String CHECKBOX = "checkbox";
  private static final String SELECT = "select";
  private static final String OPTION = "option";
  private static final String TEXT = "text";
  private static final String LINK = "a";
  
  private static final String NULL = "null";
  private static final String INPUT = "input";
  private static final String PASSWORD = "input";
  private static final String SUBMIT = "submit";
  private static final String HIDDEN = "hidden";
  private static final String RESET = "reset";
  private static final String SELECT_ONE = "select-one";

  private final Map<Entry<String, String>, Type> map =
      new HashMap<Entry<String, String>, Type>();

  public ControlElement(WebElement element) {
    initMaping();
    String tagName = element.getTagName().toLowerCase();
    String typeAttribute = element.getAttribute("type");
    Entry<String, String> key = new AbstractMap.SimpleEntry<String, String>(tagName, typeAttribute);

    if (map.containsKey(key)) {
      type = map.get(key);
    } else {
      type = null;
    }
  }

  private void initMaping() {
    // link
    map.put(new AbstractMap.SimpleEntry<String, String>(LINK, NULL), Type.LINK);
    // button
    map.put(new AbstractMap.SimpleEntry<String, String>(BUTTON, NULL), Type.BUTTON);
    map.put(new AbstractMap.SimpleEntry<String, String>(BUTTON, SUBMIT), Type.BUTTON);
    map.put(new AbstractMap.SimpleEntry<String, String>(BUTTON, RESET), Type.BUTTON);
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, SUBMIT), Type.BUTTON);
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, BUTTON), Type.BUTTON);
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, RESET), Type.BUTTON);
    // text input
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, TEXT), Type.TEXT);
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, PASSWORD), Type.TEXT);
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, HIDDEN), Type.TEXT);
    // text area
    map.put(new AbstractMap.SimpleEntry<String, String>(TEXT_AREA, NULL), Type.TEXT_AREA);
    // checkbox
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, CHECKBOX), Type.CHECKBOX);
    // radio
    map.put(new AbstractMap.SimpleEntry<String, String>(INPUT, RADIO), Type.RADIO);
    // select
    map.put(new AbstractMap.SimpleEntry<String, String>(SELECT, NULL), Type.SELECT);
    map.put(new AbstractMap.SimpleEntry<String, String>(SELECT, SELECT_ONE), Type.SELECT);
    // option
    map.put(new AbstractMap.SimpleEntry<String, String>(OPTION, NULL), Type.OPTION);
  }

  /**
   * @return the type of html control element
   */
  public Type getType() {
    return type;
  }
}

Wednesday, 21 November 2012

Web Page Object How To

That's been a while since I wrote last time :) Therefore, have lately decided to summarize more or less how to use Page Object properly just to keep the good note for myself.
First of all, let's remember the git of Page Object Pattern briefly. It is an abstract description of a page (web page in our case) or of its part that has got at least one "business value point". Technically, that is a class that has a set of fields and/or their getters/setters which describe the elements available on a page and a set of methods which describe actions you may perform with those elements. And that's it, not less, not more ...

That's the typical page object implementation using webdriver API: (of course you're free to use PageFactory instead of defining the locators as fields)
public class TypicalPageObject {
  private final WebDriver driver;
  
  private static final By ELEMENT_LOCATOR = By.id("element1");
  private static final String VALUE_LOCATOR = "value";

  public TypicalPageObject(WebDriver webdriver) {
    driver = webdriver;
  }

  public void setElementValue(String value) {
    getElement().sendKeys(value);
  }

  public void getElementValue() {
    getElement().getAttribute(VALUE_LOCATOR);
  }

  private WebElement getElement() {
    return driver.findElement(ELEMENT_LOCATOR);
  }
}

Let's analyse it a bit.

  • no public webelements - page object is the margin layer of webdriver API usage; it should not provide any public accessory to others unless it is some child page object
  • WebDriver is defined as constructor parameter - it makes the page object independent from webdriver, though I not entirely agree it should be applied as a pattern - you may initialize driver wherever you want and encapsulate it in page object class. Though, Thucydides throws WrongPageException and says "the page object looks dodgy" if it's constructor does not contain parameter with WebDriver type. That was quite a surprise for me.
  • at least getter - that's the part of "not less" ... you probably may have an element which is read-only
  • no DSL within page object - that's the part of "not more" ... DSL is the higher level of abstraction and by far has nothing to do with page object. Though it is usually implemented invoking the page object. No createUserAccout(), populatePurchaserForm() etc ...
  • No assertions - another part of "not more". Page object should provide the data for them only. 
That's basically it ...
You may also extend the page object with entity based approach. Let's say you have the entity: 

public class TypicalEntity {
    public static final String FIELD = "field";
    private String field;

    public String getField() {
      return field;
    }

    public void setField(String field) {
      this.field = field;
    }
  }

Then our typical page object might look like:

public class TypicalPageObject {
  private final WebDriver driver;
  
  private static final By ELEMENT_LOCATOR = By.id("element1");
  private static final String VALUE_LOCATOR = "value";

  private final Map<String, By> mapLocatorToField = new HashMap<String, By>();

  public TypicalPageObject(WebDriver webdriver) {
    driver = webdriver;
    mapLocatorToField.put(TypicalEntity.FIELD, ELEMENT_LOCATOR);
  }

  public void setElementValue(String field, String value) {
    getElement(field).sendKeys(value);
  }

  public void getElementValue(String field) {
    getElement(field).getAttribute(VALUE_LOCATOR);
  }

  private WebElement getElement(String field) {
    return driver.findElement(mapLocatorToField.get(field));
  }
}
In that case you have to map the accessory to webelements upon the field of an entity. It makes you put additional efforts to maintain entities, though page object becomes more generic.

Saturday, 17 March 2012

My first contribution to selenium :)

Well, I really proud of it :)  -  r16282
You would say only 3 lines of code , haha ... that was enough though to fix rather important thing
However, I consider it as an experience of getting introduced with Jim Evans more , than the fix itself.

Tuesday, 28 February 2012

Selenium Camp 2012 Review

Of course I've been there with other 350 attendees ... I could not skip the event, therefore, which took place that near. I really like what XPInjection does, and SeleniumCamp is nearly the best among theirs inventions and achievements.
Saying about speakers, however, I did not notice much progress comparing to 2011. I would even say that by far a half of them brought the same what I had heard at 2011. Although, another half of topics was just amazing: major excitement I've got was about Selenium as a W3C standard and WebDriver Internals. Besides, I really enjoyed "Selenium vs Ajax" topic by Alexey Barancev and heard by far the nicest feedback to TDD topic by Nikolay Alimenkov.
Camp attracts a lot of people from abroad  - a co-worker of mine came from Sweden to attend the camp. Five among seven time slots have got at least one speaker in English. I wish it would've been a half of all speeches and slides in English preferably for topics in Russian

I hope Selenium project will find outstanding contributors within 350 attendees auditory ... Though, when I checked out sources from selenium trunk, I utterly realised David Burns's words about project complexity and scale. For time being I do not fill I can consciously change something without breaking anything :), however, I fill the beginning of a long road to follow. I also really hope I am not the only one who feels the same.

Catch javascript errors with webdriver

Everyone would consider to have the way of catching JavaScript errors on site earlier or later.
Thanks a lot to Marc Guillemot’s blog for nice solution. You should include extension to your Firefox instance.
After this you might use your capabilities for remote webdriver
Besides,

Wednesday, 15 February 2012

How to maximize firefox or explorer window with webdriver

There are two reasons to regard the obvious, from the first point of view, topic. Despite the solution fits .net webdriver only, it is better than JavaScript approach since firefox window cannot be re-sized with it - that's the first reason. The second reason is the issue described here, and window maximizing partially solves it

Friday, 27 January 2012

Post condition: getting all the windows closed

We've got a separate remote pc for tests execution. I don't rdp there often, though once I do, I have to close lots of windows manually :(
That's because some of my tests have to deal with popups and new windows, however, only the current window got closed until I applied following post condition

Tuesday, 24 January 2012

How to automate Omniture Debugger. Omniture Debugger Page Object.

That is quite a task from the first point of view, taking into consideration that the debugger itself is javascript based. In addition the task gets a condition, when you know that the Omniture Debugger page actually does not stop loading at FireFox - it transfers data from some omniture host, so, please be the following solution works for IE only. Though, after all, I do not really care, because I do not tests omniture, I test properties are accessed by debugger
Let's review what do we have:
  • javascript that actually calls omniture debugger page. Normally you use it as a bookmark.
  • some page that is considered as a referrer to Omniture Debugger page
What we need to have at the end:
  • open Omniture Debugger page in a popup and switch to it
  • ensure that variable or property we are searching for exists on the page and has the appropriate value

Friday, 20 January 2012

hub.bat + webdriver.bat + JSON configuration

Hope someone will consider it helpful. Do not forget to resolve the ports at firewall.

hub.bat
@echo off
cd c:\selenium
java -jar selenium-server-standalone-2.14.0.jar -role hub
webdriver.bat
@echo off
cd c:\selenium
java -jar selenium-server-standalone-2.14.0.jar -role webdriver -nodeConfig myconfig.json -hub http://localhost:4444/grid/register

Tuesday, 17 January 2012

How to debug selenium / webdriver tests live

I don't really remember where have I found that, yet I guess it would be nice to have the quick reference here.
What I have are:
  • Visual Studio 2010 Pro
  • hub + webdriver started
  • NUnit based tests
What I need is:
  • start a test execution both in debug or normal mode directly from Visual Studio
  • an ability to use breakpoints, watchers etc

Monday, 16 January 2012

Why selenium?

I utterly understand that there are lots of talks around the subject and there is no sense to pick it up again, yet I just want to ensure myself that I have chosen correct tool to work with. If you agreed the reasons I would really appreciate the fact that my first post here has got several followers.
Ok, let's try reviewing it through one by one , avoiding obvious reasons like selenium is free, cross platform/browser and bla bla bla