capybara 3.29.0 → 3.31.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +40 -1
  3. data/README.md +1 -1
  4. data/lib/capybara/config.rb +7 -3
  5. data/lib/capybara/dsl.rb +10 -2
  6. data/lib/capybara/helpers.rb +3 -1
  7. data/lib/capybara/minitest.rb +18 -4
  8. data/lib/capybara/node/actions.rb +23 -19
  9. data/lib/capybara/node/document.rb +2 -2
  10. data/lib/capybara/node/document_matchers.rb +3 -3
  11. data/lib/capybara/node/element.rb +21 -16
  12. data/lib/capybara/node/finders.rb +17 -11
  13. data/lib/capybara/node/matchers.rb +60 -45
  14. data/lib/capybara/node/simple.rb +4 -2
  15. data/lib/capybara/queries/ancestor_query.rb +1 -1
  16. data/lib/capybara/queries/base_query.rb +2 -1
  17. data/lib/capybara/queries/selector_query.rb +17 -4
  18. data/lib/capybara/queries/sibling_query.rb +1 -1
  19. data/lib/capybara/rack_test/browser.rb +4 -1
  20. data/lib/capybara/rack_test/driver.rb +1 -1
  21. data/lib/capybara/rack_test/form.rb +1 -1
  22. data/lib/capybara/rack_test/node.rb +34 -9
  23. data/lib/capybara/result.rb +24 -4
  24. data/lib/capybara/rspec/matchers.rb +27 -27
  25. data/lib/capybara/rspec/matchers/base.rb +12 -6
  26. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  27. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  28. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  29. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  30. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  31. data/lib/capybara/rspec/matchers/have_text.rb +2 -2
  32. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  33. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  34. data/lib/capybara/rspec/matchers/match_style.rb +2 -2
  35. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  36. data/lib/capybara/selector.rb +24 -16
  37. data/lib/capybara/selector/css.rb +1 -1
  38. data/lib/capybara/selector/definition.rb +2 -2
  39. data/lib/capybara/selector/definition/button.rb +7 -2
  40. data/lib/capybara/selector/definition/checkbox.rb +2 -2
  41. data/lib/capybara/selector/definition/css.rb +3 -1
  42. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  43. data/lib/capybara/selector/definition/datalist_option.rb +1 -1
  44. data/lib/capybara/selector/definition/element.rb +1 -1
  45. data/lib/capybara/selector/definition/field.rb +1 -1
  46. data/lib/capybara/selector/definition/file_field.rb +1 -1
  47. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  48. data/lib/capybara/selector/definition/label.rb +4 -2
  49. data/lib/capybara/selector/definition/radio_button.rb +2 -2
  50. data/lib/capybara/selector/definition/select.rb +32 -13
  51. data/lib/capybara/selector/definition/table.rb +5 -2
  52. data/lib/capybara/selector/filter_set.rb +11 -9
  53. data/lib/capybara/selector/filters/base.rb +6 -1
  54. data/lib/capybara/selector/filters/locator_filter.rb +1 -1
  55. data/lib/capybara/selector/selector.rb +4 -2
  56. data/lib/capybara/selenium/driver.rb +19 -11
  57. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
  58. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
  59. data/lib/capybara/selenium/extensions/html5_drag.rb +30 -13
  60. data/lib/capybara/selenium/node.rb +29 -10
  61. data/lib/capybara/selenium/nodes/chrome_node.rb +11 -5
  62. data/lib/capybara/selenium/nodes/edge_node.rb +4 -2
  63. data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
  64. data/lib/capybara/server.rb +15 -3
  65. data/lib/capybara/server/checker.rb +1 -1
  66. data/lib/capybara/server/middleware.rb +20 -10
  67. data/lib/capybara/session.rb +40 -23
  68. data/lib/capybara/session/config.rb +6 -2
  69. data/lib/capybara/session/matchers.rb +6 -6
  70. data/lib/capybara/spec/public/test.js +51 -6
  71. data/lib/capybara/spec/session/all_spec.rb +60 -5
  72. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  73. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  74. data/lib/capybara/spec/session/click_button_spec.rb +5 -0
  75. data/lib/capybara/spec/session/fill_in_spec.rb +20 -0
  76. data/lib/capybara/spec/session/find_spec.rb +20 -0
  77. data/lib/capybara/spec/session/has_css_spec.rb +3 -3
  78. data/lib/capybara/spec/session/has_select_spec.rb +28 -0
  79. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  80. data/lib/capybara/spec/session/has_text_spec.rb +35 -0
  81. data/lib/capybara/spec/session/node_spec.rb +106 -2
  82. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  83. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  84. data/lib/capybara/spec/session/selectors_spec.rb +15 -2
  85. data/lib/capybara/spec/views/form.erb +11 -1
  86. data/lib/capybara/version.rb +1 -1
  87. data/spec/dsl_spec.rb +2 -2
  88. data/spec/minitest_spec_spec.rb +46 -46
  89. data/spec/rack_test_spec.rb +0 -1
  90. data/spec/regexp_dissassembler_spec.rb +45 -37
  91. data/spec/result_spec.rb +7 -3
  92. data/spec/rspec/features_spec.rb +1 -0
  93. data/spec/rspec/shared_spec_matchers.rb +3 -3
  94. data/spec/rspec_spec.rb +4 -4
  95. data/spec/selenium_spec_chrome.rb +5 -4
  96. data/spec/selenium_spec_firefox.rb +7 -2
  97. data/spec/server_spec.rb +42 -0
  98. data/spec/session_spec.rb +1 -1
  99. data/spec/shared_selenium_node.rb +3 -3
  100. data/spec/shared_selenium_session.rb +8 -7
  101. metadata +3 -3
