Using Page Objects In Sahi (Javascript)

Oct 5, 2013

pageI read a nice post by Martin Fowler about Page Objects, that reminded me that I've not yet expanded on my original post about using them with Sahi. Let me correct that...

To demonstrate using page objects in Sahi, I refactored three tests from Sahi's own demo. You'll find the original three tests in your Sahi scripts folder, in: demo\testcases\training.sah.

Note, that this is my implementation of the page object pattern... I'm sure it could be improved upon. Also note that this code currently only works in Sahi Pro version 5.x. If you're running the open source version of Sahi, you'll need to wait until they update the OS version to 5.x. Feel free to lobby the Sahi dev team to do just that ;)

If you'd like to cut to the chase, you can just download the example code .zip or get it from GitHub.

What Is a Page Object?

A page object is code written to model an actual page (or part of a page) in a web application. Eg. each page in your app, would have an associated page object file that provides the "services" that the actual page offers. Eg. if there's a button on a page in your application, you would have a reference to that button in your page object. If you then write a test that clicks that button, you would access the button through the page object.

Why Use Page Objects?

Page object provide many benefits, including:

  1. Make tests more readable/easier to understand
  2. Make tests easier to maintain
  3. Help organize code into logical chunks

Sahi Page Object Example (Javascript)

The following is the login page object you'll find in my example code.

    Login Page Object...

function LoginPage() {
    // properties: all accessors and data should be abstracted from tests...
    this.$url = $BASEURL+"/login.htm";
    this.$userTextBox = \_textbox("user");
    this.$passwordBox = \_password("password");
    this.$loginButton = \_submit("Login");

    // helper methods...
    this.go = function() {

    // pass in role array; helps test simplicity and readability...
    this.loginAs = function($roleArray) {
        this.login($roleArray\[0\], $roleArray\[1\]);

    // handle login; allows manual logins...
    this.login = function($userName, $password) {
        \_setValue(this.$userTextBox, $userName);
        \_setValue(this.$passwordBox, $password);

// extend CommonPage...
LoginPage.prototype = new CommonPage();
// instantiate the object...
loginPage = new LoginPage();

In our example, we first include a CommonPage (more on that in a minute) and then create our page object (Javascript's version of a class), LoginPage. Then we abstract out all the elements we need from the actual page and store them as properties (variables). This is one of the biggest benefits of a page object. If an element is changed in your application, you need only update it's associated property to fix your code.

We then add any methods (functions) we want to make use of on the page. In this case, we simplify the login method by also offering a loginAs method that takes a user role array as an argument. Thus to login (which we'll do a LOT) we need only call: loginPage.loginAs($admin) or in this case, loginPage.loginAs($user).

Next, we extend the functionality of our LoginPage. You may wish to have certain properties and methods available to all of your page objects. You might also have common elements across multiple pages (eg. search box or nav bar). To accomplish this, we use Javascript's prototype object to extend common objects. In our example, we have methods we'd like to make available in all of our page objects. Thus, we have LoginPage's prototype object to extend CommonPage. Now, any method in CommonPage is also available in LoginPage.

Lastly, we simply instantiate the LoginPage object. I do this because I don't care to have a bunch of "new" statements mucking up my tests. Therefore, when I include a page object, it's already instantiated and I can just start using it. This might not jive with some folks... You're of course welcome to instantiate each new page directly in your tests if you like ;)

Testing Login Using LoginPage Object

And now, here's how we use our page object...

    brian \[at\]



var $currentPageURL= booksPage.getCurrentPageURL();

\_assertEqual(booksPage.$url, $currentPageURL);

Pretty simple. We include our config.sah and the two page objects we need for the test. You'll do this in every test. Then we login using our LoginPage object that we instantiated in the variable loginPage (note the object is uppercase and reference is lowercase). Once logged in, we grab the current page url and assert that we're on the page we expected.

That's it for our login test. The test code is simple and concise... we don't use any Sahi code to access anything on the page, which should make maintaining our code MUCH easier down the line.

There are additional pages and tests in my example code. Feel free to give it a whirl and let me know what you think!