watir 6.15.1 → 6.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -2
  3. data/.travis.yml +2 -0
  4. data/CHANGES.md +13 -0
  5. data/Rakefile +6 -0
  6. data/lib/watir.rb +1 -0
  7. data/lib/watir/browser.rb +4 -1
  8. data/lib/watir/element_collection.rb +27 -17
  9. data/lib/watir/elements/element.rb +41 -14
  10. data/lib/watir/elements/iframe.rb +3 -1
  11. data/lib/watir/elements/radio.rb +7 -2
  12. data/lib/watir/elements/select.rb +1 -0
  13. data/lib/watir/locators.rb +21 -21
  14. data/lib/watir/locators/button/matcher.rb +40 -0
  15. data/lib/watir/locators/cell/selector_builder.rb +3 -0
  16. data/lib/watir/locators/element/locator.rb +29 -172
  17. data/lib/watir/locators/element/matcher.rb +127 -0
  18. data/lib/watir/locators/element/selector_builder.rb +69 -23
  19. data/lib/watir/locators/element/selector_builder/xpath.rb +3 -10
  20. data/lib/watir/locators/row/selector_builder.rb +5 -5
  21. data/lib/watir/locators/text_area/selector_builder.rb +0 -14
  22. data/lib/watir/locators/text_area/selector_builder/xpath.rb +2 -2
  23. data/lib/watir/locators/text_field/matcher.rb +38 -0
  24. data/lib/watir/radio_set.rb +28 -31
  25. data/lib/watir/scroll.rb +69 -0
  26. data/lib/watir/version.rb +1 -1
  27. data/spec/locator_spec_helper.rb +58 -14
  28. data/spec/unit/element_locator_spec.rb +46 -591
  29. data/spec/unit/match_elements/button_spec.rb +80 -0
  30. data/spec/unit/match_elements/element_spec.rb +368 -0
  31. data/spec/unit/match_elements/text_field_spec.rb +79 -0
  32. data/spec/unit/selector_builder/anchor_spec.rb +51 -0
  33. data/spec/unit/selector_builder/button_spec.rb +206 -0
  34. data/spec/unit/selector_builder/cell_spec.rb +63 -0
  35. data/spec/unit/selector_builder/element_spec.rb +744 -0
  36. data/spec/unit/selector_builder/row_spec.rb +111 -0
  37. data/spec/unit/selector_builder/text_field_spec.rb +189 -0
  38. data/spec/unit/selector_builder/textarea_spec.rb +25 -0
  39. data/spec/watirspec/browser_spec.rb +7 -8
  40. data/spec/watirspec/element_hidden_spec.rb +1 -2
  41. data/spec/watirspec/elements/element_spec.rb +52 -16
  42. data/spec/watirspec/elements/iframe_spec.rb +1 -1
  43. data/spec/watirspec/elements/select_list_spec.rb +1 -1
  44. data/spec/watirspec/html/obscured.html +3 -1
  45. data/spec/watirspec/html/scroll.html +32 -0
  46. data/spec/watirspec/relaxed_locate_spec.rb +6 -1
  47. data/spec/watirspec/scroll_spec.rb +106 -0
  48. data/spec/watirspec/support/rspec_matchers.rb +2 -0
  49. data/spec/watirspec/wait_spec.rb +1 -1
  50. data/watir.gemspec +2 -4
  51. metadata +36 -33
  52. data/lib/watir/locators/button/locator.rb +0 -32
  53. data/lib/watir/locators/button/validator.rb +0 -17
  54. data/lib/watir/locators/cell/locator.rb +0 -13
  55. data/lib/watir/locators/element/validator.rb +0 -11
  56. data/lib/watir/locators/row/locator.rb +0 -13
  57. data/lib/watir/locators/text_field/locator.rb +0 -31
  58. data/lib/watir/locators/text_field/validator.rb +0 -13
  59. data/spec/unit/anchor_locator_spec.rb +0 -68
  60. data/spec/watirspec/selector_builder/button_spec.rb +0 -250
  61. data/spec/watirspec/selector_builder/cell_spec.rb +0 -92
  62. data/spec/watirspec/selector_builder/element_spec.rb +0 -628
  63. data/spec/watirspec/selector_builder/row_spec.rb +0 -148
  64. data/spec/watirspec/selector_builder/text_spec.rb +0 -199
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c5ac0f3aeeeec1422ef300b30ffc292113a053e
4
- data.tar.gz: a12b41e42257e86152ffb7e8b3c4e5777e657acf
3
+ metadata.gz: 63d608d81ce067739aacf522e62047450f0c0599
4
+ data.tar.gz: 135b27602f2a190dd6aa15401d93ecb0fbbd68a9
5
5
  SHA512:
6
- metadata.gz: 057b9175188bef9121cb6bd5970db44ef90119e57e0c910c4fcec06762f66b9a8f8eebccc627826da31ba40bba0cf999fcf4110d07273b120879f3e93a3f7112
7
- data.tar.gz: c7e93f0e181b4c11d3b20d7bd91e5a9f3fb4bb0fe8c0a98ad49a8daee676cf8fa35cb41c5d622714c6ee4c5dfc991b6dfbf010bd885b132e55d25d4d7df8c276
6
+ metadata.gz: b294891e79600c34327a05b06f01929f136a18400695b7ff03428ed264c8f66c4fae06e48e7879d691eac491029af5913ba275515eb319596c410f1cc98ba6c9
7
+ data.tar.gz: f258febcb8fbe41ff1ceacb3c96ada0ef27a8d19787e51bc45dd2d8f1901a64f5b76047a94446f427ce8338e4e4e5b4a85de12c9e83fe90283824cc40aeca402
data/.rubocop.yml CHANGED
@@ -64,10 +64,10 @@ Metrics/MethodLength:
64
64
 
65
65
  # Configuration parameters: CountComments.
66
66
  Metrics/ClassLength:
67
- Max: 95
67
+ Max: 93
68
68
  Exclude:
69
69
  - 'lib/watir/capabilities.rb'
70
- - 'lib/watir/locators/element/locator.rb'
70
+ - 'lib/watir/locators/element/matcher.rb'
71
71
  - 'lib/watir/locators/element/selector_builder.rb'
72
72
  - 'lib/watir/locators/element/selector_builder/xpath.rb'
73
73
  - 'lib/watir/browser.rb'
@@ -93,6 +93,7 @@ Metrics/AbcSize:
93
93
  - 'lib/watir/locators/element/locator.rb'
94
94
  - 'lib/watir/generator/base/generator.rb'
95
95
  - 'lib/watir/generator/base/visitor.rb'
96
+ - 'spec/locator_spec_helper.rb'
96
97
 
97
98
  # TODO: fix with Watir 7
98
99
  # Configuration parameters: CountKeywordArgs.
data/.travis.yml CHANGED
@@ -71,6 +71,8 @@ matrix:
71
71
  - env: RAKE_TASK=spec:chrome HEADLESS=true
72
72
  <<: *five
73
73
  <<: *chrome
74
+ - env: RAKE_TASK=spec:unit
75
+ <<: *five
74
76
  - env: RAKE_TASK=spec:stats
75
77
  <<: *five
76
78
  <<: *chrome
