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
@@ -0,0 +1,111 @@
1
+ require_relative '../unit_helper'
2
+
3
+ describe Watir::Locators::Row::SelectorBuilder do
4
+ include LocatorSpecHelper
5
+
6
+ let(:row_selector_builder) { described_class.new(attributes, query_scope) }
7
+ let(:selector_built) { row_selector_builder.build(selector).reject { |key| key == :scope } }
8
+ let(:selector) { @selector || {} }
9
+
10
+ describe '#build' do
11
+ context 'with query scopes' do
12
+ it 'with only table query scope' do
13
+ @query_scope = element(tag_name: 'table')
14
+
15
+ built = {xpath: "./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
16
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr']"}
17
+
18
+ expect(selector_built).to eq built
19
+ end
20
+
21
+ it 'with tbody query scope' do
22
+ @query_scope = element(tag_name: 'tbody')
23
+ built = {xpath: "./*[local-name()='tr']"}
24
+
25
+ expect(selector_built).to eq built
26
+ end
27
+
28
+ it 'with thead query scope' do
29
+ @query_scope = element(tag_name: 'thead')
30
+ built = {xpath: "./*[local-name()='tr']"}
31
+
32
+ expect(selector_built).to eq built
33
+ end
34
+
35
+ it 'with tfoot query scope' do
36
+ @query_scope = element(tag_name: 'tfoot')
37
+ built = {xpath: "./*[local-name()='tr']"}
38
+
39
+ expect(selector_built).to eq built
40
+ end
41
+ end
42
+
43
+ context 'when tag name is specified' do
44
+ before do
45
+ @query_scope = element(tag_name: 'table')
46
+ end
47
+
48
+ context 'with index' do
49
+ it 'positive' do
50
+ @selector = {index: 1}
51
+ built = {xpath: "(./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
52
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr'])[2]"}
53
+
54
+ expect(selector_built).to eq built
55
+ end
56
+
57
+ it 'negative' do
58
+ @selector = {index: -3}
59
+ built = {xpath: "(./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
60
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr'])[last()-2]"}
61
+
62
+ expect(selector_built).to eq built
63
+ end
64
+
65
+ it 'last' do
66
+ @selector = {index: -1}
67
+ built = {xpath: "(./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
68
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr'])[last()]"}
69
+
70
+ expect(selector_built).to eq built
71
+ end
72
+
73
+ it 'does not return index if it is zero' do
74
+ @selector = {index: 0}
75
+ built = {xpath: "./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
76
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr']"}
77
+
78
+ expect(selector_built).to eq built
79
+ end
80
+
81
+ it 'raises exception when index is not an Integer', skip_after: true do
82
+ @selector = {index: 'foo'}
83
+ msg = /expected one of \[(Integer|Fixnum)\], got "foo":String/
84
+ expect { selector_built }.to raise_exception TypeError, msg
85
+ end
86
+ end
87
+
88
+ context 'with multiple locators' do
89
+ it 'attribute and class' do
90
+ @selector = {id: 'gregory', class: /brick/}
91
+ built = {xpath: "./*[local-name()='tr'][contains(@class, 'brick')][@id='gregory'] | " \
92
+ "./*[local-name()='tbody']/*[local-name()='tr'][contains(@class, 'brick')][@id='gregory'] | " \
93
+ "./*[local-name()='thead']/*[local-name()='tr'][contains(@class, 'brick')][@id='gregory'] | " \
94
+ "./*[local-name()='tfoot']/*[local-name()='tr'][contains(@class, 'brick')][@id='gregory']"}
95
+
96
+ expect(selector_built).to eq built
97
+ end
98
+ end
99
+
100
+ context 'returns locators that can not be directly translated' do
101
+ it 'any text value' do
102
+ @selector = {text: 'Gregory'}
103
+ built = {xpath: "./*[local-name()='tr'] | ./*[local-name()='tbody']/*[local-name()='tr'] | " \
104
+ "./*[local-name()='thead']/*[local-name()='tr'] | ./*[local-name()='tfoot']/*[local-name()='tr']", text: 'Gregory'}
105
+
106
+ expect(selector_built).to eq built
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,189 @@
1
+ require_relative '../unit_helper'
2
+
3
+ describe Watir::Locators::TextField::SelectorBuilder do
4
+ include LocatorSpecHelper
5
+
6
+ let(:selector_builder) { described_class.new(attributes, query_scope) }
7
+ let(:uppercase) { 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ' }
8
+ let(:lowercase) { 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ' }
9
+ let(:negative_types) do
10
+ "translate(@type,'#{uppercase}','#{lowercase}')!='file' and "\
11
+ "translate(@type,'#{uppercase}','#{lowercase}')!='radio' and " \
12
+ "translate(@type,'#{uppercase}','#{lowercase}')!='checkbox' and " \
13
+ "translate(@type,'#{uppercase}','#{lowercase}')!='submit' and " \
14
+ "translate(@type,'#{uppercase}','#{lowercase}')!='reset' and " \
15
+ "translate(@type,'#{uppercase}','#{lowercase}')!='image' and " \
16
+ "translate(@type,'#{uppercase}','#{lowercase}')!='button' and " \
17
+ "translate(@type,'#{uppercase}','#{lowercase}')!='hidden' and " \
18
+ "translate(@type,'#{uppercase}','#{lowercase}')!='range' and " \
19
+ "translate(@type,'#{uppercase}','#{lowercase}')!='color' and " \
20
+ "translate(@type,'#{uppercase}','#{lowercase}')!='date' and " \
21
+ "translate(@type,'#{uppercase}','#{lowercase}')!='datetime-local'"
22
+ end
23
+
24
+ describe '#build' do
25
+ it 'without any arguments' do
26
+ selector = {}
27
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}
28
+
29
+ expect(selector_builder.build(selector)).to eq built
30
+ end
31
+
32
+ context 'with type' do
33
+ it 'specified text field type that is text' do
34
+ selector = {type: 'text'}
35
+ built = {xpath: ".//*[local-name()='input']" \
36
+ "[translate(@type,'#{uppercase}','#{lowercase}')='text']"}
37
+
38
+ expect(selector_builder.build(selector)).to eq built
39
+ end
40
+
41
+ it 'specified text field type that is not text' do
42
+ selector = {type: 'number'}
43
+ built = {xpath: ".//*[local-name()='input']" \
44
+ "[translate(@type,'#{uppercase}','#{lowercase}')='number']"}
45
+
46
+ expect(selector_builder.build(selector)).to eq built
47
+ end
48
+
49
+ it 'true locates text field with a type specified' do
50
+ selector = {type: true}
51
+ built = {xpath: ".//*[local-name()='input'][#{negative_types}]"}
52
+
53
+ expect(selector_builder.build(selector)).to eq built
54
+ end
55
+
56
+ it 'false locates text field without type specified' do
57
+ selector = {type: false}
58
+ built = {xpath: ".//*[local-name()='input'][not(@type)]"}
59
+
60
+ expect(selector_builder.build(selector)).to eq built
61
+ end
62
+
63
+ it 'raises exception when a non-text field type input is specified' do
64
+ selector = {type: 'checkbox'}
65
+ msg = 'TextField Elements can not be located by type: checkbox'
66
+
67
+ expect { selector_builder.build(selector) }
68
+ .to raise_exception Watir::Exception::LocatorException, msg
69
+ end
70
+ end
71
+
72
+ context 'with index' do
73
+ it 'positive' do
74
+ selector = {index: 4}
75
+ built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[5]"}
76
+
77
+ expect(selector_builder.build(selector)).to eq built
78
+ end
79
+
80
+ it 'negative' do
81
+ selector = {index: -3}
82
+ built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[last()-2]"}
83
+
84
+ expect(selector_builder.build(selector)).to eq built
85
+ end
86
+
87
+ it 'last' do
88
+ selector = {index: -1}
89
+ built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[last()]"}
90
+
91
+ expect(selector_builder.build(selector)).to eq built
92
+ end
93
+
94
+ it 'does not return index if it is zero' do
95
+ selector = {index: 0}
96
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}
97
+
98
+ expect(selector_builder.build(selector)).to eq built
99
+ end
100
+
101
+ it 'raises exception when index is not an Integer' do
102
+ selector = {index: 'foo'}
103
+ msg = /expected one of \[(Integer|Fixnum)\], got "foo":String/
104
+
105
+ expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
106
+ end
107
+ end
108
+
109
+ context 'with wrong tag name' do
110
+ # TODO: Should this throw exception?
111
+ it 'ignores tag name' do
112
+ selector = {tag_name: 'div'}
113
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}
114
+
115
+ expect(selector_builder.build(selector)).to eq built
116
+ end
117
+ end
118
+
119
+ context 'with text' do
120
+ it 'String for value' do
121
+ selector = {text: 'Developer'}
122
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: 'Developer'}
123
+
124
+ expect(selector_builder.build(selector)).to eq built
125
+ end
126
+
127
+ it 'Simple Regexp for value' do
128
+ selector = {text: /Dev/}
129
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: /Dev/}
130
+
131
+ expect(selector_builder.build(selector)).to eq built
132
+ end
133
+
134
+ it 'returns complicated Regexp to the locator as a value' do
135
+ selector = {text: /^foo$/}
136
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: /^foo$/}
137
+
138
+ expect(selector_builder.build(selector)).to eq built
139
+ end
140
+ end
141
+
142
+ context 'with label' do
143
+ it 'using String' do
144
+ selector = {label: 'First name'}
145
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
146
+ "[@id=//label[normalize-space()='First name']/@for or parent::label[normalize-space()='First name']]"}
147
+
148
+ expect(selector_builder.build(selector)).to eq built
149
+ end
150
+
151
+ it 'uses String with hidden text' do
152
+ selector = {label: 'With hidden text'}
153
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
154
+ "[@id=//label[normalize-space()='With hidden text']/@for or parent::label[normalize-space()='With hidden text']]"}
155
+
156
+ expect(selector_builder.build(selector)).to eq built
157
+ end
158
+
159
+ # TODO: Can use contains once remove :text_regexp deprecation
160
+ xit 'using simple Regexp' do
161
+ selector = {label: /First/}
162
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
163
+ "[@id=//label[contains(text(), 'First')]/@for or parent::label[contains(text(), 'First')]]"}
164
+
165
+ expect(selector_builder.build(selector)).to eq built
166
+ end
167
+
168
+ # TODO: Can use contains once remove :text_regexp deprecation
169
+ xit 'using complex Regexp' do
170
+ selector = {label: /([qa])st? name/}
171
+ built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
172
+ "[@id=//label[contains(text(), 's') and contains(text(), ' name')]/@for or " \
173
+ "parent::label[contains(text(), 's') and contains(text(), ' name')]]", label_element: /([qa])st? name/}
174
+
175
+ expect(selector_builder.build(selector)).to eq built
176
+ end
177
+ end
178
+
179
+ context 'with multiple locators' do
180
+ it 'locates using tag name, class, attributes and text' do
181
+ selector = {text: 'Developer', class: /c/, id: true}
182
+ built = {xpath: ".//*[local-name()='input'][contains(@class, 'c')]" \
183
+ "[not(@type) or (#{negative_types})][@id]", text: 'Developer'}
184
+
185
+ expect(selector_builder.build(selector)).to eq built
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../unit_helper'
2
+
3
+ describe Watir::Locators::TextArea::SelectorBuilder do
4
+ include LocatorSpecHelper
5
+
6
+ let(:selector_builder) { described_class.new(attributes, query_scope) }
7
+
8
+ describe '#build' do
9
+ context 'Always returns value argument' do
10
+ it 'String' do
11
+ selector = {tag_name: 'textarea', value: 'Foo'}
12
+ built = {xpath: ".//*[local-name()='textarea']", value: 'Foo'}
13
+
14
+ expect(selector_builder.build(selector)).to eq built
15
+ end
16
+
17
+ it 'Regexp' do
18
+ selector = {tag_name: 'textarea', value: /Foo/}
19
+ built = {xpath: ".//*[local-name()='textarea']", value: /Foo/}
20
+
21
+ expect(selector_builder.build(selector)).to eq built
22
+ end
23
+ end
24
+ end
25
+ end
@@ -90,16 +90,15 @@ describe 'Browser' do
90
90
  expect(browser.text_field(id: 'new_user_first_name').value).to eq 'hello'
