capybara 3.8.0 → 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 +25 -1
  3. data/lib/capybara/config.rb +1 -0
  4. data/lib/capybara/dsl.rb +2 -2
  5. data/lib/capybara/helpers.rb +1 -0
  6. data/lib/capybara/node/actions.rb +4 -0
  7. data/lib/capybara/node/base.rb +1 -0
  8. data/lib/capybara/node/element.rb +3 -0
  9. data/lib/capybara/node/finders.rb +2 -0
  10. data/lib/capybara/node/simple.rb +1 -0
  11. data/lib/capybara/queries/base_query.rb +1 -0
  12. data/lib/capybara/queries/match_query.rb +1 -0
  13. data/lib/capybara/queries/selector_query.rb +34 -37
  14. data/lib/capybara/queries/text_query.rb +2 -0
  15. data/lib/capybara/rack_test/browser.rb +1 -0
  16. data/lib/capybara/rack_test/driver.rb +5 -0
  17. data/lib/capybara/rack_test/node.rb +2 -0
  18. data/lib/capybara/result.rb +2 -0
  19. data/lib/capybara/rspec/compound.rb +2 -0
  20. data/lib/capybara/rspec/matchers.rb +1 -0
  21. data/lib/capybara/selector/builders/css_builder.rb +49 -0
  22. data/lib/capybara/selector/builders/xpath_builder.rb +56 -0
  23. data/lib/capybara/selector/filter_set.rb +1 -0
  24. data/lib/capybara/selector/filters/base.rb +2 -0
  25. data/lib/capybara/selector/regexp_disassembler.rb +66 -0
  26. data/lib/capybara/selector/selector.rb +25 -5
  27. data/lib/capybara/selector.rb +14 -27
  28. data/lib/capybara/selenium/driver.rb +8 -1
  29. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +19 -1
  30. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +1 -0
  31. data/lib/capybara/selenium/node.rb +11 -0
  32. data/lib/capybara/selenium/nodes/chrome_node.rb +2 -0
  33. data/lib/capybara/selenium/nodes/marionette_node.rb +37 -20
  34. data/lib/capybara/server/animation_disabler.rb +1 -0
  35. data/lib/capybara/server.rb +4 -0
  36. data/lib/capybara/session/config.rb +2 -0
  37. data/lib/capybara/session.rb +5 -0
  38. data/lib/capybara/spec/session/has_css_spec.rb +16 -0
  39. data/lib/capybara/spec/session/has_field_spec.rb +4 -0
  40. data/lib/capybara/spec/session/node_spec.rb +6 -0
  41. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  42. data/lib/capybara/spec/session/reset_session_spec.rb +15 -1
  43. data/lib/capybara/spec/session/selectors_spec.rb +12 -2
  44. data/lib/capybara/spec/views/form.erb +15 -0
  45. data/lib/capybara/version.rb +1 -1
  46. data/lib/capybara/xpath_patches.rb +27 -0
  47. data/lib/capybara.rb +32 -10
  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 +53 -18
  52. data/spec/selenium_spec_chrome.rb +28 -0
  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
data/lib/capybara.rb CHANGED
@@ -299,7 +299,7 @@ module Capybara
299
299
  # @return [Capybara::Session] The currently used session
300
300
  #
301
301
  def current_session
302
- session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
302
+ specified_session || session_pool["#{current_driver}:#{session_name}:#{app.object_id}"]
303
303
  end
304
304
 
305
305
  ##
@@ -337,22 +337,25 @@ module Capybara
337
337
 
338
338
  ##
339
339
  #
340
- # Yield a block using a specific session name.
340
+ # Yield a block using a specific session name or Capybara::Session instance.
341
341
  #
342
- def using_session(name)
342
+ def using_session(name_or_session)
343
343
  previous_session_info = {
344
+ specified_session: specified_session,
344
345
  session_name: session_name,
345
346
  current_driver: current_driver,
346
347
  app: app
347
348
  }