@@ -20,7 +20,7 @@ module Capybara
20
20
  # @return [true]
21
21
  #
22
22
  def assert_current_path(path, **options)
23
- _verify_current_path(path, options) do |query|
23
+ _verify_current_path(path, **options) do |query|
24
24
  raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
25
25
  end
26
26
  end
@@ -36,7 +36,7 @@ module Capybara
36
36
  # @return [true]
37
37
  #
38
38
  def assert_no_current_path(path, **options)
39
- _verify_current_path(path, options) do |query|
39
+ _verify_current_path(path, **options) do |query|
40
40
  raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self)
41
41
  end
42
42
  end
@@ -51,7 +51,7 @@ module Capybara
51
51
  # @return [Boolean]
52
52
  #
53
53
  def has_current_path?(path, **options)
54
- make_predicate(options) { assert_current_path(path, options) }
54
+ make_predicate(options) { assert_current_path(path, **options) }
55
55
  end
56
56
 
57
57
  ##
@@ -64,13 +64,13 @@ module Capybara
64
64
  # @return [Boolean]
65
65
  #
66
66
  def has_no_current_path?(path, **options)
67
- make_predicate(options) { assert_no_current_path(path, options) }
67
+ make_predicate(options) { assert_no_current_path(path, **options) }
68
68
  end
69
69
 
70
70
  private
71
71
 
72
- def _verify_current_path(path, options)
73
- query = Capybara::Queries::CurrentPathQuery.new(path, options)
72
+ def _verify_current_path(path, **options)
73
+ query = Capybara::Queries::CurrentPathQuery.new(path, **options)
74
74
  document.synchronize(query.wait) do
75
75
  yield(query)
76
76
  end
@@ -1,21 +1,57 @@
1
1
  var activeRequests = 0;
2
2
  $(function() {
3
3
  $('#change').text('I changed it');
4
- $('#drag, #drag_scroll, #drag_link').draggable();
4
+ $('#drag, #drag_scroll, #drag_link').draggable({
5
+ start: function(event, ui){
6
+ $(document.body).append(
7
+ "<div class='drag_start'>Dragged!" +
8
+ (event.altKey ? "-alt" : "") +
9
+ (event.ctrlKey ? "-ctrl" : "") +
10
+ (event.metaKey ? "-meta" : "") +
11
+ (event.shiftKey ? "-shift" : "") +
12
+ "</div>"
13
+ );
14
+ }
15
+ });
5
16
  $('#drop, #drop_scroll').droppable({
6
17
  tolerance: 'touch',
7
18
  drop: function(event, ui) {
8
19
  ui.draggable.remove();
9
- $(this).html('Dropped!');
20
+ $(this).html(
21
+ "Dropped!" +
22
+ (event.altKey ? "-alt" : "") +
23
+ (event.ctrlKey ? "-ctrl" : "") +
24
+ (event.metaKey ? "-meta" : "") +
25
+ (event.shiftKey ? "-shift" : "")
26
+ );
10
27
  }
11
28
  });
12
29
  $('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){
30
+ $(document.body).append(
31
+ "<div class='drag_start'>HTML5 Dragged!" +
32
+ (event.altKey ? "-alt" : "") +
33
+ (event.ctrlKey ? "-ctrl" : "") +
34
+ (event.metaKey ? "-meta" : "") +
35
+ (event.shiftKey ? "-shift" : "") +
36
+ "</div>"
37
+ );
13
38
  ev.originalEvent.dataTransfer.setData("text", ev.target.id);
14
39
  });
40
+ $('#drag_html5, #drag_html5_scroll').on('dragend', function(ev){
41
+ $(this).after('<div class="log">DragEnd with client position: ' + ev.clientX + ',' + ev.clientY)
42
+ });
15
43
  $('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
16
44
  $(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
17
45
  if ($(this).hasClass('drop')) { ev.preventDefault(); }
18
46
  });
47
+ $('#drop_html5, #drop_html5_scroll').on('dragleave', function(ev){
48
+ $(this).after('<div class="log">DragLeave with client position: ' + ev.clientX + ',' + ev.clientY)
49
+ if ($(this).hasClass('drop')) { ev.preventDefault(); }
50
+ });
51
+ $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
52
+ $(this).after('<div class="log">Drop with client position: ' + ev.clientX + ',' + ev.clientY)
53
+ if ($(this).hasClass('drop')) { ev.preventDefault(); }
54
+ });
19
55
  $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
