page-object 0.6.2 released

On February 12th I released a new version of my page-object gem. There are some very exciting features that have made it into page-object over the past few releases. I have been totally focused on finishing up my book and haven’t blogged for some time. This post is my attempt to showcase some of these new features.

Populating your page with data

In previous posts we had a page object that looked something like this:

class CheckoutPage
  include PageObject

  DEFAULT_DATA = {
    'name' => 'cheezy',
    'address' => '123 Main Street',
    'email' => 'cheezy@example.com',
    'pay_type' => 'Purchase order'
  }

  text_field(:name, :id => "order_name")
  text_field(:address, :id => "order_address")
  text_field(:email, :id => "order_email")
  select_list(:pay_type, :id => "order_pay_type")
  button(:place_order, :value => "Place Order")

  def complete_order(data = {})
    data = DEFAULT_DATA.merge(data)
    self.name = data['name']
    self.address = data['address']
    self.email = data['email']
    self.pay_type = data['pay_type']
    place_order
  end
end

Our complete_order method is passed a Hash that it merges with its’ default data and then populates the elements on the page. There are a few things we can do to simplify this page object. The first is to eliminate the methodical code that takes the data from the merged hash and sets the fields on the page.

Several releases ago page-object added a method named populate_page_with. This method takes a Hash and updates fields on the page with the items found in that Hash. It does this by matching the keys with the names you set when defining the elements. For example, if you defined a text_field like this:

  text_field(:name, :id => "order_name")

then the item in your Hash must have a key of name. All values must be Strings except radio buttons and checkboxes must be true or false. Using this method we could simplify our complete_order method above to this:

  def complete_order(data = {})
    populate_page_with DEFAULT_DATA.merge(data)
    place_order
  end

The next then we need to address is the large Hash defined in the page. I’ve recently released a new gem named data_magic that can help us here. Let’s explore how this gem works and how we can use it for our page.

data_magic allows you to specify data used by your application in yml files. With this gem you can easily load and use this data. You can also randomize the data. You only have to learn thee methods in order to learn everything there is to know about data_magic. The first method defines where the yml files will be located.

DataMagic::Config.yml_directory = 'features/yaml'

This method should be called prior to using the features of the gem. If you do not specify a directory the gem will assume a default directory named config. If you are using cucumber I suggest you just place this line in your env.rb file.

The next method you need to call is the method that actually loads a file. It looks like this:

DataMagic.load "ohio.yml"

This method will load the yml file and make it available to your tests. You would typically call this in your Given portion of your step definition. For example, you might have a step that states Given I’m using the application from Ohio and then have this step load the file for Ohio. If you do not call this method the gem will default to using a file named default.yml.

The final method you can use will return some data based on a key. If you have a file with this contents:

checkout_page:
  name: Cheezy
  address: 123 Main Street
  email: cheezy@example.com
  pay_type: Check

Then you could simply update the page-object above to use the data_for method like this:

class CheckoutPage
  include PageObject
  include DataMagic

  text_field(:name, :id => "order_name")
  text_field(:address, :id => "order_address")
  text_field(:email, :id => "order_email")
  select_list(:pay_type, :id => "order_pay_type")
  button(:place_order, :value => "Place Order")

  def complete_order(data = {})
    populate_page_with data_for('checkout_page', data)
    place_order
  end
end

The second parameter to data_for is an option Hash that is merged with the data from the yml file. The other nice feature about the data_magic gem is that it can randomize your data. By simply updating our yml file to the following we now have data that is randomized each time we call the methods.

checkout_page:
  name: ~full_name
  address: ~street_address
  email: ~email_address
  pay_type: Check

Navigation

Those of you who have taken my ATDD with Cucumber class know that on day three of the class we end up with step definitions that contain duplication:

When /^I complete the adoption of a puppy$/ do
  on_page(HomePage).select_puppy
  on_page(DetailsPage).add_to_cart
  on_page(ShoppingCartPage).continue_to_checkout
  on_page(CheckoutPage).complete_order
end

When /^I adopt a puppy leaving the name field blank$/ do
  on_page(HomePage).select_puppy
  on_page(DetailsPage).add_to_cart
  on_page(ShoppingCartPage).continue_to_checkout
  on_page(CheckoutPage).complete_order('name' => '')
end

