capybara 3.8.2 → 3.9.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +10 -0
  3. data/lib/capybara.rb +32 -10
  4. data/lib/capybara/config.rb +1 -0
  5. data/lib/capybara/dsl.rb +2 -2
  6. data/lib/capybara/helpers.rb +1 -0
  7. data/lib/capybara/node/actions.rb +4 -0
  8. data/lib/capybara/node/base.rb +1 -0
  9. data/lib/capybara/node/element.rb +3 -0
  10. data/lib/capybara/node/finders.rb +2 -0
  11. data/lib/capybara/node/simple.rb +1 -0
  12. data/lib/capybara/queries/base_query.rb +1 -0
  13. data/lib/capybara/queries/match_query.rb +1 -0
  14. data/lib/capybara/queries/selector_query.rb +34 -37
  15. data/lib/capybara/queries/text_query.rb +2 -0
  16. data/lib/capybara/rack_test/browser.rb +1 -0
  17. data/lib/capybara/rack_test/driver.rb +5 -0
  18. data/lib/capybara/rack_test/node.rb +2 -0
  19. data/lib/capybara/result.rb +2 -0
  20. data/lib/capybara/rspec/compound.rb +2 -0
  21. data/lib/capybara/rspec/matchers.rb +1 -0
  22. data/lib/capybara/selector.rb +14 -27
  23. data/lib/capybara/selector/builders/css_builder.rb +49 -0
  24. data/lib/capybara/selector/builders/xpath_builder.rb +56 -0
  25. data/lib/capybara/selector/filter_set.rb +1 -0
  26. data/lib/capybara/selector/filters/base.rb +2 -0
  27. data/lib/capybara/selector/regexp_disassembler.rb +66 -0
  28. data/lib/capybara/selector/selector.rb +25 -5
  29. data/lib/capybara/selenium/driver.rb +8 -1
  30. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +19 -1
  31. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +1 -0
  32. data/lib/capybara/selenium/node.rb +7 -0
  33. data/lib/capybara/selenium/nodes/chrome_node.rb +2 -0
  34. data/lib/capybara/selenium/nodes/marionette_node.rb +37 -20
  35. data/lib/capybara/server.rb +4 -0
  36. data/lib/capybara/server/animation_disabler.rb +1 -0
  37. data/lib/capybara/session.rb +5 -0
  38. data/lib/capybara/session/config.rb +2 -0
  39. data/lib/capybara/spec/session/has_css_spec.rb +16 -0
  40. data/lib/capybara/spec/session/has_field_spec.rb +4 -0
  41. data/lib/capybara/spec/session/node_spec.rb +6 -0
  42. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  43. data/lib/capybara/spec/session/reset_session_spec.rb +15 -1
  44. data/lib/capybara/spec/session/selectors_spec.rb +12 -2
  45. data/lib/capybara/spec/views/form.erb +15 -0
  46. data/lib/capybara/version.rb +1 -1
  47. data/lib/capybara/xpath_patches.rb +27 -0
  48. data/spec/dsl_spec.rb +15 -1
  49. data/spec/rack_test_spec.rb +6 -1
  50. data/spec/regexp_dissassembler_spec.rb +154 -0
  51. data/spec/selector_spec.rb +37 -2
  52. data/spec/selenium_spec_chrome.rb +2 -2
  53. data/spec/selenium_spec_firefox_remote.rb +2 -0
  54. data/spec/selenium_spec_marionette.rb +11 -0
  55. data/spec/shared_selenium_session.rb +20 -0
  56. data/spec/spec_helper.rb +4 -0
  57. metadata +7 -2
@@ -19,7 +19,12 @@ skipped_tests = %i[
19
19
  download
20
20
  css
21
21
  ]
22
- Capybara::SpecHelper.run_specs TestSessions::RackTest, 'RackTest', capybara_skip: skipped_tests
22
+ Capybara::SpecHelper.run_specs TestSessions::RackTest, 'RackTest', capybara_skip: skipped_tests do |example|
23
+ case example.metadata[:full_description]
24
+ when /has_css\? should support case insensitive :class and :id options/
25
+ pending "Nokogiri doesn't support case insensitive CSS attribute matchers"
26
+ end
27
+ end
23
28
 
24
29
  RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