20
56
  ev.preventDefault();
21
57
  var oev = ev.originalEvent;
@@ -27,10 +63,19 @@ $(function() {
27
63
  $(this).append('HTML5 Dropped file: ' + file.name);
28
64
  } else {
29
65
  var _this = this;
30
- var callback = (function(type){
31
- return function(s){
32
- $(_this).append('HTML5 Dropped string: ' + type + ' ' + s)
33
- }
66
+ var callback = (function(type) {
67
+ return function(s) {
68
+ $(_this).append(
69
+ "HTML5 Dropped string: " +
70
+ type +
71
+ " " +
72
+ s +
73
+ (ev.altKey ? "-alt" : "") +
74
+ (ev.ctrlKey ? "-ctrl" : "") +
75
+ (ev.metaKey ? "-meta" : "") +
76
+ (ev.shiftKey ? "-shift" : "")
77
+ );
78
+ };
34
79
  })(item.type);
35
80
  item.getAsString(callback);
36
81
  }
@@ -36,10 +36,50 @@ Capybara::SpecHelper.spec '#all' do
36
36
  expect(@result).to include('Smith', 'John', 'John Smith')
37
37
  end
38
38
 
39
+ it 'should allow reversing the order' do
40
+ @session.visit('/form')
41
+ fields = @session.all(:fillable_field, 'Name', exact: false).to_a
42
+ reverse_fields = @session.all(:fillable_field, 'Name', order: :reverse, exact: false).to_a
43
+ expect(fields).to eq(reverse_fields.reverse)
44
+ end
45
+
39
46
  it 'should raise an error when given invalid options' do
40
47
  expect { @session.all('//p', schmoo: 'foo') }.to raise_error(ArgumentError)
41
48
  end
42
49
 
50
+ it 'should not reload by default', requires: [:driver] do
51
+ paras = @session.all(:css, 'p', minimum: 3)
52
+ expect { paras[0].text }.not_to raise_error
53
+ @session.refresh
54
+ expect { paras[0].text }.to raise_error do |err|
55
+ expect(err).to be_an_invalid_element_error(@session)
56
+ end
57
+ end
58
+
59
+ context 'with allow_reload' do
60
+ it 'should reload if true' do
61
+ paras = @session.all(:css, 'p', allow_reload: true, minimum: 3)
62
+ expect { paras[0].text }.not_to raise_error
63
+ @session.refresh
64
+ sleep 1 # Ensure page has started to reload
65
+ expect(paras[0]).to have_text('Lorem ipsum dolor')
66
+ expect(paras[1]).to have_text('Duis aute irure dolor')
67
+ end
68
+
69
+ it 'should not reload if false', requires: [:driver] do
70
+ paras = @session.all(:css, 'p', allow_reload: false, minimum: 3)
71
+ expect { paras[0].text }.not_to raise_error
72
+ @session.refresh
73
+ sleep 1 # Ensure page has started to reload
74
+ expect { paras[0].text }.to raise_error do |err|
75
+ expect(err).to be_an_invalid_element_error(@session)
76
+ end
77
+ expect { paras[2].text }.to raise_error do |err|
78
+ expect(err).to be_an_invalid_element_error(@session)
79
+ end
80
+ end
81
+ end
82
+
43
83
  context 'with css selectors' do
44
84
  it 'should find all elements using the given selector' do
45
85
  expect(@session.all(:css, 'h1').first.text).to eq('This is a test')
@@ -128,6 +168,7 @@ Capybara::SpecHelper.spec '#all' do
128
168
  it 'should succeed when the number of elements founds matches the expectation' do
129
169
  expect { @session.all(:css, 'h1, p', count: 4) }.not_to raise_error
130
170
  end
171
+
131
172
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
132
173
  expect { @session.all(:css, 'h1, p', count: 5) }.to raise_error(Capybara::ExpectationNotMet)
133
174
  end
@@ -137,6 +178,7 @@ Capybara::SpecHelper.spec '#all' do
137
178
  it 'should succeed when the number of elements founds matches the expectation' do
138
179
  expect { @session.all(:css, 'h1, p', minimum: 0) }.not_to raise_error
139
180
  end
181
+
140
182
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
141
183
  expect { @session.all(:css, 'h1, p', minimum: 5) }.to raise_error(Capybara::ExpectationNotMet)
142
184
  end
@@ -146,6 +188,7 @@ Capybara::SpecHelper.spec '#all' do
146
188
  it 'should succeed when the number of elements founds matches the expectation' do
147
189
  expect { @session.all(:css, 'h1, p', maximum: 4) }.not_to raise_error
148
190
  end
191
+
149
192
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
150
193
  expect { @session.all(:css, 'h1, p', maximum: 0) }.to raise_error(Capybara::ExpectationNotMet)
151
194
  end
@@ -155,6 +198,7 @@ Capybara::SpecHelper.spec '#all' do
155
198
  it 'should succeed when the number of elements founds matches the expectation' do
156
199
  expect { @session.all(:css, 'h1, p', between: 2..7) }.not_to raise_error
157
200
  end
201
+
158
202
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
159
203
  expect { @session.all(:css, 'h1, p', between: 0..3) }.to raise_error(Capybara::ExpectationNotMet)
160
204
  end
@@ -165,6 +209,13 @@ Capybara::SpecHelper.spec '#all' do
165
209
  expect { @session.all(:css, 'h1, p', between: 5..) }.to raise_error(Capybara::ExpectationNotMet)
166
210
  end
167
211
  TEST
212
+
213
+ eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.6
214
+ it'treats a beginless range as maximum' do
215
+ expect { @session.all(:css, 'h1, p', between: ..7) }.not_to raise_error
216
+ expect { @session.all(:css, 'h1, p', between: ..3) }.to raise_error(Capybara::ExpectationNotMet)
217
+ end
218
+ TEST
168
219
  end
169
220
 
170
221
  context 'with multiple count filters' do
@@ -173,32 +224,36 @@ Capybara::SpecHelper.spec '#all' do
173
224
  minimum: 5,
174
225
  maximum: 0,
175
226
  between: 0..3 }
176
- expect { @session.all(:css, 'h1, p', o) }.not_to raise_error
227
+ expect { @session.all(:css, 'h1, p', **o) }.not_to raise_error
177
228
  end
229
+
178
230
  context 'with no :count expectation' do
179
231
  it 'fails if :minimum is not met' do
180
232
  o = { minimum: 5,
181
233
  maximum: 4,
182
234
  between: 2..7 }
183
- expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
235
+ expect { @session.all(:css, 'h1, p', **o) }.to raise_error(Capybara::ExpectationNotMet)
184
236
  end
237
+
185
238
  it 'fails if :maximum is not met' do
186
239
  o = { minimum: 0,
187
240
  maximum: 0,
188
241
  between: 2..7 }
189
- expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
242
+ expect { @session.all(:css, 'h1, p', **o) }.to raise_error(Capybara::ExpectationNotMet)
190
243
  end
244
+
191
245
  it 'fails if :between is not met' do
192
246
  o = { minimum: 0,
193
247
  maximum: 4,
194
248
  between: 0..3 }
195
- expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
249
+ expect { @session.all(:css, 'h1, p', **o) }.to raise_error(Capybara::ExpectationNotMet)
196
250
  end
251
+
197
252
  it 'succeeds if all combineable expectations are met' do
198
253
  o = { minimum: 0,
199
254
  maximum: 4,
200
255
  between: 2..7 }
201
- expect { @session.all(:css, 'h1, p', o) }.not_to raise_error
256
+ expect { @session.all(:css, 'h1, p', **o) }.not_to raise_error
202
257
  end
203
258
  end
204
259
  end
@@ -20,6 +20,11 @@ Capybara::SpecHelper.spec '#ancestor' do
20
20
  expect(el.ancestor('//div', text: "Ancestor\nAncestor\nAncestor")[:id]).to eq('ancestor3')
21
21
  end
22
22
 
23
+ it 'should find the closest ancestor' do
24
+ el = @session.find(:css, '#child')
25
+ expect(el.ancestor('.//div', order: :reverse, match: :first)[:id]).to eq('ancestor1')
26
+ end
27
+
23
28
  it 'should raise an error if there are multiple matches' do
24
29
  el = @session.find(:css, '#child')
25
30
  expect { el.ancestor('//div') }.to raise_error(Capybara::Ambiguous)
@@ -157,32 +157,36 @@ Capybara::SpecHelper.spec '#assert_text' do
157
157
  minimum: 6,
158
158
  maximum: 0,
159
159
  between: 0..4 }
160
- expect { @session.assert_text('Header', o) }.not_to raise_error
160
+ expect { @session.assert_text('Header', **o) }.not_to raise_error
161
161
  end
162
+
162
163
  context 'with no :count expectation' do
163
164
  it 'fails if :minimum is not met' do
164
165
  o = { minimum: 6,
165
166
  maximum: 5,
166
167
  between: 2..7 }
167
- expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
168
+ expect { @session.assert_text('Header', **o) }.to raise_error(Capybara::ExpectationNotMet)
168
169
  end
170
+
169
171
  it 'fails if :maximum is not met' do
170
172
  o = { minimum: 0,
171
173
  maximum: 0,
172
174
  between: 2..7 }
173
- expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
175
+ expect { @session.assert_text('Header', **o) }.to raise_error(Capybara::ExpectationNotMet)
174
176
  end
177
+
175
178
  it 'fails if :between is not met' do
176
179
  o = { minimum: 0,
177
180
  maximum: 5,
178
181
  between: 0..4 }
179
- expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
182
+ expect { @session.assert_text('Header', **o) }.to raise_error(Capybara::ExpectationNotMet)
180
183
  end
184
+
181
185
  it 'succeeds if all combineable expectations are met' do
182
186
  o = { minimum: 0,
183
187
  maximum: 5,
184
188
  between: 2..7 }
185
- expect { @session.assert_text('Header', o) }.not_to raise_error
189
+ expect { @session.assert_text('Header', **o) }.not_to raise_error
186
190
  end
187
191
  end
188
192
  end
@@ -186,6 +186,11 @@ Capybara::SpecHelper.spec '#click_button' do
186
186
  @session.click_button(name: 'form[awesome]')
187
187
  expect(extract_results(@session)['first_name']).to eq('John')
188
188
  end
189
+
190
+ it 'should submit by specific button name regex' do
191
+ @session.click_button(name: /form\[awes.*\]/)
192
+ expect(extract_results(@session)['first_name']).to eq('John')
193
+ end
189
194
  end
190
195
 
191
196
  context 'with fields associated with the form using the form attribute', requires: [:form_attribute] do
@@ -95,6 +95,26 @@ Capybara::SpecHelper.spec '#fill_in' do
95
95
  expect(extract_results(@session)['html5_color']).to eq('#112233')
96
96
  end
97
97
 
98
+ describe 'with input[type="range"]' do
99
+ it 'should set the range slider correctly' do
100
+ @session.fill_in('form_age', with: 51)
101
+ @session.click_button('awesome')
102
+ expect(extract_results(@session)['age'].to_f).to eq 51
103
+ end
104
+
105
+ it 'should set the range slider to valid values' do
106
+ @session.fill_in('form_age', with: '37.6')
107
+ @session.click_button('awesome')
108
+ expect(extract_results(@session)['age'].to_f).to eq 37.5
109
+ end
110
+
111
+ it 'should respect the range slider limits' do
112
+ @session.fill_in('form_age', with: '3')
113
+ @session.click_button('awesome')
114
+ expect(extract_results(@session)['age'].to_f).to eq 13
115
+ end
116
+ end
117
+
98
118
  it 'should fill in a field with a custom type' do
99
119
  @session.fill_in('Schmooo', with: 'Schmooo is the game')
100
120
  @session.click_button('awesome')
@@ -275,14 +275,17 @@ Capybara::SpecHelper.spec '#find' do
275
275
  @session.find(:css, '.multiple', match: :one)
276
276
  end.to raise_error(Capybara::Ambiguous)
277
277
  end
278
+
278
279
  it 'raises an error even if there the match is exact and the others are inexact' do
279
280
  expect do
280
281
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], exact: false, match: :one)
281
282
  end.to raise_error(Capybara::Ambiguous)
