ruby - 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
解决方案
我认为您的问题的简短回答是否定的,没有内置支持将值传递给页面部分。但是,这里有一些我能想到的替代方案。
选项 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
推荐阅读
- kotlin - 圆角矩形坐标
- linux - 从 html 链接通过 xdg 打开文件浏览器
- performance - Element.innerHTML 运行成本太高,需要替代方案
- javascript - 我需要使用坐标数组画线
- c# - 如何将 HamburgerButtonInfo 的可见性绑定到 ShellViewModel 以外的另一个视图模型?
- python - 如何使用 matplotlib FuncAnimation 为热图设置动画?
- amazon-quicksight - 快速查看用户组并根据组过滤数据
- plone - id 属性中的 TALES 表达式
- uwp - XAML 中的 ObservableCollection 项目数量
- jquery - 通过 Jquery 从 Gmail 中的电子邮件中获取所有信息