25
30
  context 'with rack test driver' do
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Capybara::Selector::RegexpDisassembler do
6
+ it 'handles strings' do
7
+ verify_strings(
8
+ /abcdef/ => %w[abcdef],
9
+ /abc def/ => ['abc def']
10
+ )
11
+ end
12
+
13
+ it 'handles escaped characters' do
14
+ verify_strings(
15
+ /abc\\def/ => %w[abc def],
16
+ /\nabc/ => %w[abc],
17
+ %r{abc/} => %w[abc/]
18
+ )
19
+ end
20
+
21
+ it 'handles wildcards' do
22
+ verify_strings(
23
+ /abc.*def/ => %w[abc def],
24
+ /.*def/ => %w[def],
25
+ /abc./ => %w[abc],
26
+ /abc.*/ => %w[abc],
27
+ /abc.def/ => %w[abc def],
28
+ /abc.def.ghi/ => %w[abc def ghi]
29
+ )
30
+ end
31
+
32
+ it 'handles optional characters' do
33
+ verify_strings(
34
+ /abc*def/ => %w[ab def],
35
+ /abc*/ => %w[ab],
36
+ /abc?def/ => %w[ab def],
37
+ /abc?/ => %w[ab],
38
+ /abc?def?/ => %w[ab de],
39
+ /abc?def?g/ => %w[ab de g]
40
+ )
41
+ end
42
+
43
+ it 'handles character classes' do
44
+ verify_strings(
45
+ /abc[a-z]/ => %w[abc],
46
+ /abc[a-z]def[0-9]g/ => %w[abc def g],
47
+ /[0-9]abc/ => %w[abc],
48
+ /[0-9]+/ => %w[],
49
+ /abc[0-9&&[^7]]/ => %w[abc]
50
+ )
51
+ end
52
+
53
+ it 'handles posix bracket expressions' do
54
+ verify_strings(
55
+ /abc[[:alpha:]]/ => %w[abc],
56
+ /[[:digit:]]abc/ => %w[abc],
57
+ /abc[[:print:]]def/ => %w[abc def]
58
+ )
59
+ end
60
+
61
+ it 'handles repitition' do
62
+ verify_strings(
63
+ /abc{3}/ => %w[abccc],
64
+ /abc{3}d/ => %w[abcccd],
65
+ /abc{0}/ => %w[ab],
66
+ /abc{,2}/ => %w[ab],
67
+ /abc{2,}/ => %w[abcc],
68
+ /def{1,5}/ => %w[def],
69
+ /abc+def/ => %w[abc def],
70
+ /ab(cde){,4}/ => %w[ab],
71
+ /(ab){,2}cd/ => %w[cd],
72
+ /(abc){2,3}/ => %w[abcabc],
73
+ /(abc){3}/ => %w[abcabcabc],
74
+ /ab{2,3}cd/ => %w[abb cd],
75
+ /(ab){2,3}cd/ => %w[abab cd]
76
+ )
77
+ end
78
+
79
+ it 'handles non-greedy repetition' do
80
+ verify_strings(
81
+ /abc.*?/ => %w[abc],
82
+ /abc+?/ => %w[abc],
83
+ /abc*?cde/ => %w[ab cde],
84
+ /(abc)+?def/ => %w[abc def],
85
+ /ab(cde)*?fg/ => %w[ab fg]
86
+ )
87
+ end
88
+
89
+ it 'handles alternation' do
90
+ verify_strings(
91
+ /abc|def/ => [],
92
+ /ab(?:c|d)/ => %w[ab],
93
+ /ab(c|d)ef/ => %w[ab ef]
94
+ )
95
+ end
96
+
97
+ it 'handles grouping' do
98
+ verify_strings(
99
+ /(abc)/ => %w[abc],
100
+ /(abc)?/ => [],
101
+ /ab(cde)/ => %w[abcde],
102
+ /(abc)de/ => %w[abcde],
103
+ /ab(cde)fg/ => %w[abcdefg],
104
+ /ab(?<name>cd)ef/ => %w[abcdef],
105
+ /gh(?>ij)kl/ => %w[ghijkl],
106
+ /m(n.*p)q/ => %w[mn pq],
107
+ /(?:ab(cd)*){2,3}/ => %w[ab],
108
+ /(ab(cd){3})?/ => [],
109
+ /(ab(cd)+){2}/ => %w[abcd]
110
+ )
111
+ end
112
+
113
+ it 'handles meta characters' do
114
+ verify_strings(
115
+ /abc\d/ => %w[abc],
116
+ /abc\wdef/ => %w[abc def],
117
+ /\habc/ => %w[abc]
118
+ )
119
+ end
120
+
121
+ it 'handles character properties' do
122
+ verify_strings(
123
+ /ab\p{Alpha}cd/ => %w[ab cd],
124
+ /ab\p{Blank}/ => %w[ab],
125
+ /\p{Digit}cd/ => %w[cd]
126
+ )
127
+ end
128
+
129
+ it 'handles backreferences' do
130
+ verify_strings(
131
+ /a(?<group>abc).\k<group>.+/ => %w[aabc]
132
+ )
133
+ end
134
+
135
+ it 'handles subexpressions' do
136
+ verify_strings(
137
+ /\A(?<paren>a\g<paren>*b)+\z/ => %w[a b]
138
+ )
139
+ end
140
+
141
+ it 'handles anchors' do
142
+ verify_strings(
143
+ /^abc/ => %w[abc],
144
+ /def$/ => %w[def],
145
+ /^abc$/ => %w[abc]
146
+ )
147
+ end
148
+
149
+ def verify_strings(hsh)
150
+ hsh.each do |regexp, expected|
151
+ expect(Capybara::Selector::RegexpDisassembler.new(regexp).substrings).to eq expected
152
+ end
153
+ end
154
+ end
@@ -31,6 +31,7 @@ RSpec.describe Capybara do
31
31
  <input type="file" id="file" class=".special file"/>