282
283
  end
284
+
283
285
  it 'returns the element if there is only one' do
284
286
  expect(@session.find(:css, '.singular', match: :one).text).to eq('singular')
285
287
  end
288
+
286
289
  it 'raises an error if there is no match' do
287
290
  expect do
288
291
  @session.find(:css, '.does-not-exist', match: :one)
@@ -294,6 +297,7 @@ Capybara::SpecHelper.spec '#find' do
294
297
  it 'returns the first matched element' do
295
298
  expect(@session.find(:css, '.multiple', match: :first).text).to eq('multiple one')
296
299
  end
300
+
297
301
  it 'raises an error if there is no match' do
298
302
  expect do
299
303
  @session.find(:css, '.does-not-exist', match: :first)
@@ -308,19 +312,23 @@ Capybara::SpecHelper.spec '#find' do
308
312
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: false)
309
313
  end.to raise_error(Capybara::Ambiguous)
310
314
  end
315
+
311
316
  it 'finds a single exact match when there also are inexact matches' do
312
317
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: false)
313
318
  expect(result.text).to eq('almost singular')
314
319
  end
320
+
315
321
  it 'raises an error when there are multiple inexact matches' do
316
322
  expect do
317
323
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: false)
318
324
  end.to raise_error(Capybara::Ambiguous)
