watir 6.17.0 → 6.18.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/.github/actions/enable-safari/action.yml +11 -0
  3. data/.github/actions/install-chrome/action.yml +11 -0
  4. data/.github/workflows/linux.yml +61 -0
  5. data/.github/workflows/mac.yml +55 -0
  6. data/.github/workflows/unit.yml +31 -0
  7. data/.github/workflows/windows.yml +39 -0
  8. data/.rubocop_todo.yml +36 -0
  9. data/CHANGES.md +14 -0
  10. data/LICENSE +2 -2
  11. data/README.md +9 -10
  12. data/Rakefile +1 -1
  13. data/lib/watir.rb +1 -0
  14. data/lib/watir/adjacent.rb +1 -1
  15. data/lib/watir/alert.rb +1 -0
  16. data/lib/watir/attribute_helper.rb +2 -0
  17. data/lib/watir/browser.rb +2 -2
  18. data/lib/watir/cookies.rb +2 -0
  19. data/lib/watir/element_collection.rb +21 -6
  20. data/lib/watir/elements/element.rb +10 -10
  21. data/lib/watir/elements/html_elements.rb +0 -1
  22. data/lib/watir/elements/iframe.rb +2 -1
  23. data/lib/watir/elements/select.rb +20 -5
  24. data/lib/watir/generator/html/generator.rb +1 -1
  25. data/lib/watir/has_window.rb +17 -15
  26. data/lib/watir/js_execution.rb +2 -2
  27. data/lib/watir/js_snippets.rb +2 -2
  28. data/lib/watir/locators.rb +1 -3
  29. data/lib/watir/locators/element/selector_builder.rb +1 -1
  30. data/lib/watir/logger.rb +2 -18
  31. data/lib/watir/radio_set.rb +2 -2
  32. data/lib/watir/user_editable.rb +6 -2
  33. data/lib/watir/version.rb +1 -1
  34. data/lib/watir/wait.rb +2 -0
  35. data/lib/watir/wait/timer.rb +1 -1
  36. data/lib/watir/window.rb +7 -3
  37. data/lib/watir/window_collection.rb +105 -0
  38. data/lib/watirspec.rb +1 -0
  39. data/lib/watirspec/implementation.rb +3 -5
  40. data/lib/watirspec/runner.rb +1 -1
  41. data/lib/watirspec/server.rb +1 -1
  42. data/spec/spec_helper.rb +2 -7
  43. data/spec/unit/match_elements/element_spec.rb +17 -15
  44. data/spec/unit/unit_helper.rb +2 -4
  45. data/spec/watirspec/after_hooks_spec.rb +15 -11
  46. data/spec/watirspec/browser_spec.rb +3 -2
  47. data/spec/watirspec/elements/element_spec.rb +14 -11
  48. data/spec/watirspec/elements/filefield_spec.rb +2 -2
  49. data/spec/watirspec/elements/iframe_spec.rb +5 -7
  50. data/spec/watirspec/elements/link_spec.rb +5 -3
  51. data/spec/watirspec/elements/select_list_spec.rb +156 -37
  52. data/spec/watirspec/html/wait.html +5 -5
  53. data/spec/watirspec/html/window_switching.html +10 -0
  54. data/spec/watirspec/legacy_wait_spec.rb +216 -0
  55. data/spec/watirspec/support/rspec_matchers.rb +10 -7
  56. data/spec/watirspec/wait_spec.rb +257 -301
  57. data/spec/watirspec/window_switching_spec.rb +282 -160
  58. data/spec/watirspec_helper.rb +10 -15
  59. data/support/doctest_helper.rb +0 -2
  60. data/watir.gemspec +2 -2
  61. metadata +25 -13
  62. data/.travis.yml +0 -87
  63. data/appveyor.yml +0 -13
  64. data/spec/watirspec/relaxed_locate_spec.rb +0 -109
@@ -58,14 +58,14 @@
58
58
  <body>
59
59
  <div id="foo" custom="" style="display:block;">foo</div>
60
60
  <div id="bar" style="display:none;" onclick='this.innerHTML = "changed"'>bar</div>
61
- <a id="show_bar" href="#" onclick="setTimeoutDisplay('bar', 'block', 500);">show bar</a>
62
- <a id="hide_foo" href="#" onclick="setTimeoutDisplay('foo', 'none', 500);">hide foo</a>
61
+ <a id="show_bar" href="#" onclick="setTimeoutDisplay('bar', 'block', 1000);">show bar</a>
62
+ <a id="hide_foo" href="#" onclick="setTimeoutDisplay('foo', 'none', 1000);">hide foo</a>
63
63
  <a id="remove_foo" href="#" onclick="setTimeoutRemove('foo', 1000);">remove foo</a>
64
64
  <a id="add_foobar" href="#" onclick="setTimeoutAddDisplay('foobar', 'bar', 1000);">add foobar</a>
65
65
  <a id="readd_bar" href="#" onclick="setTimeoutRemove('bar', 500); setTimeoutAddDisplay('bar', 'foo', 1000);">re-add bar</a>
66
66
  <div id="buttons">
67
67
  <button id="btn" type="button" onclick="setDisabled('btn', true, 0);" disabled>Click To Disable!</button>
68
- <a id="enable_btn" href="#" onclick="setDisabled('btn', false, 500);">enable btn</a>
68
+ <a id="enable_btn" href="#" onclick="setDisabled('btn', false, 1000);">enable btn</a>
69
69
  <button id="btn2" style="display:none;" type="button" disabled>Hidden and Disabled</button>
70
70
  <a id="show_and_enable_btn" href="#" onclick="setTimeoutDisplay('btn2', 'block', 500); setDisabled('btn2', false, 1000);">
71
71
  show and enable btn
@@ -78,10 +78,10 @@
78
78
  <form>
79
79
  <label for="textfield">Not Displayed Yet</label>
80
80
  <input type="textfield" title="Text Displayed" name="textfield" id="textfield" style="display:none;" /> <br />