91
91
  end
92
92
 
93
- not_compliant_on(:firefox) do
94
- it 'sends keys to a frame' do
95
- browser.goto WatirSpec.url_for 'frames.html'
96
- tf = browser.frame.text_field(id: 'senderElement')
97
- tf.clear
93
+ it 'sends keys to a frame' do
94
+ browser.goto WatirSpec.url_for 'frames.html'
95
+ tf = browser.frame.text_field(id: 'senderElement')
96
+ tf.clear
97
+ tf.click
98
98
 
99
- browser.frame.send_keys 'hello'
99
+ browser.frame.send_keys 'hello'
100
100
 
101
- expect(tf.value).to eq 'hello'
102
- end
101
+ expect(tf.value).to eq 'hello'
103
102
  end
104
103
  end
105
104
 
@@ -71,9 +71,8 @@ describe Watir::Locators::Element::Locator do
71
71
  end
72
72
 
73
73
  it 'raises exception when value is not Boolean' do
74
- element = browser.body.element(visible: 'true')
75
74
  msg = 'expected one of [TrueClass, FalseClass], got "true":String'
76
- expect { element.exists? }.to raise_exception(TypeError, msg)
75
+ expect { browser.body.element(visible: 'true') }.to raise_exception(TypeError, msg)
77
76
  end
