watir 5.0.0 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.document +5 -0
  3. data/.gitignore +21 -0
  4. data/.gitmodules +3 -0
  5. data/.travis.yml +35 -0
  6. data/CHANGES.md +1756 -0
  7. data/Gemfile +12 -0
  8. data/LICENSE +23 -0
  9. data/README.md +92 -0
  10. data/Rakefile +161 -32
  11. data/lib/watir.rb +127 -1
  12. data/lib/watir/after_hooks.rb +132 -0
  13. data/lib/watir/alert.rb +104 -0
  14. data/lib/watir/aliases.rb +6 -0
  15. data/lib/watir/atoms.rb +24 -0
  16. data/lib/watir/atoms/README +3 -0
  17. data/lib/watir/atoms/fireEvent.js +29 -0
  18. data/lib/watir/atoms/getAttribute.js +18 -0
  19. data/lib/watir/atoms/getInnerHtml.js +18 -0
  20. data/lib/watir/atoms/getOuterHtml.js +18 -0
  21. data/lib/watir/atoms/getParentElement.js +17 -0
  22. data/lib/watir/atoms/selectText.js +61 -0
  23. data/lib/watir/attribute_helper.rb +98 -0
  24. data/lib/watir/browser.rb +346 -0
  25. data/lib/watir/cell_container.rb +25 -0
  26. data/lib/watir/container.rb +51 -0
  27. data/lib/watir/cookies.rb +132 -0
  28. data/lib/watir/element_collection.rb +126 -0
  29. data/lib/watir/elements/area.rb +12 -0
  30. data/lib/watir/elements/button.rb +37 -0
  31. data/lib/watir/elements/cell.rb +17 -0
  32. data/lib/watir/elements/checkbox.rb +54 -0
  33. data/lib/watir/elements/dlist.rb +12 -0
  34. data/lib/watir/elements/element.rb +646 -0
  35. data/lib/watir/elements/file_field.rb +41 -0
  36. data/lib/watir/elements/font.rb +11 -0
  37. data/lib/watir/elements/form.rb +17 -0
  38. data/lib/watir/elements/hidden.rb +20 -0
  39. data/lib/watir/elements/html_elements.rb +2063 -0
  40. data/lib/watir/elements/iframe.rb +163 -0
  41. data/lib/watir/elements/image.rb +62 -0
  42. data/lib/watir/elements/input.rb +7 -0
  43. data/lib/watir/elements/link.rb +18 -0
  44. data/lib/watir/elements/option.rb +74 -0
  45. data/lib/watir/elements/radio.rb +42 -0
  46. data/lib/watir/elements/row.rb +17 -0
  47. data/lib/watir/elements/select.rb +238 -0
  48. data/lib/watir/elements/svg_elements.rb +667 -0
  49. data/lib/watir/elements/table.rb +42 -0
  50. data/lib/watir/elements/table_cell.rb +6 -0
  51. data/lib/watir/elements/table_row.rb +15 -0
  52. data/lib/watir/elements/table_section.rb +15 -0
  53. data/lib/watir/elements/text_area.rb +5 -0
  54. data/lib/watir/elements/text_field.rb +37 -0
  55. data/lib/watir/exception.rb +17 -0
  56. data/lib/watir/extensions/nokogiri.rb +14 -0
  57. data/lib/watir/extensions/select_text.rb +10 -0
  58. data/lib/watir/generator.rb +3 -0
  59. data/lib/watir/generator/base.rb +11 -0
  60. data/lib/watir/generator/base/generator.rb +115 -0
  61. data/lib/watir/generator/base/idl_sorter.rb +47 -0
  62. data/lib/watir/generator/base/spec_extractor.rb +138 -0
  63. data/lib/watir/generator/base/util.rb +21 -0
  64. data/lib/watir/generator/base/visitor.rb +157 -0
  65. data/lib/watir/generator/html.rb +15 -0
  66. data/lib/watir/generator/html/generator.rb +36 -0
  67. data/lib/watir/generator/html/spec_extractor.rb +50 -0
  68. data/lib/watir/generator/html/visitor.rb +21 -0
  69. data/lib/watir/generator/svg.rb +7 -0
  70. data/lib/watir/generator/svg/generator.rb +38 -0
  71. data/lib/watir/generator/svg/spec_extractor.rb +46 -0
  72. data/lib/watir/generator/svg/visitor.rb +21 -0
  73. data/lib/watir/has_window.rb +53 -0
  74. data/lib/watir/locators.rb +22 -0
  75. data/lib/watir/locators/button/locator.rb +38 -0
  76. data/lib/watir/locators/button/selector_builder.rb +27 -0
  77. data/lib/watir/locators/button/selector_builder/xpath.rb +29 -0
  78. data/lib/watir/locators/button/validator.rb +15 -0
  79. data/lib/watir/locators/cell/locator.rb +17 -0
  80. data/lib/watir/locators/cell/selector_builder.rb +24 -0
  81. data/lib/watir/locators/element/locator.rb +249 -0
  82. data/lib/watir/locators/element/selector_builder.rb +147 -0
  83. data/lib/watir/locators/element/selector_builder/css.rb +65 -0
  84. data/lib/watir/locators/element/selector_builder/xpath.rb +72 -0
  85. data/lib/watir/locators/element/validator.rb +23 -0
  86. data/lib/watir/locators/row/locator.rb +17 -0
  87. data/lib/watir/locators/row/selector_builder.rb +29 -0
  88. data/lib/watir/locators/text_area/locator.rb +13 -0
  89. data/lib/watir/locators/text_area/selector_builder.rb +22 -0
  90. data/lib/watir/locators/text_field/locator.rb +44 -0
  91. data/lib/watir/locators/text_field/selector_builder.rb +34 -0
  92. data/lib/watir/locators/text_field/selector_builder/xpath.rb +19 -0
  93. data/lib/watir/locators/text_field/validator.rb +20 -0
  94. data/lib/watir/row_container.rb +36 -0
  95. data/lib/watir/screenshot.rb +50 -0
  96. data/lib/watir/user_editable.rb +38 -0
  97. data/lib/watir/version.rb +3 -3
  98. data/lib/watir/wait.rb +250 -0
  99. data/lib/watir/wait/timer.rb +19 -0
  100. data/lib/watir/window.rb +244 -0
  101. data/lib/watir/xpath_support.rb +20 -0
  102. data/spec/always_locate_spec.rb +43 -0
  103. data/spec/browser_spec.rb +130 -0
  104. data/spec/click_spec.rb +19 -0
  105. data/spec/container_spec.rb +34 -0
  106. data/spec/element_locator_spec.rb +532 -0
  107. data/spec/element_spec.rb +136 -0
  108. data/spec/implementation.rb +216 -0
  109. data/spec/input_spec.rb +14 -0
  110. data/spec/locator_spec_helper.rb +57 -0
  111. data/spec/spec_helper.rb +35 -0
  112. data/spec/special_chars_spec.rb +13 -0
  113. data/support/doctest_helper.rb +78 -0
  114. data/support/travis.sh +44 -0
  115. data/support/version_differ.rb +59 -0
  116. data/watir.gemspec +37 -25
  117. metadata +288 -23
  118. data/lib/watir/loader.rb +0 -64