32
32
  <input type="hidden" id="hidden_field" value="this is hidden"/>
33
33
  <input type="submit" value="click me" title="submit button"/>
34
+ <input type="button" value="don't click me" title="Other button 1"/>
34
35
  <a href="#">link</a>
35
36
  <fieldset></fieldset>
36
37
  <select id="select">
@@ -147,6 +148,15 @@ RSpec.describe Capybara do
147
148
  expect { string.find(:custom_css_selector, 'div', id: XPath.contains('peci')) }
148
149
  .to raise_error(ArgumentError, /not supported/)
149
150
  end
151
+
152
+ it 'accepts Regexp for xpath based selectors' do
153
+ expect(string.find(:custom_xpath_selector, './/div', id: /peci/)[:id]).to eq '#special'
154
+ expect(string.find(:custom_xpath_selector, './/div', id: /pEcI/i)[:id]).to eq '#special'
155
+ end
156
+
157
+ it 'accepts Regexp for css based selectors' do
158
+ expect(string.find(:custom_css_selector, 'div', id: /sp.*al/)[:id]).to eq '#special'
159
+ end
150
160
  end
151
161
 
152
162
  context 'with :class option' do
@@ -176,6 +186,15 @@ RSpec.describe Capybara do
176
186
  expect { string.find(:custom_css_selector, 'div', class: XPath.contains('random')) }
177
187
  .to raise_error(ArgumentError, /not supported/)
178
188
  end
189
+
190
+ it 'accepts Regexp for XPath based selectors' do
191
+ expect(string.find(:custom_xpath_selector, './/div', class: /dom wor/)[:id]).to eq 'random_words'
192
+ expect(string.find(:custom_xpath_selector, './/div', class: /dOm WoR/i)[:id]).to eq 'random_words'
193
+ end
194
+
195
+ it 'accepts Regexp for CSS base selectors' do
196
+ expect(string.find(:custom_css_selector, 'div', class: /random/)[:id]).to eq 'random_words'
197
+ end
179
198
  end
180
199
 
181
200
  # :css, :xpath, :id, :field, :fieldset, :link, :button, :link_or_button, :fillable_field, :radio_button, :checkbox, :select,
@@ -206,10 +225,14 @@ RSpec.describe Capybara do
206
225
  expect(string.find(:field, 'form[my_text_input]')[:id]).to eq 'my_text_input'
207
226
  end
208
227
 
209
- it 'finds by id' do
228
+ it 'finds by id string' do
210
229
  expect(string.find(:field, id: 'my_text_input')[:name]).to eq 'form[my_text_input]'