data/CHANGES.md CHANGED
@@ -1,3 +1,16 @@
1
+ ### 6.16.0 (2018-12-16)
2
+
3
+ * Fix bug that did not re-locate Stale elements when taking an action on them (#814)
4
+ * Implement `Element#cache=` to assign otherwise located Selenium Element to `Element`
5
+ * Allow `:class` and `:class_name` locators to be used at the same time
6
+ * Allow `:class` locator with empty `Array` value to find all elements without a `class` attribute
7
+ * Fix bug that forced nested elements to wait when calling predicate methods (#827)
8
+ * Locator filtering behavior and Validate class moved into new `Matcher` classes
9
+ * Selector is built on Element initialization rather than during location
10
+ * Allow some nested elements to be located with a single XPath call
11
+ * Merge p0deje's watir-scroll gem functionality directly into Watir
12
+ * Fix bug with `#obscured?` for non-interactive elements (#818)
13
+
1
14
  ### 6.15.1 (2018-12-04)
2
15
 
3
16
  * Locator value type check error message now returns array of allowed class types
data/Rakefile CHANGED
@@ -7,6 +7,7 @@ require 'rspec/core/rake_task'
7
7
  RSpec::Core::RakeTask.new(:spec) do |spec|
8
8
  spec.rspec_opts = %w[--color --require fuubar --format Fuubar]
9
9
  spec.pattern = 'spec/**/*_spec.rb'
10
+ spec.exclude_pattern = 'spec/unit/**/*_spec.rb'
10
11
  end
11
12
 
12
13
  require 'rubocop/rake_task'
@@ -155,6 +156,11 @@ namespace :spec do
155
156
  end
156
157
  end
157
158
 
159
+ desc 'Run the Unit tests'
160
+ RSpec::Core::RakeTask.new(:unit) do |spec|
161
+ spec.pattern = 'spec/unit/**/**_spec.rb'
162
+ end
163
+
158
164
  desc 'Run element location specs and report wire calls'
159
165
  RSpec::Core::RakeTask.new(:stats) do |spec|
160
166
  ENV['SELENIUM_STATS'] = 'true'
data/lib/watir.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'selenium-webdriver'
2
2
  require 'time'
3
3
 
4
+ require 'watir/scroll'
4
5
  require 'watir/legacy_wait'
5
6
  require 'watir/wait'
6
7
  require 'watir/exception'
data/lib/watir/browser.rb CHANGED
@@ -9,6 +9,7 @@ module Watir
9
9
  include Waitable
10
10
  include Navigation
11
11
  include Exception
12
+ include Scrolling
12
13
 
13
14
  attr_writer :default_context, :original_window, :locator_namespace
14
15
  attr_reader :driver, :after_hooks
@@ -213,7 +214,9 @@ module Watir
213
214
  #
214
215
 
215
216
  def execute_script(script, *args)
216
- args.map! { |e| e.is_a?(Element) ? e.wd : e }
217
+ args.map! do |e|
218
+ e.is_a?(Element) ? e.wait_until(&:exists?).wd : e
219
+ end
217
220
 
218
221
  wrap_elements_in(self, @driver.execute_script(script, *args))
219
222
  end
@@ -10,6 +10,8 @@ module Watir
10
10
  def initialize(query_scope, selector)
11
11
  @query_scope = query_scope
12
12
  @selector = selector
13
+
14
+ build unless @selector.key?(:element)
13
15
  end
14
16
 
15
17
  #
@@ -33,11 +35,15 @@ module Watir
33
35
 
34
36
  alias empty? none?
35
37
 
38
+ def build
39
+ selector_builder.build(@selector.dup)
40
+ end
41
+
36
42
  #
37
43
  # Get the element at the given index or range.
38
44
  #
39
- # Any call to an ElementCollection including an adjacent selector
40
- # can not be lazy loaded because it must store correct type
45
+ # Any call to an ElementCollection that includes an adjacent selector
46
+ # can not be lazy loaded because it must store the correct type
41
47
  #
42
48
  # Ranges can not be lazy loaded
43
49
  #
@@ -86,19 +92,14 @@ module Watir
86
92
  def to_a
87
93
  hash = {}
88
94
  @to_a ||=
89
- elements.map.with_index do |e, idx|
90
- selector = @selector.merge(element: e)
91
- selector[:index] = idx
95
+ elements.map.with_index do |el, idx|
96
+ selector = @selector.dup
97
+ selector[:index] = idx unless idx.zero?
92
98
  element = element_class.new(@query_scope, selector)
93
99
  if [HTMLElement, Input].include? element.class
94
- tag_name = @selector[:tag_name] || element.tag_name
95
- hash[tag_name] ||= 0
96
- hash[tag_name] += 1
97
- selector[:index] = hash[tag_name] - 1
98
- selector[:tag_name] = tag_name
99
- Watir.element_class_for(tag_name).new(@query_scope, selector)
100
+ construct_subtype(element, hash).tap { |e| e.cache = el }
100
101
  else
101
- element
102
+ element.tap { |e| e.cache = el }
102
103
  end
103
104
  end
104
105
  end
@@ -154,11 +155,10 @@ module Watir
154
155
 
155
156
  def elements
156
157
  ensure_context
157
- if @query_scope.is_a?(Browser)
158
- locate_all
159
- else
160
- # This gives all of the standard Watir waiting behaviors to Collections
158
+ if selector_builder.built.key?(:scope)
161
159
  @query_scope.send(:element_call) { locate_all }
160
+ else
161
+ locate_all
162
162
  end
163
163
  end
164
164
 
@@ -168,11 +168,21 @@ module Watir
168
168
  end
169
169
 
170
170
  def locate_all
171
- build_locator.locate_all
171
+ locator.locate_all(selector_builder.built)
172
172
  end
173
173
 
174
174
  def element_class
175
175
  Kernel.const_get(self.class.name.sub(/Collection$/, ''))
176
176
  end
177
+
178
+ def construct_subtype(element, hash)
179
+ selector = element.selector
180
+ tag_name = selector[:tag_name] || element.tag_name
181
+ hash[tag_name] ||= 0
182
+ hash[tag_name] += 1
183
+ selector[:index] = hash[tag_name] - 1
184
+ selector[:tag_name] = tag_name
185
+ Watir.element_class_for(tag_name).new(@query_scope, selector)
186
+ end
177
187
  end # ElementCollection
178
188
  end # Watir
@@ -13,6 +13,7 @@ module Watir
13
13
  include Adjacent
14
14
  include JSExecution
15
15
  include Locators::ClassHelpers
16
+ include Scrolling
16
17
 
17
18
  attr_accessor :keyword
18
19
  attr_reader :selector
@@ -34,7 +35,14 @@ module Watir
34
35
  raise ArgumentError, "invalid argument: #{selector.inspect}" unless selector.is_a? Hash
35
36
 
36
37
  @element = selector.delete(:element)
38
+
39
+ if @element && !(selector.keys - %i[tag_name]).empty?
40
+ Watir.logger.deprecate(':element locator to initialize a relocatable Element', '#cache=', ids: [:element_cache])
41
+ end
42
+
37
43
  @selector = selector
44
+
45
+ build unless @element
38
46
  end
39
47
 
40
48
  #
@@ -133,6 +141,7 @@ module Watir
133
141
  #
134
142
 
135
143
  def click(*modifiers)
144
+ # TODO: Should wait_for_enabled be default, or `Button` specific behavior?
136
145
  element_call(:wait_for_enabled) do
137
146
  if modifiers.any?
138
147
  action = driver.action
@@ -436,7 +445,8 @@ module Watir
436
445
  #
437
446
 
438
447
  def wd
439
- element_call { @element }
448
+ assert_exists
449
+ @element
440
450
  end
441
451
 
442
452
  #
@@ -504,7 +514,7 @@ module Watir
504
514
  element_call do
505
515
  return true unless present?
506
516
 
507
- scroll_into_view
517
+ scroll.to
508
518
  execute_js(:elementObscured, self)
509
519
  end
510
520
  end
@@ -555,7 +565,7 @@ module Watir
555
565
  Watir.element_class_for(tag)
556
566
  end
557
567
 
558
- klass.new(@query_scope, @selector.merge(element: wd))
568
+ klass.new(@query_scope, @selector).tap { |el| el.cache = wd }
559
569
  end
560
570
 
561
571
  #
@@ -583,7 +593,7 @@ module Watir
583
593
  end
584
594
 
585
595
  def stale_in_context?
586
- @element.enabled? # any wire call will check for staleness
596
+ @element.css_value('staleness_check') # any wire call will check for staleness
587
597
  false
588
598
  rescue Selenium::WebDriver::Error::ObsoleteElementError
589
599
  true
@@ -603,6 +613,14 @@ module Watir
603
613
  self
604
614
  end
605
615
 
616
+ #
617
+ # @api private
618
+ #
619
+
620
+ def build
621
+ selector_builder.build(@selector.dup)
622
+ end
623
+
606
624
  #
607
625
  # @api private
608
626
  #
@@ -615,6 +633,16 @@ module Watir
615
633
  !!@element
616
634
  end
617
635
 
636
+ #
637
+ # @api private
638
+ #
639
+ # Set the cached element. For use when element can be relocated with the provided selector.
640
+ #
641
+
642
+ def cache=(element)
643
+ @element = element
644
+ end
645
+
618
646
  #
619
647
  # @api private
620
648
  #
@@ -646,7 +674,7 @@ module Watir
646
674
 
647
675
  begin
648
676
  @query_scope.wait_for_exists unless @query_scope.is_a? Browser
649
- wait_until(&:exists?)
677
+ wait_until(element_reset: true, &:exists?)
650
678
  rescue Wait::TimeoutError
651
679
  msg = "timed out after #{Watir.default_timeout} seconds, waiting for #{inspect} to be located"
652
680
  raise unknown_exception, msg
@@ -710,8 +738,7 @@ module Watir
710
738
  end
711
739
 
712
740
  def locate_in_context
713
- @locator = build_locator
714
- @element = @locator.locate
741
+ @element = locator.locate(selector_builder.built)
715
742
  end
716
743
 
717
744
  private
@@ -747,7 +774,7 @@ module Watir
747
774
  end
748
775
 
749
776
  def assert_is_element(obj)
750
- raise TypeError, "execpted Watir::Element, got #{obj.inspect}:#{obj.class}" unless obj.is_a? Element
777
+ raise TypeError, "expected Watir::Element, got #{obj.inspect}:#{obj.class}" unless obj.is_a? Element
751
778
  end
752
779
 
753
780
  # Removes duplication in #present? & #visible? and makes setting deprecation notice easier
@@ -771,13 +798,13 @@ module Watir
771
798
 
772
799
  begin
773
800
  check_condition(precondition, caller)
774
- Watir.logger.info "-> `Executing #{inspect}##{caller}`"
801
+ Watir.logger.debug "-> `Executing #{inspect}##{caller}`"
775
802
  yield
776
803
  rescue unknown_exception => ex
777
804
  element_call(:wait_for_exists, &block) if precondition.nil?
778
805
  msg = ex.message
779
806
  msg += '; Maybe look in an iframe?' if @query_scope.iframe.exists?
780
- custom_attributes = @locator.nil? ? [] : @locator.selector_builder.custom_attributes
807
+ custom_attributes = @locator.nil? ? [] : selector_builder.custom_attributes
781
808
  unless custom_attributes.empty?
782
809
  msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended"
783
810
  end
@@ -796,7 +823,7 @@ module Watir
796
823
  rescue Selenium::WebDriver::Error::NoSuchWindowError
797
824
  raise NoMatchingWindowFoundException, 'browser window was closed'
798
825
  ensure
799
- Watir.logger.info "<- `Completed #{inspect}##{caller}`"
826
+ Watir.logger.debug "<- `Completed #{inspect}##{caller}`"
800
827
  Wait.timer.reset! unless already_locked
801
828
  end
802
829
  end
@@ -806,14 +833,14 @@ module Watir
806
833
  # rubocop:enable Metrics/CyclomaticComplexity:
807
834
 
808
835
  def check_condition(condition, caller)
809
- Watir.logger.info "<- `Verifying precondition #{inspect}##{condition} for #{caller}`"
836
+ Watir.logger.debug "<- `Verifying precondition #{inspect}##{condition} for #{caller}`"
810
837
  begin
811
838
  condition.nil? ? assert_exists : send(condition)
812
- Watir.logger.info "<- `Verified precondition #{inspect}##{condition || 'assert_exists'}`"
839
+ Watir.logger.debug "<- `Verified precondition #{inspect}##{condition || 'assert_exists'}`"
813
840
  rescue unknown_exception
814
841
  raise unless condition.nil?
815
842
 
816
- Watir.logger.info "<- `Unable to satisfy precondition #{inspect}##{condition}`"
843
+ Watir.logger.debug "<- `Unable to satisfy precondition #{inspect}##{condition}`"
817
844
  check_condition(:wait_for_exists, caller)
818
845
  end
819
846
  end
@@ -45,7 +45,9 @@ module Watir
45
45
  #
46
46
 
47
47
  def execute_script(script, *args)
48
- args.map! { |e| e.is_a?(Element) ? e.wd : e }
48
+ args.map! do |e|
49
+ e.is_a?(Element) ? e.wait_until(&:exists?).wd : e
50
+ end
49
51
  returned = driver.execute_script(script, *args)
50
52
 
51
53
  browser.wrap_elements_in(self, returned)
@@ -1,8 +1,8 @@
1
1
  module Watir
2
2
  class Radio < Input
3
- def initialize(query_scope, selector)
4
- super
3
+ def build
5
4
  @selector[:label] = @selector.delete(:text) if @selector.key?(:text)
5
+ super
6
6
  end
7
7
 
8
8
  #
@@ -51,6 +51,11 @@ module Watir
51
51
  class RadioCollection < InputCollection
52
52
  private
53
53
 
54
+ def build
55
+ @selector[:label] = @selector.delete(:text) if @selector.key?(:text)
56
+ super
57
+ end
58
+
54
59
  def element_class
55
60
  Radio
56
61
  end
@@ -218,6 +218,7 @@ module Watir
218
218
  raise_no_value_found(str_or_rx)
219
219
  end
220
220
 
221
+ # TODO: Consider locating the Select List before throwing the exception
221
222
  def raise_no_value_found(str_or_rx)
222
223
  raise NoValueFoundException, "#{str_or_rx.inspect} not found in #{inspect}"
223
224
  end
@@ -3,33 +3,35 @@ require 'watir/locators/element/selector_builder'
3
3
  require 'watir/locators/element/selector_builder/xpath_support'
4
4
  require 'watir/locators/element/selector_builder/regexp_disassembler'
5
5
  require 'watir/locators/element/selector_builder/xpath'
6
- require 'watir/locators/element/validator'
6
+ require 'watir/locators/element/matcher'
7
7
 
8
8
  require 'watir/locators/anchor/selector_builder'
9
9
 
10
- require 'watir/locators/button/locator'
11
10
  require 'watir/locators/button/selector_builder'
12
11
  require 'watir/locators/button/selector_builder/xpath'
13
- require 'watir/locators/button/validator'
12
+ require 'watir/locators/button/matcher'
14
13
 
15
- require 'watir/locators/cell/locator'
16
14
  require 'watir/locators/cell/selector_builder'
17
15
  require 'watir/locators/cell/selector_builder/xpath'
18
16
 
19
- require 'watir/locators/row/locator'
20
17
  require 'watir/locators/row/selector_builder'
21
18
  require 'watir/locators/row/selector_builder/xpath'
22
19
 
23
20
  require 'watir/locators/text_area/selector_builder'
24
21
  require 'watir/locators/text_area/selector_builder/xpath'
25
22
 
26
- require 'watir/locators/text_field/locator'
27
23
  require 'watir/locators/text_field/selector_builder'
28
24
  require 'watir/locators/text_field/selector_builder/xpath'
29
- require 'watir/locators/text_field/validator'
25
+ require 'watir/locators/text_field/matcher'
30
26
 
31
27
  module Watir
32
28
  module Locators
29
+ W3C_FINDERS = %i[css
30
+ link
31
+ link_text
32
+ partial_link_text
33
+ xpath].freeze
34
+
33
35
  module ClassHelpers
34
36
  def locator_class
35
37
  class_from_string("#{browser.locator_namespace}::#{element_class_name}::Locator") ||
@@ -38,11 +40,11 @@ module Watir
38
40
  Locators::Element::Locator
39
41
  end
40
42
 
41
- def element_validator_class
42
- class_from_string("#{browser.locator_namespace}::#{element_class_name}::Validator") ||
43
- class_from_string("Watir::Locators::#{element_class_name}::Validator") ||
44
- class_from_string("#{browser.locator_namespace}::Element::Validator") ||
45
- Locators::Element::Validator
43
+ def element_matcher_class
44
+ class_from_string("#{browser.locator_namespace}::#{element_class_name}::Matcher") ||
45
+ class_from_string("Watir::Locators::#{element_class_name}::Matcher") ||
46
+ class_from_string("#{browser.locator_namespace}::Element::Matcher") ||
47
+ Locators::Element::Matcher
46
48
  end
47
49
 
48
50
  def selector_builder_class
@@ -62,15 +64,13 @@ module Watir
62
64
  element_class.to_s.split('::').last
63
65
  end
64
66
 
65
- def build_locator
66
- selector_builder = if element_class == Watir::Row
67
- scope_tag_name = @query_scope.selector[:tag_name]
68
- selector_builder_class.new(element_class.attribute_list, scope_tag_name)
69
- else
70
- selector_builder_class.new(element_class.attribute_list)
71
- end
72
- element_validator = element_validator_class.new
73
- locator_class.new(@query_scope, @selector.dup, selector_builder, element_validator)
67
+ def selector_builder
68
+ @selector_builder ||= selector_builder_class.new(element_class.attribute_list, @query_scope)
69
+ end
70
+
71
+ def locator
72
+ @element_matcher ||= element_matcher_class.new(@query_scope, @selector.dup)
73
+ @locator ||= locator_class.new(@element_matcher)
74
74
  end
75
75
  end
76
76
  end