Search code examples
rubyangularpage-object-gem

Re-Usable Components in Cheezy Page Object


I'm using cheezy page-object gem and cucumber.

I have page objects for an angular website and many pages contain angular ng-select element which is a dropdown. All of the ng-select elements are the same format for each page. The only thing that changes is the data and the id of the ng-select. I'd like to build some re-usable ng-select component that I can put in my page-objects as I have quite a few methods I use on the element.

class NGSelectComponent
  include PageObject

  def wrapper(id)
    element(:element, tag_name: 'ng-select', id: id)
  end

  def wrapper_text_field
    wrapper.text_field_element
  end

  def wrapper_span
    wrapper.span_element(class: ['ng-value-label','ng-star-inserted'])
  end

  def wrapper_value
    wrapper_span.text
  end

  def wrapper_values
    wrapper.div_elements
  end
end

As you can see the wrapper method is the ng-select element and it takes an id for the locator hash. This is as far as I got. I saw something like this but it looks like it only works for HTML elements.

How can I turn this into a re-usable component using page-object gem? As a sidenote I call my page objects using the on() method in my step definitions. So for example on(SomePage). I felt like that matters for however the solution turns out.


Solution

  • Widgets and page sections are the 2 options for re-usable components. As you will likely want to setup getters/setters for the field, widgets are the better choice.

    The widget could be defined like:

    class NGSelectComponent < PageObject::Elements::Element
      def self.accessor_methods(accessor, name)   
        #
        # Define a getter
        #
        accessor.send(:define_method, "#{name}") do
          self.send("#{name}_element").value
        end
    
        #
        # Define a setter. Use "#{name}=" so that the widget can be used
        # in #populate_page_with
        # 
        #
        accessor.send(:define_method, "#{name}=") do |value|
          self.send("#{name}_element").set(value)
        end
      end
    
      def set(value)
        text_field_element.set(value)
      end
    
      def value
        text_field_element.value
      end
    
      def wrapper_text_field
        text_field_element
      end
    
      def wrapper_span
        span_element(class: ['ng-value-label','ng-star-inserted'])
      end
    
      def wrapper_value
        wrapper_span.text
      end
    
      def wrapper_values
        div_elements
      end
    
      PageObject.register_widget :ng_select, self, :element
    end
    

    Page objects would define the ng-select elements like any other accessor:

    class TestPage
      include PageObject
    
      ng_select(:name, id: 'name')
    end
    

    Giving the page a getter/setter for the field - eg:

    page = TestPage.new(browser)
    page.populate_page_with(name: 'My Name')
    p page.name
    #=> "My Name"