watir 6.15.1 → 6.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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