81
- <a id="show_textfield" href="#" onclick="setTimeoutDisplay('textfield', 'block', 2000);">show textfield</a>
81
+ <a id="show_textfield" href="#" onclick="setTimeoutDisplay('textfield', 'block', 1000);">show textfield</a>
82
82
  <label for="writable">Not Writable</label>
83
83
  <input type="text" title="Not Writable" name="writable" id="writable" readonly/> <br />
84
- <a id="make-writable" onclick="makeWritable('writable', 2000);">Make Writable</a>
84
+ <a id="make-writable" onclick="makeWritable('writable', 1000);">Make Writable</a>
85
85
  <fieldset>
86
86
  <select name="languages" id="languages", style="display: none">
87
87
  <option id="danish" value="1">Danish</option>
@@ -2,11 +2,21 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>window switching</title>
5
+ <script>
6
+ function windowOpenDelayed(timeout) {
7
+ setTimeout(function() {
8
+ window.open("closeable.html");
9
+ }, timeout);
10
+ }
11
+ </script>
5
12
  </head>
6
13
 
7
14
  <body>
8
15
  <p>
9
16
  Click <a id="open" href="#" onclick='window.open("closeable.html")'>here</a> to open a new window.
10
17
  </p>
18
+ <p>
19
+ Click <a id="delayed" href="#" onclick=windowOpenDelayed(1000)>here</a> to open a new window after a delay.
20
+ </p>
11
21
  </body>
12
22
  </html>