348
- self.session_name = name
349
+ self.specified_session = self.session_name = nil
350
+ if name_or_session.is_a? Capybara::Session
351
+ self.specified_session = name_or_session
352
+ else
353
+ self.session_name = name_or_session
354
+ end
349
355
  yield
350
356
  ensure
351
- self.session_name = previous_session_info[:session_name]
352
- if threadsafe
353
- self.current_driver = previous_session_info[:current_driver]
354
- self.app = previous_session_info[:app]
355
- end
357
+ self.session_name, self.specified_session = previous_session_info.values_at(:session_name, :specified_session)
358
+ self.current_driver, self.app = previous_session_info.values_at(:current_driver, :app) if threadsafe
356
359
  end
357
360
 
358
361
  ##
@@ -381,7 +384,25 @@ module Capybara
381
384
  end
382
385
 
383
386
  def session_pool
384
- @session_pool ||= {}
387
+ @session_pool ||= Hash.new do |hash, name|
388
+ hash[name] = Capybara::Session.new(current_driver, app)
389
+ end
390
+ end
391
+
392
+ def specified_session
393
+ if threadsafe
394
+ Thread.current['capybara_specified_session']
395
+ else
396
+ @specified_session
397
+ end
398
+ end
399
+
400
+ def specified_session=(session)
401
+ if threadsafe
402
+ Thread.current['capybara_specified_session'] = session
403
+ else
404
+ @specified_session = session
405
+ end
385
406
  end
386
407
  end
387
408
 
@@ -393,6 +414,7 @@ module Capybara
393
414
  module RackTest; end
394
415
  module Selenium; end
395
416
 
417
+ require 'capybara/xpath_patches'
396
418
  require 'capybara/helpers'
397
419
  require 'capybara/session'
398
420
  require 'capybara/window'
data/spec/dsl_spec.rb CHANGED
@@ -9,7 +9,12 @@ end
9
9
 
10
10
  Capybara::SpecHelper.run_specs TestClass.new, 'DSL', capybara_skip: %i[
11
11
  js modals screenshot frames windows send_keys server hover about_scheme psc download css driver
12
- ]
12
+ ] do |example|
13
+ case example.metadata[:full_description]
14
+ when /has_css\? should support case insensitive :class and :id options/
15
+ pending "Nokogiri doesn't support case insensitive CSS attribute matchers"
16
+ end
17
+ end
13
18
 
14
19
  RSpec.describe Capybara::DSL do
15
20
  after do
@@ -224,6 +229,15 @@ RSpec.describe Capybara::DSL do
224
229
  end
225
230
  expect(Capybara.session_name).to eq(:default)
226
231
  end
232
+
233
+ it 'should allow a session object' do
234
+ original_session = Capybara.current_session
235
+ new_session = Capybara::Session.new(:rack_test, proc {})
236
+ Capybara.using_session(new_session) do
237
+ expect(Capybara.current_session).to eq(new_session)
238
+ end
239
+ expect(Capybara.current_session).to eq(original_session)
240
+ end
227
241
  end
228
242
 
229
243
  describe '#session_name' do
@@ -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
@@ -11,13 +11,13 @@ RSpec.describe Capybara do
11
11
  <title>selectors</title>
12
12
  </head>
13
13
  <body>
14
- <div class="a" id="page">
15
- <div class="b" id="content">
16
- <h1 class="a">Totally awesome</h1>
14
+ <div class="aa" id="page">
15
+ <div class="bb" id="content">
16
+ <h1 class="aa">Totally awesome</h1>
17
17
  <p>Yes it is</p>
18
18
  </div>
19
- <p class="b c">Some Content</p>
20
- <p class="b d"></p>
19
+ <p class="bb cc">Some Content</p>
20
+ <p class="bb dd"></p>
21
21
  </div>
22
22
  <div id="#special">
23
23
  </div>
@@ -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">
@@ -84,12 +85,12 @@ RSpec.describe Capybara do
84
85
 
85
86
  describe 'modify_selector' do
86
87
  it 'allows modifying a selector' do
87
- el = string.find(:custom_selector, 'a')
88
+ el = string.find(:custom_selector, 'aa')
88
89
  expect(el.tag_name).to eq 'div'