211
230
  end
212
231
 
232
+ it 'finds by id regexp' do
233
+ expect(string.find(:field, id: /my_text_inp/)[:name]).to eq 'form[my_text_input]'
234
+ end
235
+
213
236
  it 'finds by name' do
214
237
  expect(string.find(:field, name: 'form[my_text_input]')[:id]).to eq 'my_text_input'
215
238
  end
@@ -257,6 +280,18 @@ RSpec.describe Capybara do
257
280
  expect(string.find(:element, 'input', type: 'submit').value).to eq 'click me'
258
281
  end
259
282
 
283
+ it 'supports regexp matching' do
284
+ expect(string.find(:element, 'input', type: /sub/).value).to eq 'click me'
285
+ expect(string.find(:element, 'input', title: /sub\w.*button/).value).to eq 'click me'
286
+ expect(string.find(:element, 'input', title: /sub.* b.*ton/).value).to eq 'click me'
287
+ expect(string.find(:element, 'input', title: /sub.*mit.*/).value).to eq 'click me'
288
+ expect(string.find(:element, 'input', title: /^submit button$/).value).to eq 'click me'
289
+ expect(string.find(:element, 'input', title: /^(?:submit|other) button$/).value).to eq 'click me'
290
+ expect(string.find(:element, 'input', title: /SuB.*mIt/i).value).to eq 'click me'
291
+ expect(string.find(:element, 'input', title: /^Su.*Bm.*It/i).value).to eq 'click me'
292
+ expect(string.find(:element, 'input', title: /^Ot.*he.*r b.*\d/i).value).to eq "don't click me"
293
+ end
294
+
260
295
  it 'still works with system keys' do
261
296
  expect { string.all(:element, 'input', type: 'submit', count: 1) }.not_to raise_error
262
297
  end
@@ -289,7 +324,7 @@ RSpec.describe Capybara do
289
324
  expect(string.find(:element, 'input', type: XPath.ends_with('ext'))[:type]).to eq 'text'
290
325
  expect(string.find(:element, 'input', type: XPath.contains('ckb'))[:type]).to eq 'checkbox'
291
326
  expect(string.find(:element, 'input', title: XPath.contains_word('submit'))[:type]).to eq 'submit'
292
- expect(string.find(:element, 'input', title: XPath.contains_word('button'))[:type]).to eq 'submit'
327
+ expect(string.find(:element, 'input', title: XPath.contains_word('button 1'))[:type]).to eq 'button'
293
328
  end
294
329
  end
295
330
  end
@@ -94,13 +94,13 @@ RSpec.describe 'Capybara::Session with chrome' do
94
94
  end
95
95
 
96
96
  it 'should fill in a date input with a String' do
97
- @session.fill_in('form_date', with: "06/19/1983")
97
+ @session.fill_in('form_date', with: '06/19/1983')
98
98
  @session.click_button('awesome')
99
99
  expect(Date.parse(extract_results(@session)['date'])).to eq datetime.to_date
100
100
  end
101
101
 
102
102
  it 'should fill in a time input with a String' do
103
- @session.fill_in('form_time', with: "06:30A")
103
+ @session.fill_in('form_time', with: '06:30A')
104
104
  @session.click_button('awesome')
105
105
  results = extract_results(@session)['time']
106
106
  expect(Time.parse(results).strftime('%r')).to eq datetime.strftime('%r')
@@ -69,6 +69,8 @@ Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVE
69
69
  'if files are already set'
70
70
  when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once when uploading multiple files from empty'
71
71
  pending "FF < 62 doesn't support setting all files at once" if marionette_lt?(62, @session)
72
+ when 'Capybara::Session selenium_firefox_remote #reset_session! removes ALL cookies'
73
+ pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
72
74
  end
73
75
  end
74
76
 
@@ -65,6 +65,8 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumMarionette, 'selenium', cap
65
65
  skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if marionette_gte?(63, @session)
66
66
  when 'Capybara::Session selenium #click_link can download a file'
67
67
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
68
+ when 'Capybara::Session selenium #reset_session! removes ALL cookies'
69
+ pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
68
70
  end
69
71
  end
70
72
 
@@ -169,4 +171,13 @@ RSpec.describe Capybara::Selenium::Node do
169
171
  expect(session).to have_link('Has been alt control meta')