@@ -0,0 +1,216 @@
1
+ require 'watirspec_helper'
2
+
3
+ describe Watir::Element do
4
+ before do
5
+ browser.goto WatirSpec.url_for('wait.html')
6
+ end
7
+
8
+ compliant_on :relaxed_locate do
9
+ describe '#wait_until_present' do
10
+ it 'waits until the element appears' do
11
+ browser.a(id: 'show_bar').click
12
+ expect {
13
+ expect { browser.div(id: 'bar').wait_until_present(timeout: 5) }.to_not raise_exception
14
+ }.to have_deprecated_wait_until_present
15
+ end
16
+
17
+ bug 'Safari does not recognize date type', :safari do
18
+ it 'waits until the element re-appears' do
19
+ browser.link(id: 'readd_bar').click
20
+ expect {
21
+ expect { browser.div(id: 'bar').wait_until_present }.to_not raise_exception
22
+ }.to have_deprecated_wait_until_present
23
+ end
24
+ end
25
+
26
+ it "times out if the element doesn't appear" do
27
+ inspected = '#<Watir::Div: located: true; {:id=>"bar", :tag_name=>"div"}>'
28
+ error = Watir::Wait::TimeoutError
29
+ message = "timed out after 1 seconds, waiting for #{inspected} to become present"
30
+
31
+ expect {
32
+ expect { browser.div(id: 'bar').wait_until_present(timeout: 1) }.to raise_error(error, message)
33
+ }.to have_deprecated_wait_until_present
34
+ end
35
+
36
+ it 'uses provided interval' do
37
+ element = browser.div(id: 'bar')
38
+ expect(element).to receive(:present?).twice
39
+
40
+ expect {
41
+ expect { element.wait_until_present(timeout: 0.4, interval: 0.2) }.to raise_timeout_exception
42
+ }.to have_deprecated_wait_until_present
43
+ end
44
+ end
45
+
46
+ describe '#wait_while_present' do
47
+ it 'waits until the element disappears' do
48
+ browser.a(id: 'hide_foo').click
49
+ expect {
50
+ expect { browser.div(id: 'foo').wait_while_present(timeout: 2) }.to_not raise_exception
51
+ }.to have_deprecated_wait_while_present
52
+ end
53
+
54
+ it "times out if the element doesn't disappear" do
55
+ error = Watir::Wait::TimeoutError
56
+ inspected = '#<Watir::Div: located: true; {:id=>"foo", :tag_name=>"div"}>'
57
+ message = "timed out after 1 seconds, waiting for #{inspected} not to be present"
58
+ expect {
59
+ expect { browser.div(id: 'foo').wait_while_present(timeout: 1) }.to raise_error(error, message)
60
+ }.to have_deprecated_wait_while_present
61
+ end
62
+
63
+ it 'uses provided interval' do
64
+ error = Watir::Wait::TimeoutError
65
+ element = browser.div(id: 'foo')
66
+ expect(element).to receive(:present?).and_return(true).twice
67
+
68
+ expect {
69
+ expect { element.wait_while_present(timeout: 0.4, interval: 0.2) }.to raise_error(error)
70
+ }.to have_deprecated_wait_while_present
71
+ end
72
+
73
+ it 'does not error when element goes stale' do
74
+ element = browser.div(id: 'foo').locate
75
+
76
+ allow(element).to receive(:stale?).and_return(false, true)
77
+
78
+ browser.a(id: 'hide_foo').click
79
+ expect {
80
+ expect { element.wait_while_present(timeout: 2) }.to_not raise_exception
81
+ }.to have_deprecated_wait_while_present
82
+ end
83
+
84
+ it 'waits until the selector no longer matches' do
85
+ element = browser.link(name: 'add_select').wait_until(&:exists?)
86
+ browser.link(id: 'change_select').click
87
+ expect {
88
+ expect { element.wait_while_present }.not_to raise_error
89
+ }.to have_deprecated_wait_while_present
90
+ end
91
+ end
92
+ end
93
+
94
+ not_compliant_on :relaxed_locate do
95
+ describe '#when_present' do
96
+ it 'invokes subsequent method calls when the element becomes present' do
97
+ browser.a(id: 'show_bar').click
98
+
99
+ bar = browser.div(id: 'bar')
100
+ bar.when_present(2).click
101
+ expect(bar.text).to eq 'changed'
102
+ end
103
+
104
+ it 'times out when given a block' do
105
+ expect { browser.div(id: 'bar').when_present(1) {} }.to raise_error(Watir::Wait::TimeoutError)
106
+ end
107
+
108
+ it 'times out when not given a block' do
109
+ locator = '(\{:id=>"bar", :tag_name=>"div"\}|\{:tag_name=>"div", :id=>"bar"\})'
110
+ msg = /^timed out after 1 seconds, waiting for #{locator} to become present$/
111
+ expect { browser.div(id: 'bar').when_present(1).click }.to raise_error(Watir::Wait::TimeoutError, msg)
112
+ end
113
+
114
+ it 'responds to Element methods' do
115
+ decorator = browser.div.when_present
116
+
117
+ expect(decorator).to respond_to(:exist?)
118
+ expect(decorator).to respond_to(:present?)
119
+ expect(decorator).to respond_to(:click)
120
+ end
121
+
122
+ it 'delegates present? to element' do
123
+ Object.class_eval do
124
+ def present?
125
+ false
126
+ end
127
+ end
128
+ element = browser.a(id: 'show_bar').when_present(1)
129
+ expect(element).to be_present
130
+ end
131
+
132
+ it 'processes before calling present?' do
133
+ browser.a(id: 'show_bar').click
134
+ expect(browser.div(id: 'bar').when_present.present?).to be true
135
+ end
136
+ end
137
+
138
+ describe '#wait_until &:enabled?' do
139
+ it 'invokes subsequent method calls when the element becomes enabled' do
140
+ browser.a(id: 'enable_btn').click
141
+
142
+ btn = browser.button(id: 'btn')
143
+ btn.wait_until(timeout: 2, &:enabled?).click
144
+ Watir::Wait.while { btn.enabled? }
145
+ expect(btn.disabled?).to be true
146
+ end
147
+
148
+ it 'times out' do
149
+ error = Watir::Wait::TimeoutError
150
+ inspected = '#<Watir::Button: located: true; {:id=>"btn", :tag_name=>"button"}>'
151
+ message = "timed out after 1 seconds, waiting for true condition on #{inspected}"
152
+ element = browser.button(id: 'btn')
153
+ expect { element.wait_until(timeout: 1, &:enabled?).click }.to raise_error(error, message)
154
+ end
155
+
156
+ it 'responds to Element methods' do
157
+ element = browser.button.wait_until { true }
158
+
159
+ expect(element).to respond_to(:exist?)
160
+ expect(element).to respond_to(:present?)
161
+ expect(element).to respond_to(:click)
162
+ end
163
+
164
+ it 'can be chained with #wait_until &:present?' do
165
+ browser.a(id: 'show_and_enable_btn').click
166
+ browser.button(id: 'btn2').wait_until(&:present?).wait_until(&:enabled?).click
167
+
168
+ expect(browser.button(id: 'btn2')).to exist
169
+ expect(browser.button(id: 'btn2')).to be_enabled
170
+ end
171
+ end
172
+
173
+ context 'when acting on an element that is never present' do
174
+ it 'raises exception immediately' do
175
+ element = browser.link(id: 'not_there')
176
+ start_time = ::Time.now
177
+ expect { element.click }.to raise_exception(Watir::Exception::UnknownObjectException)
178
+ expect(::Time.now - start_time).to be < 1
179
+ end
180
+ end
181
+
182
+ context 'when acting on an element that eventually becomes present' do
183
+ it 'raises exception immediately' do
184
+ start_time = ::Time.now
185
+ browser.a(id: 'show_bar').click
186
+
187
+ expect { browser.div(id: 'bar').click }.to raise_unknown_object_exception
188
+
189
+ expect(::Time.now - start_time).to be < 1
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ describe Watir::Window do
196
+ not_compliant_on :relaxed_locate do
197
+ describe '#wait_until &:present?' do
198
+ before do
199
+ browser.goto WatirSpec.url_for('window_switching.html')
200
+ browser.a(id: 'open').click
201
+ Watir::Wait.until { browser.windows.size == 2 }
202
+ end
203
+
204
+ after do
205
+ browser.original_window.use
206
+ browser.windows.reject(&:current?).each(&:close)
207
+ end
208
+
209
+ it 'times out waiting for a non-present window' do
210
+ expect {
211
+ browser.window(title: 'noop').wait_until(timeout: 0.5, &:present?)
212
+ }.to raise_error(Watir::Wait::TimeoutError)
213
+ end
214
+ end
215
+ end
216
+ end
@@ -12,10 +12,12 @@ if defined?(RSpec)
12
12
  stale_exists
13
13
  stale_visible
14
14
  stale_present
15
+ select_all
15
16
  select_by
16
17
  value_button
17
18
  wait_until_present
18
- wait_while_present].freeze
19
+ wait_while_present
20
+ window_index].freeze
19
21
 
20
22
  DEPRECATION_WARNINGS.each do |deprecation|
21
23
  RSpec::Matchers.define "have_deprecated_#{deprecation}" do
@@ -117,27 +119,28 @@ if defined?(RSpec)
117
119
  end
118
120
  end
119
121
 
120
- RSpec::Matchers.define :execute_immediately do |timeout: 10|
122
+ RSpec::Matchers.define :execute_when_satisfied do |min: 0, max: nil|
123
+ max ||= min + 1
121
124
  match do |actual|
122
125
  original_timeout = Watir.default_timeout
123
- Watir.default_timeout = timeout
126
+ Watir.default_timeout = max
124
127
  begin
125
128
  start_time = ::Time.now
126
129
  actual.call
127
130
  @time_difference = ::Time.now - start_time
128
- @time_difference < timeout
131
+ @time_difference > min && @time_difference < max
129
132
  ensure
130
133
  Watir.default_timeout = original_timeout
131
134
  end
132
135
  end
133
136
 
134
137
  failure_message_when_negated do
135
- "expected action to take more than provided timeout (#{timeout} seconds), " \
138
+ "expected action to take less than #{min} seconds or more than #{max} seconds, " \
136
139
  "instead it took #{@time_difference} seconds"
