oz icon indicating copy to clipboard operation
oz copied to clipboard

[Feature Evaluation] Wrapping defined driver elements

Open Castone22 opened this issue 7 years ago • 1 comments

Okay, as per the discussion @greenarrowdb and I had in discord the other day, i'm going to document my thoughts here. Pardon the conscious stream of thought, it's just how things tend to live in my head.

I'm finding the hard ban on watir elements in the page models to be a lot more limiting than i originally thought and i'm finding that it causes me to have to.. A. Reinvent the wheel B. Write somewhat confusing code

Some examples might be a bit more telling here... Say i wanted to define a navigation bar for github. I'm not going to use the best selectors here because they wouldn't be available in the model i'm testing against. If i wanted to get a given nav link in watir

class Github
  class BasePage
    @header_bar = @browser.a(class: 'header-logo-invertocat').parent.parent.parent.parent
    @nav_links = @header_bar.links

    def visit(option)
      nav_link(option).click
    end

    def nav_link(option)
      @nav_links.find{|it| it.text =~ /option/i}
    end
  end
end

Oz's paradigm for this gets a lot less readable really quickly, one reason being that i'd have to use an xpath to find that header bar, another being that i end up with a lot of replication, and i have to statically define each element, since post processing availability is currently very limited being that the page models don't allow me to easily request a sub element... anonymously for lack of a better word.

class Github
  class BasePage < CorePage
    
    def create_common_elements
      @header_bar = add_element(:header_bar, element_type: :header, CoreElement, xpath: '//a[@class="header-logo-invertocat"]/../../../header') #this may not be exactly correct
      @header_nav_links = []
      @header_nav_links << add_link(:header_bar_pull_requests, parent: @header_bar, xpath: '//a[1]')
      @header_nav_links << add_link(:header_bar_issues, parent: @header_bar, xpath: '//a[2]')
      @header_nav_links << add_link(:header_bar_marketplace, parent: @header_bar, xpath: '//a[3]')
      @header_nav_links << add_link(:header_bar_explore, parent: @header_bar, xpath: '//a[4]')
    end

    def visit(option)
      click_on nav_link(option)
    end

    def nav_link(option)
      @header_nav_links.find{|it| it[:name].to_s =~ /option.gsub('_','')/i}
    end
  end
end

Granted, I understand that visit as a method isn't something that's typically used on the external page api in this case (i'm using it to ease defining a route, which i think should fit within the expected behavior). The Oz way just feels... really clunky to me as is.

With all of this background i can finally get to my point i think. I don't necessarily fully believe that driver elements don't have their place in a given page model. They would allow for much easier definition of structural objects on the page (things that are only really used to navigate the dom and find other elements more easily) and let us get to the stuff we actually care about validating much more easily. Something like the below example would feel pretty nice to me.

class Github
  class BasePage < CorePage
    add_route('Github::PullRequests', [:click_on, :pull_request_link])

    def create_elements
      @header_bar = browser.a(class: 'header-logo-invertocat').parent.parent.parent.parent
      add_element(:header_bar, driver_element: @header_bar)
      create_nav_links
    end

    def create_nav_links
      @header_bar.links.each do |link_element|
        add_link(link_element.text.to_field_name, driver_element: link_element)
      end
    end
  end
end

This has a few benefits, two big ones being

  • reduces maintenance and repetition on the page model quite a bit.
    • think if a new link gets added to the nav bar, now all we have to do is define a new route for it at the top of the page
  • still maintains the existing api where we get easily defined checks on elements

Some of the immediate cons I can think of though.

  • increases coupling to whatever driver you're using to develop
  • might get kinda brittle (what happens with those symbol links on the right side of githubs header?)

Castone22 avatar Dec 03 '18 18:12 Castone22

After talking to uriah, i had a few more ideas how this might work

add_from_watir_element(:name, options) do
  browser.div(id: id).parent.select_list
end

Sometimes this just gets way cleaner than an xpath

Castone22 avatar Jul 16 '19 14:07 Castone22