319
325
  end
326
+
320
327
  it 'finds a single inexact match' do
321
328
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: false)
322
329
  expect(result.text).to eq('almost singular but not quite')
323
330
  end
331
+
324
332
  it 'raises an error if there is no match' do
325
333
  expect do
326
334
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: false)
@@ -334,20 +342,24 @@ Capybara::SpecHelper.spec '#find' do
334
342
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: true)
335
343
  end.to raise_error(Capybara::Ambiguous)
336
344
  end
345
+
337
346
  it 'finds a single exact match when there also are inexact matches' do
338
347
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: true)
339
348
  expect(result.text).to eq('almost singular')
340
349
  end
350
+
341
351
  it 'raises an error when there are multiple inexact matches' do
342
352
  expect do
343
353
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: true)
344
354
  end.to raise_error(Capybara::ElementNotFound)
345
355
  end
356
+
346
357
  it 'raises an error when there is a single inexact matches' do
347
358
  expect do
348
359
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: true)
349
360
  end.to raise_error(Capybara::ElementNotFound)
350
361
  end
362
+
351
363
  it 'raises an error if there is no match' do
352
364
  expect do
353
365
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: true)
@@ -362,18 +374,22 @@ Capybara::SpecHelper.spec '#find' do
362
374
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: false)
363
375
  expect(result.text).to eq('multiple one')