137
140
  end
138
141
 
139
142
  failure_message do
140
- "expected action to take less than provided timeout (#{timeout} seconds), " \
143
+ "expected action to take more than #{min} seconds and less than #{max} seconds, " \
141
144
  "instead it took #{@time_difference} seconds"
142
145
  end
143
146
 
@@ -148,7 +151,7 @@ if defined?(RSpec)
148
151
 
149
152
  RSpec::Matchers.define :exist do |*args|
150
153
  match do |actual|
151
- actual.exists?(*args)
154
+ actual.exist?(*args)
152
155
  end
153
156
 
154
157
  failure_message do |obj|
@@ -1,396 +1,352 @@
1
1
  require 'watirspec_helper'
2
2
 
3
- describe Watir::Element do
4
- before do
5
- browser.goto WatirSpec.url_for('wait.html')
6
- end
7
-
8
- # TODO: This is deprecated; remove in future version
9
- not_compliant_on :relaxed_locate do
10
- describe '#when_present' do
11
- it 'invokes subsequent method calls when the element becomes present' do
12
- browser.a(id: 'show_bar').click
13
-
14
- bar = browser.div(id: 'bar')
15
- bar.when_present(2).click
16
- expect(bar.text).to eq 'changed'
17
- end
18
-
19
- it 'times out when given a block' do
20
- expect { browser.div(id: 'bar').when_present(1) {} }.to raise_error(Watir::Wait::TimeoutError)
21
- end
22
-
23
- it 'times out when not given a block' do
24
- locator = '(\{:id=>"bar", :tag_name=>"div"\}|\{:tag_name=>"div", :id=>"bar"\})'
25
- msg = /^timed out after 1 seconds, waiting for #{locator} to become present$/
26
- expect { browser.div(id: 'bar').when_present(1).click }.to raise_error(Watir::Wait::TimeoutError, msg)
27
- end
28
-
29
- it 'responds to Element methods' do
30
- decorator = browser.div.when_present
3
+ compliant_on :relaxed_locate do
4
+ describe Watir do
5
+ before do
6
+ browser.goto WatirSpec.url_for('wait.html')
7
+ end
31
8
 
32
- expect(decorator).to respond_to(:exist?)
33
- expect(decorator).to respond_to(:present?)
34
- expect(decorator).to respond_to(:click)
35
- end
9
+ describe '#default_timeout' do
10
+ context 'when no timeout is specified' do
11
+ it 'is used by Wait#until' do
12
+ expect { Watir::Wait.until { false } }.to wait_and_raise_timeout_exception(timeout: 1)
13
+ end
36
14
 
37
- it 'delegates present? to element' do
38
- Object.class_eval do
39
- def present?
40
- false
41
- end
15
+ it 'is used by Wait#while' do
16
+ expect { Watir::Wait.while { true } }.to wait_and_raise_timeout_exception(timeout: 1)
42
17
  end
43
- element = browser.a(id: 'show_bar').when_present(1)
44
- expect(element).to be_present
45
- end
46
18
 
47
- it 'processes before calling present?' do
48
- browser.a(id: 'show_bar').click
49
- expect(browser.div(id: 'bar').when_present.present?).to be true
19
+ it 'ensures all checks happen once even if time has expired' do
20
+ Watir.default_timeout = -1
21
+ expect { browser.link.click }.to_not raise_exception
22
+ ensure
23
+ Watir.default_timeout = 30
24
+ end
50
25
  end
51
26
  end
52
27
  end
53
28
 
54
- compliant_on :relaxed_locate do
55
- it 'clicking automatically waits until the element appears' do
56
- browser.a(id: 'show_bar').click
57
- expect { browser.div(id: 'bar').click }.to_not raise_exception
58
- expect(browser.div(id: 'bar').text).to eq 'changed'
59
- end
60
-
61
- it "raises exception if the element doesn't appear" do
62
- expect { browser.div(id: 'bar').click }.to raise_unknown_object_exception
63
- end
64
-
65
- it "raises exception if the element doesn't become enabled" do
66
- expect { browser.button(id: 'btn').click }.to raise_object_disabled_exception
29
+ describe Watir::Element do
30
+ before do
31
+ browser.goto WatirSpec.url_for('wait.html')
67
32
  end
68
- end
69
33
 
70
- not_compliant_on :relaxed_locate do
71
- describe '#wait_until &:enabled?' do
72
- it 'invokes subsequent method calls when the element becomes enabled' do
73
- browser.a(id: 'enable_btn').click
74
-
75
- btn = browser.button(id: 'btn')
76
- btn.wait_until(timeout: 2, &:enabled?).click
77
- Watir::Wait.while { btn.enabled? }
78
- expect(btn.disabled?).to be true
34
+ describe '#wait_until' do
35
+ it 'returns element for additional actions' do
36
+ element = browser.div(id: 'foo')
37
+ expect(element.wait_until(&:exist?)).to eq element
79
38
  end
80
39
 
81
- it 'times out' do
82
- error = Watir::Wait::TimeoutError
83
- inspected = '#<Watir::Button: located: true; {:id=>"btn", :tag_name=>"button"}>'
84
- message = "timed out after 1 seconds, waiting for true condition on #{inspected}"
85
- element = browser.button(id: 'btn')
86
- expect { element.wait_until(timeout: 1, &:enabled?).click }.to raise_error(error, message)
40
+ it 'accepts self in block' do
41
+ element = browser.div(id: 'bar')
42
+ browser.a(id: 'show_bar').click
43
+ expect { element.wait_until { |el| el.text == 'bar' } }.to_not raise_exception
87
44
  end
88
45
 
89
- it 'responds to Element methods' do
90
- element = browser.button.wait_until { true }
91
-
92
- expect(element).to respond_to(:exist?)
93
- expect(element).to respond_to(:present?)
94
- expect(element).to respond_to(:click)
46
+ it 'accepts any values in block' do
47
+ element = browser.div(id: 'bar')
48
+ expect { element.wait_until { true } }.to_not raise_exception
95
49
  end
