Search code examples
page-object-gem

page element definition for cloned rows


I am using the page-object-gem and trying find the best way to define my page elements when a set of text_field have an infinite number of occurrences.

The HTML on page load is similar to the following:

<div><input id="dx_1_code" value=""/> <input id="dx_1_dos" onblur="clone($(this),false)" value=""/></div>

If the user tabs out of the last input then a new row is cloned with id values that increment with HTML like follows:

<div><input id="dx_2_code" value=""/> <input id="dx_2_dos" onblur="clone($(this),false)" value=""/></div>
<div><input id="dx_3_code" value=""/> <input id="dx_3_dos" onblur="clone($(this),false)" value=""/></div>

My first try was to define my class as follows:

class SamplePage
  include PageObject
  include DataMagic

  text_field(:dx_1, :id => "dx_1_code")
  text_field(:dx_2, :id => "dx_2_code")
  text_field(:dos_1, :id => "dx_1_dos")
  text_field(:dos_2, :id => "dx_2_dos")

end

However I quickly ended up with a lot of redundant entries.

Is there a better way to handle an unknown number or entries like this in terms of element setups and use of the populate_page_with method?


Solution

  • The elements are indexed, which makes them a good candidate for the indexed properties feature. The indexed_property lets you define locators where a number is substituted in when accessing the element. The page object would look like:

    class MyPage
      include PageObject
    
      indexed_property(:dx, [
        [:text_field, :code, {id: 'dx_%s_code'}],
        [:text_field, :dos, {id: 'dx_%s_dos'}],
      ])
    end
    

    The first two rows would then be inputted using:

    page = MyPage.new(browser)
    page.dx[1].code = 'a'
    page.dx[1].dos = 'b'
    page.dx[2].code = 'c'
    page.dx[2].dos = 'd'
    

    Unfortunately there is no built-in way for the populate_page_with method to work with indexed properties. As with anything, you could hack in something. The populate_page_with method looks for an "element" method as well as a setter method. By adding your own to the page object, the method could be used.

    class MyPage
      include PageObject
    
      indexed_property(:dx, [
        [:text_field, :code, {id: 'dx_%s_code'}],
        [:text_field, :dos, {id: 'dx_%s_dos'}],
      ])
    
      # Method for inputting the various dx code/dos values based on a Hash
      def dx=(values)
        values.each_pair do |index, fields|
          fields.each_pair do |field, value|
            dx[index].send("#{field}=", value)
          end
        end
      end
    
      # This is so that populate_page_with can check that the element is enabled/visible
      def dx_element
        dx[1].code_element
      end
    end
    

    This would give you the ability to use populate_page_with by sending a Hash where the keys are the index and the values are the fields/values for that index. The same inputting of the page that we did before can now be written as:

    page = MyPage.new(browser)
    page.populate_page_with(dx: {
      1 => {code: 'a', dos: 'b'},
      2 => {code: 'c', dos: 'd'}
    })