170
172
  end
171
173
  end
174
+
175
+ context '#send_keys' do
176
+ it 'should process space' do
177
+ session = TestSessions::SeleniumMarionette
178
+ session.visit('/form')
179
+ session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side'])
180
+ expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE'
181
+ end
182
+ end
172
183
  end
@@ -334,6 +334,20 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
334
334
  end
335
335
  end
336
336
 
337
+ describe 'Capybara#Node#attach_file' do
338
+ it 'can attach a directory' do
339
+ pending "Geckodriver doesn't support uploading a directory" if marionette?(session)
340
+ pending "Selenium remote doesn't support transferring a directory" if remote?(session)
341
+ pending "Headless Chrome doesn't support directory upload - https://bugs.chromium.org/p/chromedriver/issues/detail?id=2521&q=directory%20upload&colspec=ID%20Status%20Pri%20Owner%20Summary" if chrome?(session) && ENV['HEADLESS']
342
+
343
+ session.visit('/form')
344
+ @test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__))
345
+ session.attach_file('Directory Upload', @test_file_dir)
346
+ session.click_button('Upload Multiple')
347
+ expect(session.body).to include('5 | ') # number of files
348
+ end
349
+ end
350
+
337
351
  context 'Windows' do
338
352
  it "can't close the primary window" do
339
353
  expect do
@@ -431,6 +445,12 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
431
445
  expect(session).to have_selector(:element, :circle)
432
446
  expect(session).to have_selector(:element, :linearGradient, visible: :all)
433
447
  end
448
+
449
+ it 'can query attributes with strange characters' do
450
+ session.visit('/form')
451
+ expect(session).to have_selector(:element, "{custom}": true)
452
+ expect(session).to have_selector(:element, "{custom}": 'abcdef')
453
+ end
434
454
  end
435
455
  end
436
456
 
@@ -42,6 +42,10 @@ module Capybara
42
42
  def browser_name(session)
43
43
  session.driver.browser.browser if session.respond_to?(:driver)
44
44
  end
45
+
46
+ def remote?(session)
47
+ session.driver.browser.is_a? ::Selenium::WebDriver::Remote::Driver
48
+ end
45
49
  end
46
50
  end
47
51
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.2
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Walpole
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
12
12
  - gem-public_cert.pem
13
- date: 2018-09-26 00:00:00.000000000 Z
13
+ date: 2018-10-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: addressable
@@ -301,12 +301,15 @@ files:
301
301
  - lib/capybara/rspec/matcher_proxies.rb
302
302
  - lib/capybara/rspec/matchers.rb
303
303
  - lib/capybara/selector.rb
304
+ - lib/capybara/selector/builders/css_builder.rb
305
+ - lib/capybara/selector/builders/xpath_builder.rb
304
306
  - lib/capybara/selector/css.rb
305
307
  - lib/capybara/selector/filter.rb
306
308
  - lib/capybara/selector/filter_set.rb
307
309
  - lib/capybara/selector/filters/base.rb
308
310
  - lib/capybara/selector/filters/expression_filter.rb
309
311
  - lib/capybara/selector/filters/node_filter.rb
312
+ - lib/capybara/selector/regexp_disassembler.rb
310
313
  - lib/capybara/selector/selector.rb
311
314
  - lib/capybara/selenium/driver.rb
312
315
  - lib/capybara/selenium/driver_specializations/chrome_driver.rb
@@ -452,6 +455,7 @@ files:
452
455
  - lib/capybara/spec/views/within_frames.erb
453
456
  - lib/capybara/version.rb
454
457
  - lib/capybara/window.rb
458
+ - lib/capybara/xpath_patches.rb
455
459
  - spec/basic_node_spec.rb
456
460
  - spec/capybara_spec.rb
457
461
  - spec/css_splitter_spec.rb
@@ -466,6 +470,7 @@ files:
466
470
  - spec/minitest_spec_spec.rb
467
471
  - spec/per_session_config_spec.rb
468
472
  - spec/rack_test_spec.rb
473
+ - spec/regexp_dissassembler_spec.rb
469
474
  - spec/result_spec.rb
470
475
  - spec/rspec/features_spec.rb
471
476
  - spec/rspec/scenarios_spec.rb