96
50
 
97
- it 'can be chained with #wait_until &:present?' do
98
- browser.a(id: 'show_and_enable_btn').click
99
- browser.button(id: 'btn2').wait_until(&:present?).wait_until(&:enabled?).click
100
-
101
- expect(browser.button(id: 'btn2')).to exist
102
- expect(browser.button(id: 'btn2')).to be_enabled
51
+ it 'accepts just a timeout parameter' do
52
+ element = browser.div(id: 'bar')
53
+ expect { element.wait_until(timeout: 0) { true } }.to_not raise_exception
103
54
  end
104
- end
105
- end
106
55
 
107
- describe '#wait_until_present' do
108
- it 'waits until the element appears' do
109
- browser.a(id: 'show_bar').click
110
- expect {
111
- expect { browser.div(id: 'bar').wait_until_present(timeout: 5) }.to_not raise_exception
112
- }.to have_deprecated_wait_until_present
113
- end
56
+ it 'accepts just a message parameter' do
57
+ element = browser.div(id: 'bar')
58
+ expect { element.wait_until(message: 'no') { true } }.to_not raise_exception
59
+ end
114
60
 
115
- bug 'Safari does not recognize date type', :safari do
116
- it 'waits until the element re-appears' do
117
- browser.link(id: 'readd_bar').click
118
- expect {
119
- expect { browser.div(id: 'bar').wait_until_present }.to_not raise_exception
120
- }.to have_deprecated_wait_until_present
61
+ it 'accepts just an interval parameter' do
62
+ element = browser.div(id: 'bar')
63
+ expect { element.wait_until(interval: 0.1) { true } }.to_not raise_exception
121
64
  end
122
- end
123
65
 
124
- it "times out if the element doesn't appear" do
125
- inspected = '#<Watir::Div: located: true; {:id=>"bar", :tag_name=>"div"}>'
126
- error = Watir::Wait::TimeoutError
127
- message = "timed out after 1 seconds, waiting for #{inspected} to become present"
66
+ context 'accepts keywords instead of block' do
67
+ before { browser.refresh }
128
68
 
129
- expect {
130
- expect { browser.div(id: 'bar').wait_until_present(timeout: 1) }.to raise_error(error, message)
131
- }.to have_deprecated_wait_until_present
132
- end
69
+ it 'accepts text keyword' do
70
+ element = browser.div(id: 'bar')
71
+ browser.a(id: 'show_bar').click
72
+ expect { element.wait_until(text: 'bar') }.to_not raise_exception
73
+ end
133
74
 
134
- it 'uses provided interval' do
135
- element = browser.div(id: 'bar')
136
- expect(element).to receive(:present?).twice
75
+ it 'accepts regular expression value' do
76
+ element = browser.div(id: 'bar')
77
+ browser.a(id: 'show_bar').click
78
+ expect { element.wait_until(style: /block/) }.to_not raise_exception
79
+ end
137
80
 
138
- expect {
139
- expect { element.wait_until_present(timeout: 0.4, interval: 0.2) }.to raise_timeout_exception
140
- }.to have_deprecated_wait_until_present
141
- end
142
- end
81
+ it 'accepts multiple keywords' do
82
+ element = browser.div(id: 'bar')
83
+ browser.a(id: 'show_bar').click
84
+ expect { element.wait_until(text: 'bar', style: /block/) }.to_not raise_exception
85
+ end
143
86
 
144
- describe '#wait_while_present' do
145
- it 'waits until the element disappears' do
146
- browser.a(id: 'hide_foo').click
147
- expect {
148
- expect { browser.div(id: 'foo').wait_while_present(timeout: 2) }.to_not raise_exception
149
- }.to have_deprecated_wait_while_present
150
- end
87
+ it 'accepts custom keyword' do
88
+ element = browser.div(id: 'bar')
89
+ browser.a(id: 'show_bar').click
90
+ expect { element.wait_until(custom: 'bar') }.to_not raise_exception
91
+ end
151
92
 
152
- it "times out if the element doesn't disappear" do
153
- error = Watir::Wait::TimeoutError
154
- inspected = '#<Watir::Div: located: true; {:id=>"foo", :tag_name=>"div"}>'
155
- message = "timed out after 1 seconds, waiting for #{inspected} not to be present"
156
- expect {
157
- expect { browser.div(id: 'foo').wait_while_present(timeout: 1) }.to raise_error(error, message)
158
- }.to have_deprecated_wait_while_present
159
- end
93
+ it 'times out when single keyword not met' do
94
+ element = browser.div(id: 'bar')
95
+ expect { element.wait_until(id: 'foo') }.to raise_timeout_exception
96
+ end
160
97
 
161
- it 'uses provided interval' do
162
- error = Watir::Wait::TimeoutError
163
- element = browser.div(id: 'foo')
164
- expect(element).to receive(:present?).and_return(true).twice
98
+ it 'times out when one of multiple keywords not met' do
99
+ element = browser.div(id: 'bar')
100
+ expect { element.wait_until(id: 'bar', text: 'foo') }.to raise_timeout_exception
101
+ end
165
102
 
166
- expect {
167
- expect { element.wait_while_present(timeout: 0.4, interval: 0.2) }.to raise_error(error)
168
- }.to have_deprecated_wait_while_present
103
+ it 'times out when a custom keywords not met' do
104
+ element = browser.div(id: 'bar')
105
+ expect { element.wait_until(custom: 'foo') }.to raise_timeout_exception
106
+ end
107
+ end
169
108
  end
170
109
 