On two different coaching assignments I wrote some custom code to help the teams eliminate such duplication in their navigation code. Reinventing the wheel gets old after a while so I decided to add navigation functionality to the gem. These new methods have been added to the PageObject::PageFactory module. The concept is simple. All you have to do is define different routes through the application and then you can use these new methods. Let’s start by defining the route from the examples above.

PageObject::PageFactory.routes = {
  :default => [[HomePage, :select_puppy],
               [DetailsPage, :add_to_cart],
               [ShoppingCartPage, :continue_to_checkout],
               [CheckoutPage, :complete_order]]
}

Here I am defining a single route named :default. With this in place I can now rewrite the two step definitions above.

When /^I complete the adoption of a puppy$/ do
  navigate_to(CheckoutPage).complete_order
end

When /^I adopt a puppy leaving the name field blank$/ do
  navigate_to(CheckoutPage).complete_order('name' => '')
end

That got rid of our duplication but let’s see what else we can do with the new methods. What can you do if you need to stop in the middle of the navigation to perform some non-default action? Here’s an example of how you can do that:

When /^I need to navigate through a long series of pages$/ do
  navigate_to(FirstPageWhenINeedToStop).do_something_here
  continue_navigation_to(SecondStoppingPoint).do_something_else
  continue_navigation_to(TheFinalPage).complete_the_work
end

In this example I’m using the continue_navigation_to method which will begin at @current_page and continue to the next defined page.

As you might suspect, both navigate_to and continue_navigation_to support passing a block if you need to perform multiple activities on the page.

It is fairly common to have multiple navigational paths through you application. They way the page-object handles this is to allow you to define as many routes as you wish. To use a route other than the :default route you simply pass a hash to the method.

# route is named :the_other_route
navigate_to(SomePage, :using => :the_other_route) 

If you are using cucumber I suggest you place the routes definitions in your env.rb file. The only gotcha that you might encounter is that all of the pages need to be required prior to the initialization of the routes. One simple way to do this is to use the require_all gem. If you have this gem available you will simply have to add two lines prior to setting the routes.

require 'require_all'
require_all File.dirname(__FILE__) + "/pages"

This example assumes that all of you page objects are stored in a directory named pages under the support directory. If that is not the case you will need to update the path to reflect the correct location.

Waiting for Ajax Calls

It is fairly challenging to test websites that have a lot of javascript and ajax calls. The page-object gem already has a nice api to help overcome these challenges. With the latest release there is yet one more tool in your arsenal.

The latest release of the gem has added a wait_for_ajax method that waits until all pending ajax requests have completed. This method currently only works with jQuery and Prototype but it has the ability to easily add additional javascript frameworks. Let’s take a look at how it can be used.

The first thing you need to do is inform page-object of what Javascript framework you are using.

PageObject.javascript_framework = :jquery

Valid options are :jquery and :prototype. Once you’ve set the framework you simply call the wait_for_ajax method on any page.

def method_that_waits_for_ajax
  wait_for_ajax
  do_something_that_needed_to_wait
end

That’s all there is to it. Again, the wait_for_ajax method will wait until the number of active ajax requests drops to zero.

What if you’re not using jQuery or Prototype? page-object has a way for you to add your own Javascript framework so you can use the wait_for_ajax method. The first thing you need to do is write a module that has a single method named pending_requests. This method must return the javascript necessary to retrieve the number of active ajax requests. Here is the module for jQuery that is a part of the page-object gem.

module PageObject
  module Javascript
    
    module JQuery
      #
      # return the number of pending ajax requests
      #
      def self.pending_requests
        'return jQuery.active'
      end
    end
    
  end
end

The next thing you will need to do is call the add_framework method passing the symbol you wish to use and the module.

PageObject.add_framework(:my_framework, MyModule)
PageObject.javascript_framework = :my_framework

You are ready to go. If you are using a widely distributed javascript framework and you wish to donate your module, I’d be happy to add it to the gem.