78
77
  end
79
78
  end
@@ -20,19 +20,32 @@ describe 'Element' do
20
20
  expect { Watir::Element.new(container, 1, 2, 3, 4) }.to raise_error(ArgumentError)
21
21
  expect { Watir::Element.new(container, 'foo') }.to raise_error(ArgumentError)
22
22
  end
23
+
24
+ it 'throws deprecation warning when combining element locator with other locators' do
25
+ element = double Selenium::WebDriver::Element
26
+ allow(element).to receive(:enabled?).and_return(true)
27
+ expect { browser.text_field(class_name: 'name', index: 1, element: element) }.to have_deprecated_element_cache
28
+ end
23
29
  end
24
30
 
25
31
  describe '#element_call' do
26
32
  it 'handles exceptions when taking an action on a stale element' do
27
33
  browser.goto WatirSpec.url_for('removed_element.html')
28
34
 
29
- element = browser.div(id: 'text').tap(&:exists?)
35
+ element = browser.div(id: 'text').locate
30
36
 
31
37
  browser.refresh
32
38
 
33
39
  expect(element).to be_stale
34
40
  expect { element.text }.to_not raise_error
35
41
  end
42
+
43
+ it 'relocates stale element when taking an action on it' do
44
+ browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
45
+ element = browser.text_field(id: 'new_user_first_name').locate
46
+ browser.refresh
47
+ expect { element.click }.not_to raise_exception
48
+ end
36
49
  end