171
- it 'does not error when element goes stale' do
172
- element = browser.div(id: 'foo').locate
173
-
174
- allow(element).to receive(:stale?).and_return(false, true)
110
+ describe '#wait_while' do
111
+ it 'returns element for additional actions' do
112
+ element = browser.div(id: 'foo')
113
+ browser.a(id: 'hide_foo').click
114
+ expect(element.wait_while(&:present?)).to eq element
115
+ end
175
116
 
176
- browser.a(id: 'hide_foo').click
177
- expect {
178
- expect { element.wait_while_present(timeout: 1) }.to_not raise_exception
179
- }.to have_deprecated_wait_while_present
180
- end
117
+ it 'accepts any values in block' do
118
+ element = browser.div(id: 'foo')
119
+ expect { element.wait_while { false } }.to_not raise_exception
120
+ end
181
121
 
182
- it 'waits until the selector no longer matches' do
183
- element = browser.link(name: 'add_select').wait_until(&:exists?)
184
- browser.link(id: 'change_select').click
185
- expect {
186
- expect { element.wait_while_present }.not_to raise_error
187
- }.to have_deprecated_wait_while_present
188
- end
189
- end
122
+ it 'accepts just a timeout parameter' do
123
+ element = browser.div(id: 'foo')
124
+ expect { element.wait_while(timeout: 0) { false } }.to_not raise_exception
125
+ end
190
126
 
191
- describe '#wait_until' do
192
- it 'returns element for additional actions' do
193
- element = browser.div(id: 'foo')
194
- expect(element.wait_until(&:exist?)).to eq element
195
- end
127
+ it 'accepts just a message parameter' do
128
+ element = browser.div(id: 'foo')
129
+ expect { element.wait_while(message: 'no') { false } }.to_not raise_exception
130
+ end
196
131
 
197
- it 'accepts self in block' do
198
- element = browser.div(id: 'bar')
199
- browser.a(id: 'show_bar').click
200
- expect { element.wait_until { |el| el.text == 'bar' } }.to_not raise_exception
201
- end
132
+ it 'accepts just an interval parameter' do
133
+ element = browser.div(id: 'foo')
134
+ expect { element.wait_while(interval: 0.1) { false } }.to_not raise_exception
135
+ end
202
136
 
203
- it 'accepts any values in block' do
204
- element = browser.div(id: 'bar')
205
- expect { element.wait_until { true } }.to_not raise_exception
206
- end
137
+ context 'accepts keywords instead of block' do
138
+ bug 'Safari does not recognize date type', :safari do
139
+ it 'accepts text keyword' do
140
+ element = browser.div(id: 'foo')
141
+ browser.a(id: 'hide_foo').click
142
+ expect { element.wait_while(text: 'foo') }.to_not raise_exception
143
+ end
144
+ end
207
145
 
208
- it 'accepts just a timeout parameter' do
209
- element = browser.div(id: 'bar')
210
- expect { element.wait_until(timeout: 0) { true } }.to_not raise_exception
211
- end
146
+ it 'accepts regular expression value' do
147
+ element = browser.div(id: 'foo')
148
+ browser.a(id: 'hide_foo').click
149
+ expect { element.wait_while(style: /block/) }.to_not raise_exception
150
+ end
212
151
 
213
- it 'accepts just a message parameter' do
214
- element = browser.div(id: 'bar')
215
- expect { element.wait_until(message: 'no') { true } }.to_not raise_exception
216
- end
152
+ it 'accepts multiple keywords' do
153
+ element = browser.div(id: 'foo')
154
+ browser.a(id: 'hide_foo').click
155
+ expect { element.wait_while(text: 'foo', style: /block/) }.to_not raise_exception
156
+ end
217
157
 
218
- it 'accepts just an interval parameter' do
219
- element = browser.div(id: 'bar')
220
- expect { element.wait_until(interval: 0.1) { true } }.to_not raise_exception
221
- end
158
+ it 'accepts custom attributes' do
159
+ element = browser.div(id: 'foo')
160
+ browser.a(id: 'hide_foo').click
161
+ expect { element.wait_while(custom: '') }.to_not raise_exception
162
+ end
222
163
 
223
- context 'accepts keywords instead of block' do
224
- before { browser.refresh }
164
+ it 'accepts keywords and block' do
165
+ element = browser.div(id: 'foo')
166
+ browser.a(id: 'hide_foo').click
167
+ expect { element.wait_while(custom: '', &:present?) }.to_not raise_exception
168
+ end
225
169
 
226
- it 'accepts text keyword' do
227
- element = browser.div(id: 'bar')
228
- browser.a(id: 'show_bar').click
229
- expect { element.wait_until(text: 'bar') }.to_not raise_exception
230
- end
170
+ it 'browser accepts keywords' do
171
+ expect { browser.wait_until(title: 'wait test') }.to_not raise_exception
172
+ expect { browser.wait_until(title: 'wrong') }.to raise_timeout_exception
173
+ end
231
174
 
232
- it 'accepts regular expression value' do
233
- element = browser.div(id: 'bar')
234
- browser.a(id: 'show_bar').click
235
- expect { element.wait_until(style: /block/) }.to_not raise_exception
236
- end
175
+ it 'alert accepts keywords' do
176
+ browser.goto WatirSpec.url_for('alerts.html')
237
177
 
238
- it 'accepts multiple keywords' do
239
- element = browser.div(id: 'bar')
240
- browser.a(id: 'show_bar').click
241
- expect { element.wait_until(text: 'bar', style: /block/) }.to_not raise_exception
242
- end
178
+ begin
179
+ browser.button(id: 'alert').click
180
+ expect { browser.alert.wait_until(text: 'ok') }.to_not raise_exception
181
+ expect { browser.alert.wait_until(text: 'not ok') }.to raise_timeout_exception
182
+ ensure
183
+ browser.alert.ok
184
+ end
185
+ end
243
186
 
