capybara 3.8.2 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
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