watirpage-object-gem

How to extend page-object to include non-standard HTML Elements


I am looking for tutorials or help on how to extend the page-object gem (github.com/cheezy/page-object) to support elements like the Angular mat-chip. This element is very similar to a check box, but isn't recognized as such by page-object. The closest things Ive found are here.

https://williamjamespowell.com/2012/12/01/extending-pageobjects/

https://williamjamespowell.com/2012/12/27/gxt-pageobject-widget-example/

These examples are 8 years old and I don't know if they would still work. Unfortunately that is the extent of what Ive found on how to extend page-object functionality.

What I would like to do is create a mat-chip class and wire it up so that I could use that just like I would checkbox from page-object. I just can't quite figure out what all the pieces are that have to be wired up.


Solution

  • I'm not an Angular person, so I stole a clickable mat-chip example from https://stackoverflow.com/a/47962183/1200545. Depending on your implementation, you may need to tweak the code below.

    First off, Page-Object has a limitation when defining widgets that use a custom tag name - ie "mat-chip". To get around this, we will define an associated method/locator in Watir:

    module Watir
      module Container
        def mat_chip(*args)
          HTMLElement.new(self, extract_selector(args).merge(tag_name: "mat-chip"))
        end
        def mat_chips(*args)
          HTMLElementCollection.new(self, extract_selector(args).merge(tag_name: "mat-chip"))
        end
      end
    end
    

    We can then create a widget and accessors in Page-Object:

    class MatChip < PageObject::Elements::Element
      def self.accessor_methods(widget, name)
        widget.send('define_method', "check_#{name}") do
          self.send("#{name}_element").check
        end
    
        widget.send('define_method', "uncheck_#{name}") do
          self.send("#{name}_element").uncheck
        end
    
        widget.send('define_method', "#{name}_checked?") do
          self.send("#{name}_element").checked?
        end
      end
    
      def set(bool = true)
        click unless set? == bool
      end
      alias check set
    
      def clear
        set(false)
      end
      alias uncheck clear
    
      def set?
        attribute_value('aria-selected') == 'true'
      end
      alias checked? set?
    
      PageObject.register_widget :mat_chip, self, :mat_chip
    end
    

    You can then use the accessor, named "mat_chip", the same was as a checkbox:

    class MyPage
      include PageObject
    
      mat_chip('mychip', index: 1)
    end
    
    page = MyPage.new(browser)
    page.check_mychip
    p page.mychip_checked?
    #=> true
    page.uncheck_mychip
    p page.mychip_checked?
    #=> false
    

    Alternatively, you can use the mat-chip directly as a nested element:

    page.mat_chip_element.check
    p page.mat_chip_element.checked?
    #=> true
    page.mat_chip_element.uncheck
    p page.mat_chip_element.checked?
    #=> false