47 thoughts on “page-object 0.6.2 released

  1. I know this isn’t something you specifically talk about above, but I’m curious. Is there a way for me to get the contents of a row? Specifically, I have a row that has text and a radio button. I need to see if the radio button is selected. If it’s not, I need to click on the row.

    I have logic like this that looks through my table object:

    on MyUsersPage do |page|
      if page.list_table.rows >= 1
        page.list_table.each do |row|
          if row.text.include?(my_data["name"])
            p row.inspect
          end
        end
      end
    end
    

    In place of the “p row.inspect”, I’d like to have logic that basically says:

    (1) Enumerate the contents of this table row.
    (2) If the contents include a radio button, check if that radio button is selected.
    (3) If the radio button is selected, do nothing.
    (4) If the radio button is not selected, click the row (which selects the radio button).

    I can’t look for the radio button ID specifically because the radio buttons are dynamically generated as new data is loaded in. They have id’s like “user_row_23830”.

    Is there some way I can do that?

    • Tatiana,

      The first thing I will suggest is that you do all of this type of work in the page object. Hiding the details of the page is what the page-object gem is all about. The code in your step definition would change to this:

      on_page(MyUsersPage).select_all_options
      

      Now the code in your page object will look something like this:

      def select_all_options
        unselected_options.each do |opt| 
          opt.click 
        end if list_element.rows > 0
      end
      
      private
      
      def unselected_options
        list_element.find_all do |row|
          row.each do |col|
            unless col.radio_button_element.nil?
              return true unless col.radio_button_element.selected?
            end
          end
          return false
        end
      end
      

      In the unselected_options method I first of all look for all rows that have a column with a radio button that is unselected. I use the find_all method to do this. Inside the find_all block I loop each row and first of all check to see if there is a radio button and next check to see if it is selected. Returning true from the find_all block will add the row to the returned set.

      Let me know if you have any questions about this code.

  2. Thanks. That did make sense and I now have a better understanding of how these things work. I’m curious about one other thing if you don’t mind. You mentioned the logic in the page object and I think I’m starting to see one of my problems. I have a method defined outside of page objects but that use the page factory. So I have this:

    def search_for(text)
      on EntityList do |page|
        if page.list_table.rows >= 1
          page.list_table.each do |row|
            if row.text.include?(text)
              result = true
            end
          end
        else
          result = false
        end
        result
      end
    end
    

    However, result is never returned. What always gets returned is the page object itself. So if I run the following statement:

    result = search_for("my text")
    

    Then I don’t get true or false; but, rather I get the page object:

    #

    I originally saw the page object as just defining the fields that are on the page. But it’s sounding like you’re saying I should also use it as a repository of the methods.

    • Tatiana,

      The purpose if page objects is to “encapsulate” the knowledge of how the page works inside a class and only expose higher level methods to your tests. The goal is to make our tests far less brittle. Let’s explore your example above. I would push the logic that walks through the table into the page object and have my step definition ask a simple question. In this way, the step definition has no idea that the page even has a table. It is simply asking if some text is in a list.

      class EntityList
        include PageObject
      
        table(:list, :id => 'list_table')
      
        ...
      
        def list_contains(text)
          list_element.any? { |row| row.text.include? text }
        end
      end
      

      Here I’m using a method from Enumerable to greatly simplify my code. The any? method will call the block with each row and return true if any of the blocks returns anything other than false or nil. In the step definition I should simply have this:

      on_page(EntityList).list_contains("text I'm looking for").should be_true
      

      Again, I only exposed a method to the step definitions. The step definitions do not know that I need to dig through a table to get the answer.

      -Cheezy

  3. In my code, I had to change the line populate_page_with data_for(:checkout_page, data) to populate_page_with data_for('checkout_page', data) because in
    data = DataMagic.yml[key], key has to be a string.

  4. hi

    cheezy , that makes our life lot easy and code maintainable. I have just started using Watir for our automation and ruby novice.I want install this gem from github to use this in my automation.i tried but it fails with one or other message. appreciate your help in his.

    thanks
    Jenga.

  5. Hi Cheezy,

    Well , I believe that I wasted real estate of this blog, with my previous question. I just did
    > gem install page-object
    and all worked.

    Thanks
    Jenga.

  6. Thanks for the responses. I have now started putting a lot om my logic in my pages and my step definitions are nice and concise.

    I do have one other question for you. We have some actions that may cause a javascript alert to pop up. It does not always however. It really depends on the data going into the test. So in other words I have one generalized step def that acts upon different data.

    How would you check for the existence of a javascript alert in these cases? I was going to have a method on my page object that checks for it. Then any ‘Then’ step defs in those pages will, as their last action, check if the alert has appeared and, if so, basically dismiss it.

    Does the page sound like the right place for that to you?

    • Perhaps you can do the following.

      def data_method
        # perform the activity here
      end
      
      def data_method_with_alert
        alert { data_method }
      end
      

      The second method will return the text of the alert if it occurred so you can have a step definition like this:

      Given /^my step has alert (.+)$/ do |text|
        on_page(MyPage).data_method_with_alert.should == text
      end
      
  7. Pingback: A Smattering of Selenium #82 « Official Selenium Blog

  8. Hey, I just noticed in data_magic, besides the ‘faker’ translations, I can use ruby code to create data. So to fill in date fields with today’s date I used:
    my_page_name:
    month: ~Date.today.month
    date: ~Date.today.mday
    year: ~Date.today.year

    Pretty cool.

    • Thanks for this, John. Saved me some time for sure! I like to timestamp all my data entries and ~ allowed regular Ruby code (reading a variable from another file).

      Also, while this might be obvious to others, I thought it would be helpful to point out that you can inject values into the data that do not exist in the default data set. This is useful for when you have radio buttons as the negative test requires a different data field to be set entirely. My first approach to have the data.yml file ‘know’ about them was to set the negative selections to ‘false’, but Watir will give an error saying that it cannot clear a cleared radio button.

      With this method, the injected data is altered last since it is not normally listed, so the radio_selection_yes button will be set to ‘true’ first, then the radio_selection_no button will be set to ‘true’, clearing the previous button’s status.

      (page.rb)
      radio_button(:radio_selection_yes, :id => “yes”)
      radio_button(:radio_selection_no, :id => “no”)

      (data.yml)
      normal_test:
      name: tester
      email: test_me@cheezyworld
      radio_selection_yes: true

      (steps)
      @page.complete_form(‘radio_selection_no’ => ‘true’) #not included in data.yml

  9. I’m a little unclear as to how navigation between different pages is accomplished. Given the example above:

    When /^I complete the adoption of a puppy$/ do
      on_page(HomePage).select_puppy
      on_page(DetailsPage).add_to_cart
      on_page(ShoppingCartPage).continue_to_checkout
      on_page(CheckoutPage).complete_order
    end
    

    Each action uses the “on_page” method, which by default does not navigate to the url passed to the “page_url” accessor method defined in the page object class. As such, it appears the above example never actually navigates to a new url between actions. This would make sense for a rich javascript application which swapped-in new “page” templates dynamically, as the url would not change- just the markup.

    If each page did actually correspond to a separate url, I’m guessing one of the above actions would need to be modified to include the optional “visit” parameter set to true:


    on_page(HomePage, true).select_puppy

    If this is the case, I’m uncertain as to how PageObject::PageFactory.routes can be used to navigate through pages with varying urls. It appears that PageFactory uses the “navigate_through_pages” method to iterate through each page specified in a route array, however, “navigate_through_pages” also uses the default “on_page” method parameters, so the url never changes.

    I noticed this happening when PageFactory was iterating through the route I specified correctly, as it was calling various methods on the page objects, but the url never changed, so the methods failed. I initiated the navigation with:


    navigate_to(SomePage, :using => :other_route).do_something

    In the example above “SomePage” was second in the “other_route”, and the first page on the route was iterated upon, as its specified method was called. What am I missing regarding navigation?

    • Frank,

      First of all there is a background task (not shown in this article) that has the following code:

      visit_page HomePage
      

      Each corresponding page is reached by clicking a button that causes the application to navigate to the next page. For example, the select_puppy on the HomePage takes you to the DetailsPage. visit_page will navigate to a specific and on_page assumes you are already on that page.

      Hope this clears it up.

      -Cheezy

      • Thanks, Cheezy- that makes sense. I didn’t realize the action methods (i.e. select_puppy) were implicitly navigating you to the next page in the route.

  10. I’m transitioning some scripts that already use page object formatting and default data to use the page_object gem. So far so good. How would you handle radio buttons and default data, with regard to the populate_page_data method?

    Right now I have a series of questions, each with two radio buttons for a negative or affirmative answer (yes/no). I was setting a value in the default data to “yes” or “no” for each question and then running a case statement for that variable – if yes, click this radio button, if no, click the other one.

    How would you build that into default data without the case statement, so that it can be used with the populate_page_with method? I’d love to have a one-liner for this.

    Or would the populate_page_with method be used just for the text fields, select lists, etc and then these radio button cases come after this command in my complete_form(data={}) method?

    Thanks!

    • Or I could just search the RDoc 30 seconds after asking! Do’h.

      This method will populate all matched page TextFields, TextAreas, SelectLists, FileFields, Checkboxes, and Radio Buttons from the Hash passed as an argument. The way it find an element is by matching the Hash key to the name you provided when declaring the element on your page.

      Checkboxe and Radio Button values must be true or false.

      can be either a string or a symbol. The value must be a string for TextField, TextArea, SelectList, and FileField and must be true or false for a Checkbox or RadioButton.

  11. Ok, I’ve nearly completed the transition for a small proof-of-concept script and am getting some unexpected behavior with a link – the script believes it to be an array, and errors out because of it.

    The script in general – hits a page, and clicks one of three links identified by :href partial match. Each matcher only finds one result on the page, so there are not multiple elements found. There are 5 total links on the page, disregarding CSS which do not match anyway.

    The reason I am using a partial match is because:
    1) All links have the same Text (“Get Started!”)
    2) The subdomain is dynamic, so I can’t match a full URL.

    Is this a known newbie issue, or have I gone mad?

    PageObject is required/included, and PageFactory is required/World.

    Given I am on the demo page (defined in page_steps.rb and paths.rb) [navigates to page successfully]
    When I click the hourly link [correct link is clicked, however script exits with error about this page]

    Cuke output:

    When I view hourly positions
    features/step definitions/application_steps.rb:6
    undefined method `click’ for []:Array (NoMethodError)./features/step definitions/application_steps.rb:9:in `/^I view (.*) positions$/’
    features\applicant.feature:11:in `When I view hourly positions’7 case category
    8 when “hourly”
    9 on_page(CompanyPage).hourly.click
    10 when “management”
    11 on_page(CompanyPage).management.click

    Page in question:

    class CompanyPage
    include PageObject

    link(:hourly, :href => /action=hourly/)
    link(:management, :href => /jobType=M/)
    link(:corporate, :href => /jobType=C/)
    end #class

      • Cheezy,

        Over some late night troubleshooting, I realized that page-objects don’t use “click” like vanilla watir-webdriver does (you can see the click action in my code above). I removed “click” from the statements and it started working as expected. An easy oversight for a PO gem newbie, but a difficult change from the watir standard I’ve been using for ~7 years.

        There are definitely more concessions when using the page-object gem than I anticipated as far as AJAX handling being 100% PO-handled, and no longer the watir way. I believe the end functionality will be equally as good after the transition to page-object, and the organization will be better.

  12. Are you thinking to allow the addition of ‘image based’ elements into page objects in future? If you remember I was talking about Sikuli when you were here last year with iMedia (Nationwide), but at that time their gem was not up to date with their tool.

    I lately checked and sikuli gem is now more up to date with the sikuli tool itself. And I was wondering if page-object can utilize “power” of sikuli!

    Now, what I mean to say is –

    class SomePage
      include PageObject
     
      text_field(:username, :id => "username")
      text_field(:password, :id => "password")
      button(:submit, :image=> "")
     
      def login
        'Login stuff where I have ability to execute command like - submit_element.click or mention of submit will automatically click on image match found
    
      end
    end
    

    I am thinking from the perspective that – sometimes you want to use page-object (due to its awesomeness) with non-web objects… or with flex objects or may be I want to build a ruby script that can play angry birds for me and I want page-object to handle my objects.

    Does it look like a good idea?

    • I am not a fan of tests that couple directly to the appearance of the item you are interacting with. They yield extremely brittle tests.

      That being said, page-object dues support images on web pages. The example above could be handled using button(:submit, :src => 'src of image'). The gem also handles image elements via the image collection of methods.

  13. This is an awesome gem, love it. I have recommended this in my company and we have just started doing this… thank you for all the work you have put into this.

    • Thanks for the kind words. Please let me know if there is anything I can do to help or if you need a feature added to the gem.

  14. first thanks for the gem – I attended your 1/2 day page objects class at watir conf in march and just now started playing around with page objects

    I have several links on a page with the same text on them.
    I figured I would base the link on the div it’s inside.

    Link

    link(:request_price, :text => “Request Price”)
    is returning me the wrong link

    Div
    div(:request_section, :id => “request”)

    I would like to define the link based on the div – what’s the syntax for that? I have tried a few things but I keep getting it wrong.

    Thanks bunches, I hate to bother you – I did do some searching – but haven’t yet been able to figure it out.

  15. Hi, I just added a class to the link – as well as the text (instead of basing off the div it resides in ) and it’s working fine now. Thanks 🙂

    • I’m glad this is working. Just for fun, would you mind posting the html with the div so I can demonstrate how to solve it that way as well?

      Thanks

      • Sure – so just trying to get the link to click using the text – but due to the other links that’s not working.

        Before page objects I would do something like:

        request_div =b.div(:id, 'request')
        request_div.link(:text, "Request a Quote").click
        

        html:

            <a href="#" rel="nofollow">Request a Quote</a>
        
        • Trying to click a link based on the text of the link, but that text occurs elsewhere on the page, so I want to click the link with that text inside the div.

          Hopefully I have done a better job formatting and pasting this time – sorry.

          <a href="#" rel="nofollow">Request a Price</a>
          
        • Leigh,

          What you will do in this case is declare the following in your page object:

            div(:request_div, :id => 'request')
            link(:request_quote) do |page|
              page.request_div_element.link_element(:text => 'Request a Quote')
            end
          

          Here we are first declaring the div and then the link. When declaring the link we are passing a block that is in turn passed the page. From there we retrieve the div element and then look for the link with the appropriate text within that div. On you page you will simply call the methods generated from the link.

          Hope this helps.

  16. Hey Cheezy!

    After using regular Watir for so long, I’ve become spoiled by how well it waited before performing the next action. In my new page-object framework, I’m finding lots of places where I need to add explicit waits. I’m on the fence about whether implicit waits are better or worse than explicit waits – I can see the rationale for each. I’ve decided to make the move to watir-webdriver, so I need to learn to deal with explicit waits. I was really excited when I read about your wait_for_ajax method.

    I’m trying to use the add_framework method to add the YUI framework so I can use the wait_for_ajax method on it.

    Here’s my yui.rb file (YUI doesn’t have a method that return the number of pending requests like JQuery, so I got the javascript our devs use):

    ###################################
    module PageObject
    module Javascript

    module YUI
    #
    # return the number of pending ajax requests
    #
    def self.pending_requests
    “var inProgress=0
    for(var i=0; i < YAHOO.util.Connect._transaction_id; i++) {
    if(YAHOO.util.Connect.isCallInProgress(i))
    inProgress++;
    }
    return inProgress;"

    end
    end

    end
    end
    #####################################

    Here's my code for adding the YUI framework:

    #####################################
    PageObject.add_framework(:yui, 'Javascript::YAHOO')
    PageObject.javascript_framework = :yui
    #####################################

    I think my problem is in defining the module, but I've tried everything I can think of (Javascript::YAHOO.util, Javascript::YAHOO.util.Connect, Javascript::YUI) and I get the following error:

    C:/ruby/lib/ruby/gems/1.9.1/gems/page-object-0.6.7/lib/page-object/javascript_framework_facade.rb:41:in `add_framework': The Javascript framework you provided does not implement the necessary methods (RuntimeError)
    from C:/ruby/lib/ruby/gems/1.9.1/gems/page-object-0.6.7/lib/page-object.rb:114:in `add_framework'
    from C:/qa/Applications/Epicenter/dev_watir_2_0/sanity_tests/sanity_test
    _harness.rb:28:in `’

    I’ve modified my query in my pending_requests method to something more simple to rule out any issues with the javascript:

    ########################
    def self.pending_requests
    ‘return isCallInProgress(0);’
    end
    #########################

    but that doesn’t help either.

    Do you have any ideas on how to add the YUI framework?

    Thanks in advance!

    -Tiffany

  17. So, it turns out,

    PageObject.add_framework(:yui, PageObject::Javascript::YUI)

    was the right answer, I just had to add an explicit require for the yui.rb file that has my new YUI module:

    require ‘page-object/javascript/yui.rb’

    Should have paid more attention to the ‘unitialized method’ errors I was getting….

    I’ve submitted a pull request to your page-object Github repository to permanently add the YUI framework.

    Thanks for this new wait_for_ajax method! My biggest problem in moving from watir 1.9.1 to watir-webdriver is the loss of watir’s implicit waits – I guess I was spoiled. 😉 Now I hope to use a lot of wait_for_ajax calls to get around thinking about waiting too much.

    Thanks Cheezy!

    -Tiffany

  18. Hi Cheezy,

    Do you if it is possible to implement indexed_property inside an indexed_property?
    Basically, I have a table inside a table and I want to use this functionality, I’m planning to initiate the indexed_property like this:

    indexed_property(:first_table [ maybe somewhere here…
    indexed_property(:second_table [
    [:row1, :id => “hello”],
    ])
    ])

    Maybe to call it like?

    page.first_table[“Hi”].second_table[“Foo”].row1

    Is this possible?

    Thanks

  19. Hi Chezzy, how can I select an element in a drop-down box using your SelectList method.

    This is how I currently do it:
    select_list :unit_type, :id => “payroll_main_account_unit_type”

    This is how I call it:
    @chart_of_accounts.unit_type = “Time”

    But alternatively I would like to select an element within a drop down box via index.

    Any ideas ?

    Regards,

    Nick

  20. I like the idea of the routes. What I’m wondering is let’s say you have yml files that you read to get data. Right now I’ll have a line like this:

    data = account_data(“default”)

    What that does is read in information from a yml file. That data is then passed around to my methods. So for example, I may have a matcher that calls this method:

    on_page(CreateAccount).enter_temporary_data

    Then the method looks like this:

    def enter_temporary_data
    data = account_data(“default”)
    create_account_study(data)
    create_account_overview(data)
    save_account
    end

    Each of those methods being called is defined on the CreateAccount page object. So with routes, I thought I might do this:

    PageObject::PageFactory.routes = {
    :create_new_account => [
    [CreateAccount, :get_data],
    [CreateAccount, :create_account_study],
    [CreateAccount, :create_account_overview]
    ]
    }

    So that first call to get_data there would hold my logic that calls out to the yaml file. Problem is: that call returns the hash. It’s that hash that I need to send to the other methods.

    So I guess my question is: how would I use routes when there is data that needs to be available to each of them? Basically what I tried to do was take my original enter_temporary_data and convert it into a route based approach. But I’m stuck on how to make sure that the relevant information gets passed to each method in turn.

  21. When using your framework with Selenium-webdriver how is one suppose to fire custom browser events that are not currently being fired by default in selenium?

    For instance suppose I want to fire the “keyup” event on a text field? My application has a “keyup” event being bound to some ajax activity to call webvservices.

    • You can call the execute_script method to pass javascript to the browser for execution. I have used this to send hover and other events to different elements.

  22. I have a set of select lists, checkboxes that have similar properties. I have them under the indexed_property. I was able to select the values from the select_list without any problem. However, when I use the check_myindexpropitem.mycheckbox, I get an error NameError: undefined local variable or method `check_myindexpropitem’ for #. I get a true returned for check_myindexpropitem.mycheckbox_element.enabled?

    Code:
    indexed_property(:myindexpropitem, [ [:select_list, :myselectlist, {:id => ‘drpV%sselect1’}], [:checkbox, :mycheckbox, {:id => ‘checkV%scheckone’}] ])

    if myindexpropitem[i].mycheckbox_element.enabled? # i is from the for loop
    check_myindexpropitem[i].mycheckbox
    end
    Please help! Thank you!

    http://stackoverflow.com/questions/20136538/how-to-check-a-checkbox-within-a-indexed-property

  23. Hi,

    I was trying to follow through the example on your book. On page, you create an instance variable @cart = ShoppingCartPage.new(@browser) which I understand. What it’s not clear is that do you create this object in the ShoppingCartPage class or in a different file? Also, to use the methods (in step definitions) inside the ShoppingCartPage class, do we have to create an instance on the ShoppingCartPage every step. I was able to make my modified steps to work if I create an instance at every step.
    Also, should I keep the step definitions for each page separate from step_definitions file under feature. The pages are in support folder, a directory below.
    Please update with your helpful comments and suggestions.
    Thank you.

    • You do not have to create an instance in every step. All step definitions share the same scope so that means that instance variables that you create in one step can easily be used in another step. I will mention that creating instance variables of pages is not the preferred way to do this. Instead, look at the methods “on” and “visit”.

      All of your step definitions should reside in the step_definitions directory. You should organize them in a way that is easy for you to find what you are looking for. Cucumber does not impose any restrictions.

  24. I am having trouble figuring out how to make page object and watir interact with a DIV element list. Everything on the page is written as a div and a li instead of buttons and such, so I cant figure out how to “click” on specific elements in the div list.

    This is the section of code I am trying to access. The part of the div that Says store is a tab, that is clickable, and thats what I am trying to test. How would i access the “store” part of the element here and click on it to go to the store page or the url listed in the code?

    Grown-ups
    Store

Leave a Reply

Your email address will not be published. Required fields are marked *