89
90
  Capybara.modify_selector :custom_selector do
90
91
  css { |css_class| "h1.#{css_class}" }
91
92
  end
92
- el = string.find(:custom_selector, 'a')
93
+ el = string.find(:custom_selector, 'aa')
93
94
  expect(el.tag_name).to eq 'h1'
94
95
  end
95
96
 
@@ -97,9 +98,9 @@ RSpec.describe Capybara do
97
98
  Capybara.modify_selector :custom_selector do
98
99
  css { |css_class| "p.#{css_class}" }
99
100
  end
100
- expect(string).to have_selector(:custom_selector, 'b', count: 1)
101
- expect(string).to have_selector(:custom_selector, 'b', not_empty: false, count: 1)
102
- expect(string).to have_selector(:custom_selector, 'b', not_empty: :all, count: 2)
101
+ expect(string).to have_selector(:custom_selector, 'bb', count: 1)
102
+ expect(string).to have_selector(:custom_selector, 'bb', not_empty: false, count: 1)
103
+ expect(string).to have_selector(:custom_selector, 'bb', not_empty: :all, count: 2)
103
104
  end
104
105
  end
105
106
 
@@ -147,19 +148,28 @@ 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
153
163
  it 'works with compound css selectors' do
154
- expect(string.all(:custom_css_selector, 'div, h1', class: 'a').size).to eq 2
155
- expect(string.all(:custom_css_selector, 'h1, div', class: 'a').size).to eq 2
164
+ expect(string.all(:custom_css_selector, 'div, h1', class: 'aa').size).to eq 2
165
+ expect(string.all(:custom_css_selector, 'h1, div', class: 'aa').size).to eq 2
156
166
  end
157
167
 
158
168
  it 'handles negated classes' do
159
- expect(string.all(:custom_css_selector, 'div, p', class: ['b', '!c']).size).to eq 2
160
- expect(string.all(:custom_css_selector, 'div, p', class: ['!c', '!d', 'b']).size).to eq 1
161
- expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['b', '!c']).size).to eq 2
162
- expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!c', '!d', 'b']).size).to eq 1
169
+ expect(string.all(:custom_css_selector, 'div, p', class: ['bb', '!cc']).size).to eq 2
170
+ expect(string.all(:custom_css_selector, 'div, p', class: ['!cc', '!dd', 'bb']).size).to eq 1
171
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['bb', '!cc']).size).to eq 2
172
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!cc', '!dd', 'bb']).size).to eq 1
163
173
  end
164
174
 
165
175
  it "works with 'special' characters" 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
@@ -84,4 +84,32 @@ RSpec.describe 'Capybara::Session with chrome' do
84
84
  end
85
85
  end
86
86
  end
87
+
88
+ describe 'filling in Chrome-specific date and time fields with keystrokes' do
89
+ let(:datetime) { Time.new(1983, 6, 19, 6, 30) }
90
+
91
+ before do
92
+ @session = TestSessions::Chrome
93
+ @session.visit('/form')
94
+ end
95
+
96
+ it 'should fill in a date input with a String' do
97
+ @session.fill_in('form_date', with: '06/19/1983')
98
+ @session.click_button('awesome')
99
+ expect(Date.parse(extract_results(@session)['date'])).to eq datetime.to_date
100
+ end
101
+
102
+ it 'should fill in a time input with a String' do
103
+ @session.fill_in('form_time', with: '06:30A')
104
+ @session.click_button('awesome')
105
+ results = extract_results(@session)['time']
106
+ expect(Time.parse(results).strftime('%r')).to eq datetime.strftime('%r')
107
+ end
108
+
109
+ it 'should fill in a datetime input with a String' do
110
+ @session.fill_in('form_datetime', with: "06/19/1983\t06:30A")
111
+ @session.click_button('awesome')
112
+ expect(Time.parse(extract_results(@session)['datetime'])).to eq datetime
113
+ end
114
+ end
87
115
  end
@@ -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
 
data/spec/spec_helper.rb CHANGED
@@ -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.0
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-20 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