首页 > 解决方案 > Ruby PageObject Design for Similar Page Sections

问题描述

I'm using the Cheezy Page Object gem (which also means I'm using Watir, which also means I'm using Selenium). I also have the watir gem explicitly loaded.

Anyway I have a site I am modeling with the UI written in angular where there is 1 page whose contents change based on dropdown selection. The page has several sections but it is visibly the same for each dropdown choice. The only difference is the xpath locators I am using to get there (there's no unique ID on the sections).

So for example I have an xpath like html/body/div[1]/div/div[1]/div/**green**/div/div[1]

and another like

html/body/div[1]/div/div[1]/div/**red**/div/div[1]

The elements on the sections strangely all have the same ID attribute and same class name. So I've been using xpath for the elements since that appears to make it a unique locator.

Problem is there are currently seven dropdown choices each with several sections like this. And they have visibly same elements and structure (from end user perspective) but when you look at html the only difference is the locator so like this for the elements:

html/body/div[1]/div/div[1]/div/green/div/div[1]/**<element>**

and another like

html/body/div[1]/div/div[1]/div/red/div/div[1]/**<element>**

In my current design I have created one page and created page sections for each section on a page. Multiply the number of page sections with number of dropdown choices and you see it is alot. Some of the choices do generate extra elements but there are still common elements between all sections. I also have to duplicate all of these elements across the seven different pages because the xpath is different. Is there some way for me to pass some initializer to the PageObject page_section like the type-a or type-b string and then based on that I can also choose correct xpath for all elements?

So like if I have text field like so in like a base page object page_section:

text_field(:team, xpath: "...#{type_variable}")

Can I do something like section = SomePageObject.page_section_name(type_variable)?

EDIT: Adding Page Object code per request

class BasePO
  include PageObject

  #Option S1 Cards
  page_section(:options_red_card, OptionRedCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red/div[2]/div[2]/div/div/div/div")
  page_section(:options_green_card, OptionGreenCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/green/div[2]/div[2]/div/div/div/div")
  page_section(:options_yellow_card, OptionYellowCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/yellow/div[2]/div[2]/div/div/div/div")

  #Detail S2 Cards
  page_section(:detail_red_card, DetailRedCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red/div[1]/div/div/div")
  page_section(:detail_green_card, DetailGreenCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/green/div[1]/div/div/div")
  page_section(:detail_yellow_card, DetailYellowCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/yellow/div[1]/div/div/div")

end

EDIT2: Adding page_section content per request. All Option Cards share these elements at a minimum. Different elements in the Detail Cards but same structure as Option Cards.

class OptionRedCard
  include PageObject

  def field1_limit
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[2]/td[2]/div/div[1]/div/currency/div/input")
  end

  def field1_agg
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[2]/td[2]/div/div[2]/div/currency/div/input")
  end

  def field2_limit
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[3]/td[2]/div/div[1]/div/currency/div/input")
  end

  def field2_agg
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[3]/td[2]/div/div[2]/div/currency/div/input")
  end

  def field3_limit
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[4]/td[2]/div/div[1]/div/currency/div/input")
  end

  def field3_agg
    text_field_element(xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red-unit/div[2]/div[2]/div/div/div/red/form/div/div/div/table/tbody/tr[4]/td[2]/div/div[2]/div/currency/div/input")
  end

  def field1_agg_value
    field1_agg.attribute_value('data-value')
  end

  def field2_agg_value
    field2_agg.attribute_value('data-value')
  end

  def field3_agg_value
    field3_agg.attribute_value('data-value')
  end

end

标签: rubyangularseleniumwatirpage-object-gem

解决方案


我认为您的问题的简短回答是否定的,没有内置支持将值传递给页面部分。但是,这里有一些我能想到的替代方案。

选项 1 - 使用 initialize_accessors

通常访问器在编译时执行。但是,您可以使用 #initialize_accessors 方法将执行推迟到页面对象(或部分)的初始化。这将允许您在基类中定义访问器,该基类在初始化时将颜色类型插入路径:

class BaseCard
  include PageObject

  def initialize_accessors
    # Accessors defined with placeholder for the color type
    self.class.text_field(:field1_limit, xpath: "/html/body/some/path/#{color_type}/more/path/input")      
  end
end

# Each card class would define its color for substitution into the accessors
class OptionRedCard < BaseCard
  def color_type
    'red'
  end
end

class OptionGreenCard < BaseCard
  def color_type
    'green'
  end
end

class BasePO
  include PageObject

  page_section(:options_red_card, OptionRedCard, xpath: '/html/body/path')
  page_section(:options_green_card, OptionGreenCard, xpath: '/html/body/path')
end

选项 2 - 使用相对路径

我建议的方法是使用相对路径,以便可以从页面部分的路径中删除颜色。根据提供的对象,您可能可以执行以下操作:

class OptionCard
  include PageObject

  element(:unit) { following_sibling(tag_name: "#{root.tag_name}-unit") }
  div(:field1_limit) { unit_element.tr(index: 1).text_field(index: 0) }
  div(:field1_agg) { unit_element.tr(index: 1).text_field(index: 1) }
  div(:field2_limit) { unit_element.tr(index: 2).text_field(index: 0) }
  div(:field2_agg) { unit_element.tr(index: 2).text_field(index: 1) }
end

class BasePO
  include PageObject

  # Page sections only defined to the top most element of the section (the color element)
  page_section(:options_red_card, OptionCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/red")
  page_section(:options_green_card, OptionCard, xpath: "/html/body/app-component/app-page/div[2]/div/div/div[1]/div/div/div/ngb-tabset/div/div/green")
end

推荐阅读