@@ -0,0 +1,22 @@
1
+ require 'watir/locators/element/locator'
2
+ require 'watir/locators/element/selector_builder'
3
+ require 'watir/locators/element/validator'
4
+
5
+ require 'watir/locators/button/locator'
6
+ require 'watir/locators/button/selector_builder'
7
+ require 'watir/locators/button/selector_builder/xpath'
8
+ require 'watir/locators/button/validator'
9
+
10
+ require 'watir/locators/cell/locator'
11
+ require 'watir/locators/cell/selector_builder'
12
+
13
+ require 'watir/locators/row/locator'
14
+ require 'watir/locators/row/selector_builder'
15
+
16
+ require 'watir/locators/text_area/locator'
17
+ require 'watir/locators/text_area/selector_builder'
18
+
19
+ require 'watir/locators/text_field/locator'
20
+ require 'watir/locators/text_field/selector_builder'
21
+ require 'watir/locators/text_field/selector_builder/xpath'
22
+ require 'watir/locators/text_field/validator'
@@ -0,0 +1,38 @@
1
+ module Watir
2
+ module Locators
3
+ class Button
4
+ class Locator < Element::Locator
5
+ def locate_all
6
+ find_all_by_multiple
7
+ end
8
+
9
+ private
10
+
11
+ def wd_find_first_by(how, what)
12
+ if how == :tag_name
13
+ how = :xpath
14
+ what = ".//button | .//input[#{selector_builder.xpath_builder.attribute_expression(:input, type: Watir::Button::VALID_TYPES)}]"
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ def can_convert_regexp_to_contains?
21
+ # regexp conversion won't work with the complex xpath selector
22
+ false
23
+ end
24
+
25
+ def matches_selector?(element, selector)
26
+ if selector.key?(:value)
27
+ copy = selector.dup
28
+ value = copy.delete(:value)
29
+
30
+ super(element, copy) && (value === fetch_value(element, :value) || value === fetch_value(element, :text))
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ module Watir
2
+ module Locators
3
+ class Button
4
+ class SelectorBuilder < Element::SelectorBuilder
5
+ def build_wd_selector(selectors)
6
+ return if selectors.values.any? { |e| e.kind_of? Regexp }
7
+
8
+ selectors.delete(:tag_name) || raise("internal error: no tag_name?!")
9
+
10
+ button_attr_exp = xpath_builder.attribute_expression(:button, selectors)
11
+
12
+ selectors[:type] = Watir::Button::VALID_TYPES
13
+ input_attr_exp = xpath_builder.attribute_expression(:input, selectors)
14
+
15
+ xpath = ".//button"
16
+ xpath << "[#{button_attr_exp}]" unless button_attr_exp.empty?
17
+ xpath << " | .//input"
18
+ xpath << "[#{input_attr_exp}]"
19
+
20
+ p build_wd_selector: xpath if $DEBUG
21
+
22
+ [:xpath, xpath]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module Watir
2
+ module Locators
3
+ class Button
4
+ class SelectorBuilder
5
+ class XPath < Element::SelectorBuilder::XPath
6
+ def lhs_for(building, key)
7
+ if building == :input && key == :text
8
+ "@value"
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def equal_pair(building, key, value)
17
+ if building == :button && key == :value
18
+ # :value should look for both node text and @value attribute
19
+ text = XpathSupport.escape(value)
20
+ "(text()=#{text} or @value=#{text})"
21
+ else
22
+ super
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Watir
2
+ module Locators
3
+ class Button
4
+ class Validator < Element::Validator
5
+ def validate(element, selector)
6
+ return unless %w[input button].include?(element.tag_name.downcase)
7
+ # TODO - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
8
+ return if element.tag_name.downcase == "input" && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
9
+
10
+ element
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Watir
2
+ module Locators
3
+ class Cell
4
+ class Locator < Element::Locator
5
+ def locate_all
6
+ find_all_by_multiple
7
+ end
8
+
9
+ private
10
+
11
+ def by_id
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Watir
2
+ module Locators
3
+ class Cell
4
+ class SelectorBuilder < Element::SelectorBuilder
5
+ def build_wd_selector(selectors)
6
+ return if selectors.values.any? { |e| e.kind_of? Regexp }
7
+
8
+ expressions = %w[./th ./td]
9
+ attr_expr = xpath_builder.attribute_expression(nil, selectors)
10
+
11
+ unless attr_expr.empty?
12
+ expressions.map! { |e| "#{e}[#{attr_expr}]" }
13
+ end
14
+
15
+ xpath = expressions.join(" | ")
16
+
17
+ p build_wd_selector: xpath if $DEBUG
18
+
19
+ [:xpath, xpath]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,249 @@
1
+ module Watir
2
+ module Locators
3
+ class Element
4
+ class Locator
5
+ attr_reader :selector_builder
6
+ attr_reader :element_validator
7
+
8
+ WD_FINDERS = [
9
+ :class,
10
+ :class_name,
11
+ :css,
12
+ :id,
13
+ :link,
14
+ :link_text,
15
+ :name,
16
+ :partial_link_text,
17
+ :tag_name,
18
+ :xpath
19
+ ]
20
+
21
+ # Regular expressions that can be reliably converted to xpath `contains`
22
+ # expressions in order to optimize the .
23
+ CONVERTABLE_REGEXP = %r{
24
+ \A
25
+ ([^\[\]\\^$.|?*+()]*) # leading literal characters
26
+ [^|]*? # do not try to convert expressions with alternates
27
+ ([^\[\]\\^$.|?*+()]*) # trailing literal characters
28
+ \z
29
+ }x
30
+
31
+ def initialize(parent, selector, selector_builder, element_validator)
32
+ @parent = parent # either element or browser
33
+ @selector = selector.dup
34
+ @selector_builder = selector_builder
35
+ @element_validator = element_validator
36
+ end
37
+
38
+ def locate
39
+ e = by_id and return e # short-circuit if :id is given
40
+
41
+ if @selector.size == 1
42
+ element = find_first_by_one
43
+ else
44
+ element = find_first_by_multiple
45
+ end
46
+
47
+ # This actually only applies when finding by xpath/css - browser.text_field(:xpath, "//input[@type='radio']")
48
+ # We don't need to validate the element if we built the xpath ourselves.
49
+ # It is also used to alter behavior of methods locating more than one type of element
50
+ # (e.g. text_field locates both input and textarea)
51
+ element_validator.validate(element, @selector) if element
52
+ rescue Selenium::WebDriver::Error::NoSuchElementError, Selenium::WebDriver::Error::StaleElementReferenceError
53
+ nil
54
+ end
55
+
56
+ def locate_all
57
+ if @selector.size == 1
58
+ find_all_by_one
59
+ else
60
+ find_all_by_multiple
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def by_id
67
+ return unless id = @selector[:id] and id.is_a? String
68
+
69
+ selector = @selector.dup
70
+ selector.delete(:id)
71
+
72
+ tag_name = selector.delete(:tag_name)
73
+ return unless selector.empty? # multiple attributes
74
+
75
+ element = @parent.wd.find_element(:id, id)
76
+ return if tag_name && !element_validator.validate(element, selector)
77
+
78
+ element
79
+ end
80
+
81
+ def find_first_by_one
82
+ how, what = @selector.to_a.first
83
+ selector_builder.check_type(how, what)
84
+
85
+ if WD_FINDERS.include?(how)
86
+ wd_find_first_by(how, what)
87
+ else
88
+ find_first_by_multiple
89
+ end
90
+ end
91
+
92
+ def find_first_by_multiple
93
+ selector = selector_builder.normalized_selector
94
+
95
+ idx = selector.delete(:index)
96
+ how, what = selector_builder.build(selector)
97
+
98
+ if how
99
+ # could build xpath/css for selector
100
+ if idx
101
+ @parent.wd.find_elements(how, what)[idx]
102
+ else
103
+ @parent.wd.find_element(how, what)
104
+ end
105
+ else
106
+ # can't use xpath, probably a regexp in there
107
+ if idx
108
+ wd_find_by_regexp_selector(selector, :select)[idx]
109
+ else
110
+ wd_find_by_regexp_selector(selector, :find)
111
+ end
112
+ end
113
+ end
114
+
115
+ def find_all_by_one
116
+ how, what = @selector.to_a.first
117
+ selector_builder.check_type how, what
118
+
119
+ if WD_FINDERS.include?(how)
120
+ wd_find_all_by(how, what)
121
+ else
122
+ find_all_by_multiple
123
+ end
124
+ end
125
+
126
+ def find_all_by_multiple
127
+ selector = selector_builder.normalized_selector
128
+
129
+ if selector.key? :index
130
+ raise ArgumentError, "can't locate all elements by :index"
131
+ end
132
+
133
+ how, what = selector_builder.build(selector)
134
+ if how
135
+ @parent.wd.find_elements(how, what)
136
+ else
137
+ wd_find_by_regexp_selector(selector, :select)
138
+ end
139
+ end
140
+
141
+ def wd_find_all_by(how, what)
142
+ if what.is_a? String
143
+ @parent.wd.find_elements(how, what)
144
+ else
145
+ all_elements.select { |element| fetch_value(element, how) =~ what }
146
+ end
147
+ end
148
+
149
+ def fetch_value(element, how)
150
+ case how
151
+ when :text
152
+ element.text
153
+ when :tag_name
154
+ element.tag_name.downcase
155
+ when :href
156
+ (href = element.attribute(:href)) && href.strip
157
+ else
158
+ element.attribute(how.to_s.tr("_", "-").to_sym)
159
+ end
160
+ end
161
+
162
+ def all_elements
163
+ @parent.wd.find_elements(xpath: ".//*")
164
+ end
165
+
166
+ def wd_find_first_by(how, what)
167
+ if what.is_a? String
168
+ @parent.wd.find_element(how, what)
169
+ else
170
+ all_elements.find { |element| fetch_value(element, how) =~ what }
171
+ end
172
+ end
173
+
174
+ def wd_find_by_regexp_selector(selector, method = :find)
175
+ parent = @parent.wd
176
+ rx_selector = delete_regexps_from(selector)
177
+
178
+ if rx_selector.key?(:label) && selector_builder.should_use_label_element?
179
+ label = label_from_text(rx_selector.delete(:label)) || return
180
+ if (id = label.attribute(:for))
181
+ selector[:id] = id
182
+ else
183
+ parent = label
184
+ end
185
+ end
186
+
187
+ how, what = selector_builder.build(selector)
188
+
189
+ unless how
190
+ raise Error, "internal error: unable to build Selenium selector from #{selector.inspect}"
191
+ end
192
+
193
+ if how == :xpath && can_convert_regexp_to_contains?
194
+ rx_selector.each do |key, value|
195
+ next if key == :tag_name || key == :text
196
+
197
+ predicates = regexp_selector_to_predicates(key, value)
198
+ what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
199
+ end
200
+ end
201
+
202
+ elements = parent.find_elements(how, what)
203
+ elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
204
+ end
205
+
206
+ def delete_regexps_from(selector)
207
+ rx_selector = {}
208
+
209
+ selector.dup.each do |how, what|
210
+ next unless what.is_a?(Regexp)
211
+ rx_selector[how] = what
212
+ selector.delete how
213
+ end
214
+
215
+ rx_selector
216
+ end
217
+
218
+ def label_from_text(label_exp)
219
+ # TODO: this won't work correctly if @wd is a sub-element
220
+ @parent.wd.find_elements(:tag_name, 'label').find do |el|
221
+ matches_selector?(el, text: label_exp)
222
+ end
223
+ end
224
+
225
+ def matches_selector?(element, selector)
226
+ selector.all? do |how, what|
227
+ what === fetch_value(element, how)
228
+ end
229
+ end
230
+
231
+ def can_convert_regexp_to_contains?
232
+ true
233
+ end
234
+
235
+ def regexp_selector_to_predicates(key, re)
236
+ return [] if re.casefold?
237
+
238
+ match = re.source.match(CONVERTABLE_REGEXP)
239
+ return [] unless match
240
+
241
+ lhs = selector_builder.xpath_builder.lhs_for(nil, key)
242
+ match.captures.reject(&:empty?).map do |literals|
243
+ "contains(#{lhs}, #{XpathSupport.escape(literals)})"
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,147 @@
1
+ require 'watir/locators/element/selector_builder/css'
2
+ require 'watir/locators/element/selector_builder/xpath'
3
+
4
+ module Watir
5
+ module Locators
6
+ class Element
7
+ class SelectorBuilder
8
+ VALID_WHATS = [String, Regexp]
9
+ WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
10
+
11
+ def initialize(parent, selector, valid_attributes)
12
+ @parent = parent # either element or browser
13
+ @selector = selector
14
+ @valid_attributes = valid_attributes
15
+ end
16
+
17
+ def normalized_selector
18
+ selector = {}
19
+
20
+ @selector.each do |how, what|
21
+ check_type(how, what)
22
+
23
+ how, what = normalize_selector(how, what)
24
+ selector[how] = what
25
+ end
26
+
27
+ selector
28
+ end
29
+
30
+ def check_type(how, what)
31
+ case how
32
+ when :index
33
+ unless what.is_a?(Fixnum)
34
+ raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}"
35
+ end
36
+ else
37
+ unless VALID_WHATS.any? { |t| what.is_a? t }
38
+ raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
39
+ end
40
+ end
41
+ end
42
+
43
+ def should_use_label_element?
44
+ !valid_attribute?(:label)
45
+ end
46
+
47
+ def build(selector)
48
+ given_xpath_or_css(selector) || build_wd_selector(selector)
49
+ end
50
+
51
+ def xpath_builder
52
+ @xpath_builder ||= xpath_builder_class.new(should_use_label_element?)
53
+ end
54
+
55
+ private
56
+
57
+ def normalize_selector(how, what)
58
+ case how
59
+ when :tag_name, :text, :xpath, :index, :class, :label, :css
60
+ # include :class since the valid attribute is 'class_name'
61
+ # include :for since the valid attribute is 'html_for'
62
+ [how, what]
63
+ when :class_name
64
+ [:class, what]
65
+ when :caption
66
+ [:text, what]
67
+ else
68
+ assert_valid_as_attribute how
69
+ [how, what]
70
+ end
71
+ end
72
+
73
+ def assert_valid_as_attribute(attribute)
74
+ return if valid_attribute?(attribute) || attribute.to_s =~ WILDCARD_ATTRIBUTE
75
+ raise Exception::MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
76
+ end
77
+
78
+ def given_xpath_or_css(selector)
79
+ xpath = selector.delete(:xpath)
80
+ css = selector.delete(:css)
81
+ return unless xpath || css
82
+
83
+ if xpath && css
84
+ raise ArgumentError, ":xpath and :css cannot be combined (#{selector.inspect})"
85
+ end
86
+
87
+ how, what = if xpath
88
+ [:xpath, xpath]
89
+ elsif css
90
+ [:css, css]
91
+ end
92
+
93
+ if selector.any? && !can_be_combined_with_xpath_or_css?(selector)
94
+ raise ArgumentError, "#{how} cannot be combined with other selectors (#{selector.inspect})"
95
+ end
96
+
97
+ [how, what]
98
+ end
99
+
100
+ def build_wd_selector(selectors)
101
+ unless selectors.values.any? { |e| e.is_a? Regexp }
102
+ build_css(selectors) || build_xpath(selectors)
103
+ end
104
+ end
105
+
106
+ def valid_attribute?(attribute)
107
+ @valid_attributes && @valid_attributes.include?(attribute)
108
+ end
109
+
110
+ def can_be_combined_with_xpath_or_css?(selector)
111
+ keys = selector.keys
112
+ return true if keys == [:tag_name]
113
+
114
+ if selector[:tag_name] == "input"
115
+ return keys.sort == [:tag_name, :type]
116
+ end
117
+
118
+ false
119
+ end
120
+
121
+ def build_xpath(selectors)
122
+ xpath_builder.build(selectors)
123
+ end
124
+
125
+ def build_css(selectors)
126
+ css_builder.build(selectors)
127
+ end
128
+
129
+ def xpath_builder_class
130
+ Kernel.const_get("#{self.class.name}::XPath")
131
+ rescue
132
+ XPath
133
+ end
134
+
135
+ def css_builder
136
+ @css_builder ||= css_builder_class.new
137
+ end
138
+
139
+ def css_builder_class
140
+ Kernel.const_get("#{self.class.name}::CSS")
141
+ rescue
142
+ CSS
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end