Introducing the Watir Page Helper gem

I’ve very recently released a watir-page-helper gem that provides some useful helper methods when creating page models when using Watir-WebDriver (I chose not to support Watir as it doesn’t work on Firefox 4, therefore not on non-Windows machines).

This is loosely based upon the great work that Jeff Morgan did in his series of UI testing blog posts, but takes the concept a lot further IMO.

I wanted it to be solid before I released it as a gem, so I wrote unit tests for every method, and wrote yard tagged documentation for each method also. You can see the documentation automatically generated on rdoc.info.

Installation

gem install watir-page-helper

Example

require 'watir-webdriver'
require 'watir-page-helper'

class MyPage
  include WatirPageHelper

  direct_url "http://www.google.com"
  expected_element :text_field, :name => "q"
  expected_title "Google"
  text_field :search_box, :name => "q"
  button :search, :name => "btnG"

  def initialize browser, visit = false
    @browser = browser
    goto if visit

    expected_element if respond_to? :expected_element
    has_expected_title? if respond_to? :has_expected_title?
  end
end

browser = Watir::Browser.new :chrome
page = MyPage.new browser, true
page.search_box = "Watirmelon" #This method is created by WatirPageHelper
page.search #This method is created by WatirPageHelper also
browser.close

The above example is very basic, but shows the concept well. You’re much better off putting the initialize method into a base page class so you don’t need to call the same methods for every page.

What you get

Page Methods

  • direct_url: allows you to navigate to a page upon initialization, if visit is set to true
  • expected_title: allows you to automatically assert the expected title of the page when it is initialized
  • expected_element: allows you to initialize the page by looking for a certain element. This is useful for pages that load dynamic content.

Element Methods

Element methods for a majority of the Watir-WebDriver supported elements which generate useful helper methods.
For example: text_field, select_list, radio_button, form, div, span, h1..h6 etc.

Summary

I’ve been using a watir page helper on a few different projects now and I’m sick of copy-and-pasting the code each time, so I thought it’d be nice to package it as a gem.

I also think it’s important to have unit tests for this kind of stuff, so it was a good opportunity to write unit tests for every method in these classes. The documentation was an added bonus too.

I hope you find this useful, please let me know if anything doesn’t make any sense.

Links/Further Info

14 thoughts on “Introducing the Watir Page Helper gem

  1. Very, very cool. I was just in the process of trying to implement some of your ideas from the WatirMelonCucumber project, so having this as a separate gem will help with that (since that’s part that I was definitely keeping intact).

    For that project, is the most sensible place to ‘require’ it in env.rb?

    This is exciting stuff, keep it up!

    thanks, Ali

  2. I just wanted to say thanks for all of the great information! I’ve recently started using Watir+Cucumber on a big project, and your tutorials, tips, and tidbits have been just what I needed when I needed them.

    I’m getting very excited to implement this for our QA team. So, again, thank you and please keep up with the great tutorials!

  3. This is a really cool addition but I’m curious as to how you actually use the row and cell abilities. I get how to reference a table. Say I have one called callTypes. That’s easy enough, I just define:

    table :callTypes, :id => “callTypes”

    But now I’m not clear on how to reference rows and cells on it. According to the comments in the code, if I have a row called odd, I could define it this way:

    row(:odd) { | test_table | test_table.tr }

    But then if I have logic like this:

    on MyPageWithTables do |page|
    page.odd.should == “something”
    end

    I can’t get that to work. The comment in the source code shows this:

    page.test_table_row_1.should == “Test Table Col 1 Test Table Col 2″

    I’m probably not getting how to actually reference the row as part of that table. Do you by chance have any examples of using tables that I could look at?

    1. Rows can be referenced using the tr method of page. I think the only way to Table row reference is by index, so with watir-webdriver, it automatically assumes index 0 if you don’t specify, so:

      @browser.table(:id => 'myTable').tr will provide you the first row
      @browser.table(:id => 'myTable').tr(:index => 3) will provide you the 4th row (0 based index)
      

      You can define these in your pages as such:

      table :test_table, :id => "myTable"
      row(:test_table_row_1) { | test_table |  test_table.tr }
      cell(:test_table_row_1_cell_1) { |test_table_row_1 | test_table_row_1.td }
      
      1. It’s unclear to me how this works, since the table_row does this:

        block ? block.call(@browser) : @browser.tr(identifier)

        You’re not using the passed in row as part of your call. So when I get into the call for the row, it’s only going to work if that’s the only table on the page.

        If I’m working on the 2nd table on the page, I’m going to get the first row of the first table.

        There needs to be some way to swap the @browser object with a different HTML element if it’s provided. I ran into this issue when trying to define p tags inside a specific div. I had:

        Hi
        Yo

        I want this one
        Or I want this one

        When I do:

        div :second, :class => “second”
        p(:i_want) { |second| second_div.p(:index, 0)}
        p(:or_want) { |second| second_div.p(:index, 1)}

        puts i_want # Yields “Hi”
        puts or_want # Yields “Yo”

        What am I missing?

  4. One other thing I’ve found: the timeout for the expected element does not seem to work when I specify it. For example, I have this as a page object:

    class LoginPage “login-button”, 5
    end

    Doing this, gets a syntax error: “unexpected \n, expecting tASSOC (SyntaxError)”.

    I finally figured it out, which is that you have to do this:

    expected_element :button, {:id => “login-button”}, 5

    But that would probably be best explained in the comments or as part of the example. (Maybe a full-on Rubyist would have known this already, I suppose.)

    1. Thanks, I think it’s trying the pass the hash as the third argument, best to use { and } in this usage.

  5. It appears every time I try to install the gem I get the following:

    C:\Users\JC>gem install ‘watir-page-helper’
    Fetching: spruz-0.2.13.gem (100%)
    Successfully installed spruz-0.2.13
    1 gem installed
    Building YARD (yri) index for spruz-0.2.13…

    C:\Users\JC>gem uninstall ‘spruz’
    Remove executables:
    enum

    in addition to the gem? [Yn] Y
    Removing enum
    Successfully uninstalled spruz-0.2.13

  6. Hi Alister, thank you again for your efforts! this is awesome!

    Thanks to your blog, we have something similar in terms of DSL for elements registration under a page (I still owe you a post about our page/widgets object model…). And one of the concerns we have faced is using prefixes for some kind of elements (mainly for non editable ones such as div, span, pp, h1, etc) is not very change-proof. E.g.

    “my_title_div” can easily become “my_title_span” by a small change in the html.

    This happens very often for us causing to change many places in step definitions. So we had to change our approach and use common DSL method “element” for such elements: “my_title_element”. This is similar to what there was in Watirloo/Watircraft Page Object pattern – they had “element” for non-editable and “field” for editable elements. But we are still using text_field, select_list, checkbox and some others as they are at a lower risk.

    What are your thoughts/experience on this?

  7. Yes, I agree. We face things like links becoming buttons and the buttons becoming links. It doesn’t all the time, but since there is no generic element click (which many of the elements already support) causes the automation to change even though the identification for control didn’t need to be modified just it’s type.

Comments are closed.