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.
- checksums.yaml +4 -4
- data/History.md +10 -0
- data/lib/capybara.rb +32 -10
- data/lib/capybara/config.rb +1 -0
- data/lib/capybara/dsl.rb +2 -2
- data/lib/capybara/helpers.rb +1 -0
- data/lib/capybara/node/actions.rb +4 -0
- data/lib/capybara/node/base.rb +1 -0
- data/lib/capybara/node/element.rb +3 -0
- data/lib/capybara/node/finders.rb +2 -0
- data/lib/capybara/node/simple.rb +1 -0
- data/lib/capybara/queries/base_query.rb +1 -0
- data/lib/capybara/queries/match_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +34 -37
- data/lib/capybara/queries/text_query.rb +2 -0
- data/lib/capybara/rack_test/browser.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +5 -0
- data/lib/capybara/rack_test/node.rb +2 -0
- data/lib/capybara/result.rb +2 -0
- data/lib/capybara/rspec/compound.rb +2 -0
- data/lib/capybara/rspec/matchers.rb +1 -0
- data/lib/capybara/selector.rb +14 -27
- data/lib/capybara/selector/builders/css_builder.rb +49 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +56 -0
- data/lib/capybara/selector/filter_set.rb +1 -0
- data/lib/capybara/selector/filters/base.rb +2 -0
- data/lib/capybara/selector/regexp_disassembler.rb +66 -0
- data/lib/capybara/selector/selector.rb +25 -5
- data/lib/capybara/selenium/driver.rb +8 -1
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +19 -1
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +1 -0
- data/lib/capybara/selenium/node.rb +7 -0
- data/lib/capybara/selenium/nodes/chrome_node.rb +2 -0
- data/lib/capybara/selenium/nodes/marionette_node.rb +37 -20
- data/lib/capybara/server.rb +4 -0
- data/lib/capybara/server/animation_disabler.rb +1 -0
- data/lib/capybara/session.rb +5 -0
- data/lib/capybara/session/config.rb +2 -0
- data/lib/capybara/spec/session/has_css_spec.rb +16 -0
- data/lib/capybara/spec/session/has_field_spec.rb +4 -0
- data/lib/capybara/spec/session/node_spec.rb +6 -0
- data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +15 -1
- data/lib/capybara/spec/session/selectors_spec.rb +12 -2
- data/lib/capybara/spec/views/form.erb +15 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/xpath_patches.rb +27 -0
- data/spec/dsl_spec.rb +15 -1
- data/spec/rack_test_spec.rb +6 -1
- data/spec/regexp_dissassembler_spec.rb +154 -0
- data/spec/selector_spec.rb +37 -2
- data/spec/selenium_spec_chrome.rb +2 -2
- data/spec/selenium_spec_firefox_remote.rb +2 -0
- data/spec/selenium_spec_marionette.rb +11 -0
- data/spec/shared_selenium_session.rb +20 -0
- data/spec/spec_helper.rb +4 -0
- metadata +7 -2
@@ -34,6 +34,7 @@ module Capybara::Selenium::Driver::MarionetteDriver
|
|
34
34
|
|
35
35
|
def switch_to_frame(frame)
|
36
36
|
return super unless frame == :parent
|
37
|
+
|
37
38
|
# geckodriver/firefox has an issue if the current frame is removed from within it
|
38
39
|
# so we have to move to the default_content and iterate back through the frames
|
39
40
|
handles = @frame_handles[current_window_handle]
|
@@ -48,6 +48,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
48
48
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
49
49
|
def set(value, **options)
|
50
50
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
51
|
+
|
51
52
|
case tag_name
|
52
53
|
when 'input'
|
53
54
|
case self[:type]
|
@@ -79,12 +80,14 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
79
80
|
|
80
81
|
def unselect_option
|
81
82
|
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
83
|
+
|
82
84
|
click if selected?
|
83
85
|
end
|
84
86
|
|
85
87
|
def click(keys = [], **options)
|
86
88
|
click_options = ClickOptions.new(keys, options)
|
87
89
|
return native.click if click_options.empty?
|
90
|
+
|
88
91
|
click_with_options(click_options)
|
89
92
|
rescue StandardError => err
|
90
93
|
if err.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
@@ -136,6 +139,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
136
139
|
|
137
140
|
def disabled?
|
138
141
|
return true unless native.enabled?
|
142
|
+
|
139
143
|
# WebDriver only defines `disabled?` for form controls but fieldset makes sense too
|
140
144
|
tag_name == 'fieldset' && find_xpath('ancestor-or-self::fieldset[@disabled]').any?
|
141
145
|
end
|
@@ -255,6 +259,7 @@ private
|
|
255
259
|
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
256
260
|
value = SettableValue.new(value)
|
257
261
|
return set_text(value) unless value.dateable?
|
262
|
+
|
258
263
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
259
264
|
update_value_js(value.to_date_str)
|
260
265
|
end
|
@@ -262,6 +267,7 @@ private
|
|
262
267
|
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
263
268
|
value = SettableValue.new(value)
|
264
269
|
return set_text(value) unless value.timeable?
|
270
|
+
|
265
271
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
266
272
|
update_value_js(value.to_time_str)
|
267
273
|
end
|
@@ -269,6 +275,7 @@ private
|
|
269
275
|
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
270
276
|
value = SettableValue.new(value)
|
271
277
|
return set_text(value) unless value.timeable?
|
278
|
+
|
272
279
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
273
280
|
update_value_js(value.to_datetime_str)
|
274
281
|
end
|
@@ -11,11 +11,13 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
11
11
|
if err.message =~ /File not found : .+\n.+/m
|
12
12
|
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
|
13
13
|
end
|
14
|
+
|
14
15
|
raise
|
15
16
|
end
|
16
17
|
|
17
18
|
def drag_to(element)
|
18
19
|
return super unless html5_draggable?
|
20
|
+
|
19
21
|
html5_drag_to(element)
|
20
22
|
end
|
21
23
|
|
@@ -21,6 +21,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
21
21
|
return super unless browser_version < 61.0
|
22
22
|
|
23
23
|
return true if super
|
24
|
+
|
24
25
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
25
26
|
if %w[option optgroup].include? tag_name
|
26
27
|
find_xpath('parent::*[self::optgroup or self::select]')[0].disabled?
|
@@ -52,13 +53,12 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
52
53
|
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
|
53
54
|
|
54
55
|
native.click
|
55
|
-
args.
|
56
|
-
_send_keys(keys, actions)
|
57
|
-
end.perform
|
56
|
+
_send_keys(args).perform
|
58
57
|
end
|
59
58
|
|
60
59
|
def drag_to(element)
|
61
60
|
return super unless (browser_version >= 62.0) && html5_draggable?
|
61
|
+
|
62
62
|
html5_drag_to(element)
|
63
63
|
end
|
64
64
|
|
@@ -71,35 +71,29 @@ private
|
|
71
71
|
super
|
72
72
|
end
|
73
73
|
|
74
|
-
def _send_keys(keys, actions, down_keys =
|
74
|
+
def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
|
75
75
|
case keys
|
76
|
-
when String
|
77
|
-
keys = keys.upcase if down_keys&.include?(:shift) # https://bugzilla.mozilla.org/show_bug.cgi?id=1405370
|
78
|
-
actions.send_keys(keys)
|
79
|
-
when :space
|
80
|
-
actions.send_keys(' ') # https://github.com/mozilla/geckodriver/issues/846
|
81
76
|
when :control, :left_control, :right_control,
|
82
77
|
:alt, :left_alt, :right_alt,
|
83
78
|
:shift, :left_shift, :right_shift,
|
84
79
|
:meta, :left_meta, :right_meta,
|
85
80
|
:command
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
81
|
+
down_keys.press(keys)
|
82
|
+
actions.key_down(keys)
|
83
|
+
when String
|
84
|
+
# https://bugzilla.mozilla.org/show_bug.cgi?id=1405370
|
85
|
+
keys = keys.upcase if (browser_version < 64.0) && down_keys&.include?(:shift)
|
86
|
+
actions.send_keys(keys)
|
92
87
|
when Symbol
|
93
88
|
actions.send_keys(keys)
|
94
89
|
when Array
|
95
|
-
|
96
|
-
keys.each
|
97
|
-
|
98
|
-
end
|
99
|
-
local_down_keys.each { |key| actions.key_up(key) }
|
90
|
+
down_keys.push
|
91
|
+
keys.each { |sub_keys| _send_keys(sub_keys, actions, down_keys) }
|
92
|
+
down_keys.pop.reverse_each { |key| actions.key_up(key) }
|
100
93
|
else
|
101
94
|
raise ArgumentError, 'Unknown keys type'
|
102
95
|
end
|
96
|
+
actions
|
103
97
|
end
|
104
98
|
|
105
99
|
def bridge
|
@@ -118,4 +112,27 @@ private
|
|
118
112
|
def browser_version
|
119
113
|
driver.browser.capabilities[:browser_version].to_f
|
120
114
|
end
|
115
|
+
|
116
|
+
class ModifierKeysStack
|
117
|
+
def initialize
|
118
|
+
@stack = []
|
119
|
+
end
|
120
|
+
|
121
|
+
def include?(key)
|
122
|
+
@stack.flatten.include?(key)
|
123
|
+
end
|
124
|
+
|
125
|
+
def press(key)
|
126
|
+
@stack.last.push(key)
|
127
|
+
end
|
128
|
+
|
129
|
+
def push
|
130
|
+
@stack.push []
|
131
|
+
end
|
132
|
+
|
133
|
+
def pop
|
134
|
+
@stack.pop
|
135
|
+
end
|
136
|
+
end
|
137
|
+
private_constant :ModifierKeysStack
|
121
138
|
end
|
data/lib/capybara/server.rb
CHANGED
@@ -8,6 +8,7 @@ require 'capybara/server/animation_disabler'
|
|
8
8
|
require 'capybara/server/checker'
|
9
9
|
|
10
10
|
module Capybara
|
11
|
+
# @api private
|
11
12
|
class Server
|
12
13
|
class << self
|
13
14
|
def ports
|
@@ -44,6 +45,7 @@ module Capybara
|
|
44
45
|
|
45
46
|
def responsive?
|
46
47
|
return false if @server_thread&.join(0)
|
48
|
+
|
47
49
|
res = @checker.request { |http| http.get('/__identify__') }
|
48
50
|
|
49
51
|
if res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection)
|
@@ -57,6 +59,7 @@ module Capybara
|
|
57
59
|
timer = Capybara::Helpers.timer(expire_in: 60)
|
58
60
|
while pending_requests?
|
59
61
|
raise 'Requests did not finish in 60 seconds' if timer.expired?
|
62
|
+
|
60
63
|
sleep 0.01
|
61
64
|
end
|
62
65
|
end
|
@@ -72,6 +75,7 @@ module Capybara
|
|
72
75
|
timer = Capybara::Helpers.timer(expire_in: 60)
|
73
76
|
until responsive?
|
74
77
|
raise 'Rack application timed out during boot' if timer.expired?
|
78
|
+
|
75
79
|
@server_thread.join(0.1)
|
76
80
|
end
|
77
81
|
end
|
data/lib/capybara/session.rb
CHANGED
@@ -76,11 +76,13 @@ module Capybara
|
|
76
76
|
|
77
77
|
def initialize(mode, app = nil)
|
78
78
|
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.' if app && !app.respond_to?(:call)
|
79
|
+
|
79
80
|
@@instance_created = true
|
80
81
|
@mode = mode
|
81
82
|
@app = app
|
82
83
|
if block_given?
|
83
84
|
raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe
|
85
|
+
|
84
86
|
yield config
|
85
87
|
end
|
86
88
|
@server = if config.run_server && @app && driver.needs_server?
|
@@ -138,6 +140,7 @@ module Capybara
|
|
138
140
|
#
|
139
141
|
def raise_server_error!
|
140
142
|
return unless @server&.error
|
143
|
+
|
141
144
|
# Force an explanation for the error being raised as the exception cause
|
142
145
|
begin
|
143
146
|
if config.raise_server_errors
|
@@ -468,6 +471,7 @@ module Capybara
|
|
468
471
|
def switch_to_window(window = nil, **options, &window_locator)
|
469
472
|
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && block_given?
|
470
473
|
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !block_given?
|
474
|
+
|
471
475
|
unless scopes.last.nil?
|
472
476
|
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
|
473
477
|
'`within` or `within_frame` blocks.'
|
@@ -769,6 +773,7 @@ module Capybara
|
|
769
773
|
#
|
770
774
|
def configure
|
771
775
|
raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
|
776
|
+
|
772
777
|
yield config
|
773
778
|
end
|
774
779
|
|
@@ -78,12 +78,14 @@ module Capybara
|
|
78
78
|
remove_method :app_host=
|
79
79
|
def app_host=(url)
|
80
80
|
raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
|
81
|
+
|
81
82
|
@app_host = url
|
82
83
|
end
|
83
84
|
|
84
85
|
remove_method :default_host=
|
85
86
|
def default_host=(url)
|
86
87
|
raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
|
88
|
+
|
87
89
|
@default_host = url
|
88
90
|
end
|
89
91
|
|
@@ -23,6 +23,22 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
23
23
|
expect(@session).not_to have_css('p.nosuchclass')
|
24
24
|
end
|
25
25
|
|
26
|
+
it 'should support :id option' do
|
27
|
+
expect(@session).to have_css('h2', id: 'h2one')
|
28
|
+
expect(@session).to have_css('h2')
|
29
|
+
expect(@session).to have_css('h2', id: /h2o/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should support :class option' do
|
33
|
+
expect(@session).to have_css('li', class: 'guitarist')
|
34
|
+
expect(@session).to have_css('li', class: /guitar/)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should support case insensitive :class and :id options' do
|
38
|
+
expect(@session).to have_css('li', class: /UiTaRI/i)
|
39
|
+
expect(@session).to have_css('h2', id: /2ON/i)
|
40
|
+
end
|
41
|
+
|
26
42
|
it 'should respect scopes' do
|
27
43
|
@session.within "//p[@id='first']" do
|
28
44
|
expect(@session).to have_css('a#foo')
|
@@ -61,6 +61,10 @@ Capybara::SpecHelper.spec '#has_field' do
|
|
61
61
|
expect(@session).not_to have_field('Description', type: 'email')
|
62
62
|
expect(@session).not_to have_field('Languages', type: 'textarea')
|
63
63
|
end
|
64
|
+
|
65
|
+
it 'it can find type="hidden" elements if explicity specified' do
|
66
|
+
expect(@session).to have_field('form[data]', with: 'TWTW', type: 'hidden')
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
70
|
context 'with multiple' do
|
@@ -478,6 +478,12 @@ Capybara::SpecHelper.spec 'node' do
|
|
478
478
|
expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
|
479
479
|
end
|
480
480
|
|
481
|
+
it 'should hold modifers at top level' do
|
482
|
+
@session.visit('/form')
|
483
|
+
@session.find(:css, '#address1_city').send_keys('ocean', :shift, 'side')
|
484
|
+
expect(@session.find(:css, '#address1_city').value).to eq 'oceanSIDE'
|
485
|
+
end
|
486
|
+
|
481
487
|
it 'should generate key events', requires: %i[send_keys js] do
|
482
488
|
@session.visit('/with_js')
|
483
489
|
@session.find(:css, '#with-key-events').send_keys([:shift, 't'], [:shift, 'w'])
|
@@ -34,6 +34,6 @@ Capybara::SpecHelper.spec '#to_capybara_node' do
|
|
34
34
|
end.to raise_error(/^expected to find css "#second" within #<Capybara::Node::Element/)
|
35
35
|
expect do
|
36
36
|
expect(para).to have_link(href: %r{/without_simple_html})
|
37
|
-
end.to raise_error(%r{^expected to find
|
37
|
+
end.to raise_error(%r{^expected to find link nil with href matching /\\/without_simple_html/ within #<Capybara::Node::Element})
|
38
38
|
end
|
39
39
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Capybara::SpecHelper.spec '#reset_session!' do
|
4
|
-
it 'removes cookies' do
|
4
|
+
it 'removes cookies from current domain' do
|
5
5
|
@session.visit('/set_cookie')
|
6
6
|
@session.visit('/get_cookie')
|
7
7
|
expect(@session).to have_content('test_cookie')
|
@@ -11,6 +11,20 @@ Capybara::SpecHelper.spec '#reset_session!' do
|
|
11
11
|
expect(@session.body).not_to include('test_cookie')
|
12
12
|
end
|
13
13
|
|
14
|
+
it 'removes ALL cookies', requires: [:server] do
|
15
|
+
domains = ['localhost', '127.0.0.1']
|
16
|
+
domains.each do |domain|
|
17
|
+
@session.visit("http://#{domain}:#{@session.server.port}/set_cookie")
|
18
|
+
@session.visit("http://#{domain}:#{@session.server.port}/get_cookie")
|
19
|
+
expect(@session).to have_content('test_cookie')
|
20
|
+
end
|
21
|
+
@session.reset_session!
|
22
|
+
domains.each do |domain|
|
23
|
+
@session.visit("http://#{domain}:#{@session.server.port}/get_cookie")
|
24
|
+
expect(@session.body).not_to include('test_cookie')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
14
28
|
it 'resets current url, host, path' do
|
15
29
|
@session.visit '/foo'
|
16
30
|
expect(@session.current_url).not_to be_empty
|
@@ -40,8 +40,18 @@ Capybara::SpecHelper.spec Capybara::Selector do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
describe 'field selectors' do
|
43
|
-
|
44
|
-
|
43
|
+
context 'with :id option' do
|
44
|
+
it 'can find specifically by id' do
|
45
|
+
expect(@session.find(:field, id: 'customer_email').value).to eq 'ben@ben.com'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can find by regex' do
|
49
|
+
expect(@session.find(:field, id: /ustomer.emai/).value).to eq 'ben@ben.com'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can find by case-insensitive id' do
|
53
|
+
expect(@session.find(:field, id: /StOmer.emAI/i).value).to eq 'ben@ben.com'
|
54
|
+
end
|
45
55
|
end
|
46
56
|
|
47
57
|
it 'can find specifically by name' do
|
@@ -25,6 +25,12 @@
|
|
25
25
|
</label>
|
26
26
|
</p>
|
27
27
|
|
28
|
+
<p>
|
29
|
+
<label for="customer_other_email">Customer Other Email
|
30
|
+
<input type="text" name="form[customer_other_email]" value="notben@notben.com" id="customer_other_email"/>
|
31
|
+
</label>
|
32
|
+
</p>
|
33
|
+
|
28
34
|
<p>
|
29
35
|
<label for="form_other_title">Other title</label>
|
30
36
|
<select name="form[other_title]" id="form_other_title">
|
@@ -547,6 +553,11 @@ New line after and before textarea tag
|
|
547
553
|
<input type="file" name="form[multiple_documents][]" id="form_multiple_documents" multiple="multiple" />
|
548
554
|
</p>
|
549
555
|
|
556
|
+
<p>
|
557
|
+
<label for="form_directory_upload">Directory Upload</label>
|
558
|
+
<input type="file" name="form[multiple_documents][]" id="form_directory_upload" multiple="multiple" webkitdirectory="webkitdirectory" mozdirectory="mozdirectory" />
|
559
|
+
</p>
|
560
|
+
|
550
561
|
<p>
|
551
562
|
<input type="submit" value="Upload Multiple"/>
|
552
563
|
<p>
|
@@ -643,3 +654,7 @@ New line after and before textarea tag
|
|
643
654
|
<label for="asterisk_input">With Asterisk<abbr title="required">*</abbr></label>
|
644
655
|
<input id="asterisk_input" type="number"value="2016"/>
|
645
656
|
</p>
|
657
|
+
|
658
|
+
<p>
|
659
|
+
<input id="special" {custom}="abcdef" value="custom attribute"/>
|
660
|
+
</p>
|
data/lib/capybara/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XPath
|
4
|
+
module DSL
|
5
|
+
def lowercase
|
6
|
+
method(:translate, 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ', 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')
|
7
|
+
end
|
8
|
+
|
9
|
+
def uppercase
|
10
|
+
method(:translate, 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Capybara
|
16
|
+
module XPathPatches
|
17
|
+
module Renderer
|
18
|
+
def attribute(current, name)
|
19
|
+
return super if name =~ /^[a-zA-Z_:][a-zA-Z0-9_:\.\-]*$/
|
20
|
+
|
21
|
+
"#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
XPath::Renderer.prepend(Capybara::XPathPatches::Renderer)
|
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
|