37
50
 
38
51
  describe '#eq and #eql?' do
@@ -224,7 +237,7 @@ describe 'Element' do
224
237
  end
225
238
 
226
239
  it 'raises UnknownObjectException exception if the element is stale' do
227
- element = browser.text_field(id: 'new_user_email').tap(&:exists?)
240
+ element = browser.text_field(id: 'new_user_email').locate
228
241
 
229
242
  browser.refresh
230
243
 
@@ -261,13 +274,36 @@ describe 'Element' do
261
274
  end
262
275
  end
263
276
 
277
+ describe '#cache=' do
278
+ it 'bypasses selector location' do
279
+ browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
280
+
281
+ wd = browser.div.wd
282
+ element = Watir::Element.new(browser, id: 'not_valid')
283
+ element.cache = wd
284
+
285
+ expect(element).to exist
286
+ end
287
+
288
+ it 'can be cleared' do
289
+ browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
290
+
291
+ wd = browser.div.wd
292
+ element = Watir::Element.new(browser, id: 'not_valid')
293
+ element.cache = wd
294
+
295
+ browser.refresh
296
+ expect(element).to_not exist
297
+ end
298
+ end
299
+
264
300
  describe '#exists?' do
265
301
  before do
266
302
  browser.goto WatirSpec.url_for('removed_element.html')
267
303
  end
268
304
 