364
376
  end
377
+
365
378
  it 'finds a single exact match when there also are inexact matches' do
366
379
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: false)
367
380
  expect(result.text).to eq('almost singular')
368
381
  end
382
+
369
383
  it 'picks the first one when there are multiple inexact matches' do
370
384
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: false)
371
385
  expect(result.text).to eq('almost singular but not quite')
372
386
  end
387
+
373
388
  it 'finds a single inexact match' do
374
389
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: false)
375
390
  expect(result.text).to eq('almost singular but not quite')
376
391
  end
392
+
377
393
  it 'raises an error if there is no match' do
378
394
  expect do
379
395
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: false)
@@ -386,20 +402,24 @@ Capybara::SpecHelper.spec '#find' do
386
402
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: true)
387
403
  expect(result.text).to eq('multiple one')
388
404
  end
405
+
389
406
  it 'finds a single exact match when there also are inexact matches' do
390
407
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: true)
391
408
  expect(result.text).to eq('almost singular')
392
409
  end
410
+
393
411
  it 'raises an error if there are multiple inexact matches' do
394
412
  expect do
395
413
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: true)
396
414
  end.to raise_error(Capybara::ElementNotFound)
397
415
  end
416
+
398
417
  it 'raises an error if there is a single inexact match' do
399
418
  expect do
400
419
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: true)
401
420
  end.to raise_error(Capybara::ElementNotFound)
402
421
  end
422
+
403
423
  it 'raises an error if there is no match' do
404
424
  expect do
405
425
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: true)