cheezy / page-object

Gem to implement PageObject pattern in watir-webdriver and selenium-webdriver
MIT License
653 stars 220 forks source link

Hookable Accessors #463

Open Donavan opened 6 years ago

Donavan commented 6 years ago

I have a gem called Captain Hook that adds before/after hooks to arbitrary objects. I built it for the purpose of DRYing up my automation code. I have a gist up of at PageObject extension that defines a new set of "hookable" accessors. It works be replacing the "_element" method PageObject adds to return a Watir element wrapped by a Captain Hook "Hookable" wrapper (A small SimpleDelegator wrapper).

The gist is way bigger than the changes it makes. It's as big as it is because it needs to override the shortcut methods that bypass the Watir element if we've hooked the method being short circuited.

I've been testing it in a large project for the past few weeks ensuring it works as intended and is actually useful before going public. Originally I had some caching built in to avoid building the Hookable object each time we instantiated an element, but that turned out to be a terrible idea.

With it I can declare a set of hooks like so:

WFA_HOOKS ||= CptHook.define_hooks do
  after(:click).call(:wait_for_ajax)
  after(:set).call(:wait_for_ajax)
  after(:value=).call(:wait_for_ajax)
  after(:check).call(:wait_for_ajax)
  after(:uncheck).call(:wait_for_ajax)
end

Then use them when I declare the elements:

button_hooked(:cancel_dialog, data_bind: /cancelDialog/, hooks: WFO_HOOKS)

Hooks can be combined for composition:

  SEARCH_HOOKS ||= define_hooks do
    before(:value=).call(:ensure_search_element_visible)
  end
  text_field_hooked(:search, id: 'cmboJobReference', hooks: SEARCH_HOOKS.merge(WFA_HOOKS))

The DSL allows for more complicated hooks where parameters are passed as well as using procs/lambdas. It also has the notion of a "context" for the method call, allowing the hook to make calls on other objects.

  DASHBOARD_MENU_HOOKS ||= define_hooks do
    before(:click).call(:ensure_visible).with(:dashboard).using(:page)
  end

I'm opening this ticket to see if there's interest in merging this into PageObject itself, perhaps as the default accessors, or if I should continue with my original plan of making it part of a wider extension Gem.