244
- it 'accepts custom keyword' do
245
- element = browser.div(id: 'bar')
246
- browser.a(id: 'show_bar').click
247
- expect { element.wait_until(custom: 'bar') }.to_not raise_exception
248
- end
187
+ it 'window accepts keywords' do
188
+ expect { browser.window.wait_until(title: 'wait test') }.to_not raise_exception
189
+ expect { browser.window.wait_until(title: 'wrong') }.to raise_timeout_exception
190
+ end
249
191
 
250
- it 'times out when single keyword not met' do
251
- element = browser.div(id: 'bar')
252
- expect { element.wait_until(id: 'foo') }.to raise_timeout_exception
253
- end
192
+ it 'times out when single keyword not met' do
193
+ element = browser.div(id: 'foo')
194
+ expect { element.wait_while(id: 'foo') }.to raise_timeout_exception
195
+ end
254
196
 
255
- it 'times out when one of multiple keywords not met' do
256
- element = browser.div(id: 'bar')
257
- expect { element.wait_until(id: 'bar', text: 'foo') }.to raise_timeout_exception
258
- end
197
+ it 'times out when one of multiple keywords not met' do
198
+ element = browser.div(id: 'foo')
199
+ browser.a(id: 'hide_foo').click
200
+ expect { element.wait_while(id: 'foo', style: /block/) }.to raise_timeout_exception
201
+ end
259
202
 
260
- it 'times out when a custom keywords not met' do
261
- element = browser.div(id: 'bar')
262
- expect { element.wait_until(custom: 'foo') }.to raise_timeout_exception
203
+ it 'times out when one of custom keywords not met' do
204
+ element = browser.div(id: 'foo')
205
+ expect { element.wait_while(custom: '') }.to raise_timeout_exception
206
+ end
263
207
  end
264
208
  end
265
- end
266
209
 
267
- describe '#wait_while' do
268
- it 'returns element for additional actions' do
269
- element = browser.div(id: 'foo')
270
- browser.a(id: 'hide_foo').click
271
- expect(element.wait_while(&:present?)).to eq element
210
+ context 'when acting on an element that is never present' do
211
+ it 'raises exception after timing out' do
212
+ element = browser.link(id: 'not_there')
213
+ expect { element.click }.to wait_and_raise_unknown_object_exception
214
+ end
272
215
  end
273
216
 
274
- it 'accepts any values in block' do
275
- element = browser.div(id: 'foo')
276
- expect { element.wait_while { false } }.to_not raise_exception
217
+ context 'when acting on an element that is already present' do
218
+ it 'does not wait' do
219
+ expect { browser.link.click }.to execute_when_satisfied(max: 1)
220
+ end
277
221
  end
278
222
 
279
- it 'accepts just a timeout parameter' do
280
- element = browser.div(id: 'foo')
281
- expect { element.wait_while(timeout: 0) { false } }.to_not raise_exception
282
- end
223
+ context 'when acting on an element that eventually becomes present' do
224
+ it 'waits until element is present and then takes action' do
225
+ expect {
226
+ browser.a(id: 'show_bar').click
227
+ browser.div(id: 'bar').click
228
+ }.to execute_when_satisfied(min: 1)
229
+ expect(browser.div(id: 'bar').text).to eq 'changed'
230
+ end
283
231
 
284
- it 'accepts just a message parameter' do
285
- element = browser.div(id: 'foo')
286
- expect { element.wait_while(message: 'no') { false } }.to_not raise_exception
232
+ it 'waits until text field present and then takes action' do
233
+ expect {
234
+ browser.a(id: 'show_textfield').click
235
+ browser.text_field(id: 'textfield').set 'Foo'
236
+ }.to execute_when_satisfied(min: 1)
237
+ end
287
238
  end
288
239
 
289
- it 'accepts just an interval parameter' do
290
- element = browser.div(id: 'foo')
291
- expect { element.wait_while(interval: 0.1) { false } }.to_not raise_exception
292
- end
240
+ context 'when acting on a read only text field' do
241
+ it 'waits and raises read only exception if never becomes writable' do
242
+ expect { browser.text_field(id: 'writable').set 'foo' }.to wait_and_raise_object_read_only_exception
243
+ end
293
244
 
294
- context 'accepts keywords instead of block' do
295
- bug 'Safari does not recognize date type', :safari do
296
- it 'accepts text keyword' do
297
- element = browser.div(id: 'foo')
298
- browser.a(id: 'hide_foo').click
299
- expect { element.wait_while(text: 'foo') }.to_not raise_exception
300
- end
245
+ it 'waits until writable' do
246
+ expect {
247
+ browser.a(id: 'make-writable').click
248
+ browser.text_field(id: 'writable').set 'foo'
249
+ }.to execute_when_satisfied(min: 1)
301
250
  end
251
+ end
302
252
 
303
- it 'accepts regular expression value' do
304
- element = browser.div(id: 'foo')
305
- browser.a(id: 'hide_foo').click
306
- expect { element.wait_while(style: /block/) }.to_not raise_exception
253
+ context 'when acting on a disabled button' do
254
+ it 'waits and raises exception if it never becomes enabled' do
255
+ expect { browser.button(id: 'btn').click }.to wait_and_raise_object_disabled_exception
307
256
  end
308
257
 
309
- it 'accepts multiple keywords' do
310
- element = browser.div(id: 'foo')
311
- browser.a(id: 'hide_foo').click
312
- expect { element.wait_while(text: 'foo', style: /block/) }.to_not raise_exception
258
+ it 'waits until enabled' do
259
+ expect {
260
+ browser.a(id: 'enable_btn').click
261
+ browser.button(id: 'btn').click
262
+ }.to execute_when_satisfied(min: 1)
313
263
  end
264
+ end
314
265
 
315
- it 'accepts custom attributes' do
316
- element = browser.div(id: 'foo')
317
- browser.a(id: 'hide_foo').click
318
- expect { element.wait_while(custom: '') }.to_not raise_exception
266
+ context 'when acting on an element with a parent' do
267
+ it 'raises exception after timing out if parent never present' do
268
+ element = browser.link(id: 'not_there')
269
+ expect { element.element.click }.to wait_and_raise_unknown_object_exception
319
270
  end
