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