269
305
  it 'element from a collection returns false when it becomes stale' do
270
- element = browser.divs(id: 'text').first.tap(&:exists?)
306
+ element = browser.divs(id: 'text').first.locate
271
307
 
272
308
  browser.refresh
273
309
 
@@ -301,7 +337,7 @@ describe 'Element' do
301
337
  end
302
338
 
303
339
  it 'returns false if the element is stale' do
304
- element = browser.div(id: 'foo').tap(&:exists?)
340
+ element = browser.div(id: 'foo').locate
305
341
 
306
342
  browser.refresh
307
343
 
@@ -313,7 +349,7 @@ describe 'Element' do
313
349
  end
314
350
 
315
351
  it 'does not raise staleness deprecation if element no longer exists in DOM' do
316
- element = browser.div(id: 'foo').tap(&:exists?)
352
+ element = browser.div(id: 'foo').locate
317
353
  browser.goto(WatirSpec.url_for('iframes.html'))
318
354
 
319
355
  expect { element.present? }.to_not have_deprecated_stale_present
@@ -321,7 +357,7 @@ describe 'Element' do
321
357
 
322
358
  # TODO: Documents Current Behavior, but needs to be refactored/removed
323
359
  it 'returns true the second time if the element is stale' do
324
- element = browser.div(id: 'foo').tap(&:exists?)
360
+ element = browser.div(id: 'foo').locate
325
361
 
326
362
  browser.refresh
327
363
 
@@ -354,7 +390,7 @@ describe 'Element' do
354
390
 
355
391
  describe '#stale?' do
356
392
  it 'returns true if the element is stale' do
357
- element = browser.button(name: 'new_user_submit_disabled').tap(&:exists?)
393
+ element = browser.button(name: 'new_user_submit_disabled').locate
358
394
 
359
395
  browser.refresh
360
396
 
@@ -362,7 +398,7 @@ describe 'Element' do
362
398
  end
363
399
 
364
400
  it 'returns false if the element is not stale' do
365
- element = browser.button(name: 'new_user_submit_disabled').tap(&:exists?)
401
+ element = browser.button(name: 'new_user_submit_disabled').locate
366
402
 
367
403
  expect(element).to_not be_stale
368
404
  end
@@ -455,12 +491,6 @@ describe 'Element' do
455
491
  expect(browser.div(id: 'no_such_div').link(id: 'no_such_id')).to_not exist
456
492
  end
457
493
 
458
- it 'raises if both :xpath and :css are given' do
459
- msg = ':xpath and :css cannot be combined ({:xpath=>"//div", :css=>"div"})'
460
- expect { browser.div(xpath: '//div', css: 'div').exists? }
461
- .to raise_exception Watir::Exception::LocatorException, msg
462
- end
463
-
464
494
  it "doesn't raise when selector has with :xpath has :index" do
465
495
  expect(browser.div(xpath: '//div', index: 1)).to exist
466
496
  end
@@ -787,7 +817,7 @@ describe 'Element' do
787
817
  end
788
818
 
789
819
  it 'returns false if element has not been located' do
790
- expect(browser.form(id: 'new_user').tap(&:exists?)).to be_located
820
+ expect(browser.form(id: 'new_user').locate).to be_located
791
821
  end
792
822
  end
793
823
 
@@ -897,12 +927,18 @@ describe 'Element' do
897
927
  expect { btn.click }.not_to raise_exception
898
928
  end
899
929
 
900
- it 'scrolls element into view before checking if obscured' do
930
+ it 'scrolls interactive element into view before checking if obscured' do
901
931
  btn = browser.button(id: 'requires_scrolling')
902
932
  expect(btn).not_to be_obscured
903
933
  expect { btn.click }.not_to raise_exception
904
934
  end
905
935
 
936
+ it 'scrolls non-interactive element into view before checking if obscured' do
937
+ div = browser.div(id: 'requires_scrolling_container')
938
+ expect(div).not_to be_obscured
939
+ expect { div.click }.not_to raise_exception
940
+ end
941
+
906
942
  it 'returns true if element cannot be scrolled into view' do
907
943
  btn = browser.button(id: 'off_screen')
908
944
  expect(btn).to be_obscured