320
271
 
321
- it 'accepts keywords and block' do
322
- element = browser.div(id: 'foo')
323
- browser.a(id: 'hide_foo').click
324
- expect { element.wait_while(custom: '', &:present?) }.to_not raise_exception
272
+ it 'raises exception after timing out when element from a collection whose parent is never present' do
273
+ element = browser.link(id: 'not_there')
274
+ expect { element.elements[2].click }.to wait_and_raise_unknown_object_exception
325
275
  end
326
276
 
327
- it 'browser accepts keywords' do
328
- expect { browser.wait_until(title: 'wait test') }.to_not raise_exception
329
- expect { browser.wait_until(title: 'wrong') }.to raise_timeout_exception
277
+ it 'does not wait for parent element to be present when querying child element' do
278
+ el = browser.element(id: 'not_there').element(id: 'doesnt_matter')
279
+ expect { el.present? }.to execute_when_satisfied(max: 1)
330
280
  end
281
+ end
282
+ end
331
283
 
332
- it 'alert accepts keywords' do
333
- browser.goto WatirSpec.url_for('alerts.html')
284
+ describe Watir::ElementCollection do
285
+ before do
286
+ browser.goto WatirSpec.url_for('wait.html')
287
+ end
334
288
 
335
- begin
336
- browser.button(id: 'alert').click
337
- expect { browser.alert.wait_until(text: 'ok') }.to_not raise_exception
338
- expect { browser.alert.wait_until(text: 'not ok') }.to raise_timeout_exception
339
- ensure
340
- browser.alert.ok
341
- end
289
+ describe '#wait_until' do
290
+ it 'returns collection' do
291
+ elements = browser.divs
292
+ expect(elements.wait_until(&:exist?)).to eq elements
342
293
  end
343
294
 
344
- it 'window accepts keywords' do
345
- expect { browser.window.wait_until(title: 'wait test') }.to_not raise_exception
346
- expect { browser.window.wait_until(title: 'wrong') }.to raise_timeout_exception
295
+ it 'times out when waiting for non-empty collection' do
296
+ expect { browser.divs.wait_until(&:empty?) }.to raise_timeout_exception
347
297
  end
348
298
 
349
- it 'times out when single keyword not met' do
350
- element = browser.div(id: 'foo')
351
- expect { element.wait_while(id: 'foo') }.to raise_timeout_exception
299
+ it 'provides matching collection when exists' do
300
+ expect {
301
+ browser.a(id: 'add_foobar').click
302
+ browser.divs(id: 'foobar').wait_until(&:exists?)
303
+ }.to execute_when_satisfied(min: 1)
352
304
  end
353
305
 
354
- it 'times out when one of multiple keywords not met' do
355
- element = browser.div(id: 'foo')
356
- browser.a(id: 'hide_foo').click
357
- expect { element.wait_while(id: 'foo', style: /block/) }.to raise_timeout_exception
306
+ it 'accepts self in block' do
307
+ expect {
308
+ browser.a(id: 'add_foobar').click
309
+ browser.divs.wait_until { |els| els.size == 7 }
310
+ }.to execute_when_satisfied(min: 1)
358
311
  end
359
312
 
360
- it 'times out when one of custom keywords not met' do
361
- element = browser.div(id: 'foo')
362
- expect { element.wait_while(custom: '') }.to raise_timeout_exception
313
+ it 'accepts attributes to evaluate' do
314
+ expect {
315
+ browser.a(id: 'add_foobar').click
316
+ browser.divs.wait_until(size: 7)
317
+ }.to execute_when_satisfied(min: 1)
363
318
  end
364
319
  end
365
- end
366
- end
367
-
368
- describe Watir do
369
- describe '#default_timeout' do
370
- before do
371
- browser.goto WatirSpec.url_for('wait.html')
372
- end
373
320
 
374
- context 'when no timeout is specified' do
375
- it 'is used by Wait#until' do
376
- expect { Watir::Wait.until { false } }.to wait_and_raise_timeout_exception(timeout: 1)
321
+ describe '#wait_while' do
322
+ it 'returns collection' do
323
+ elements = browser.divs
324
+ expect(elements.wait_while(&:empty?)).to eq elements
377
325
  end
378
326
 
379
- it 'is used by Wait#while' do
380
- expect { Watir::Wait.while { true } }.to wait_and_raise_timeout_exception(timeout: 1)
327
+ it 'times out when waiting for non-empty collection' do
328
+ elements = browser.divs
329
+ expect { elements.wait_while(&:exists?) }.to raise_timeout_exception
381
330
  end
382
331
 
383
- it 'is used by Element#wait_until_present' do
332
+ it 'provides matching collection when exists' do
384
333
  expect {
385
- expect { browser.div(id: 'bar').wait_until_present }.to wait_and_raise_timeout_exception(timeout: 1)
386
- }.to have_deprecated_wait_until_present
334
+ browser.a(id: 'remove_foo').click
335
+ browser.divs(id: 'foo').wait_while(&:exists?)
336
+ }.to execute_when_satisfied(min: 1)
387
337
  end
388
338
 
389
- it 'is used by Element#wait_while_present' do
339
+ it 'accepts self in block' do
390
340
  expect {
391
- expect { browser.div(id: 'foo').wait_while_present }.to wait_and_raise_timeout_exception(timeout: 1)
392
- }.to have_deprecated_wait_while_present
341
+ browser.a(id: 'add_foobar').click
342
+ browser.divs.wait_while { |els| els.size == 6 }
343
+ }.to execute_when_satisfied(min: 1)
393
344
  end
394
345
  end
346
+
347
+ it 'waits for parent element to be present before locating' do
348
+ els = browser.element(id: /not|there/).elements(id: 'doesnt_matter')
349
+ expect { els.to_a }.to wait_and_raise_unknown_object_exception
350
+ end
395
351
  end
396
352
  end