capybara 3.11.1 → 3.12.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 +18 -1
- data/README.md +4 -4
- data/lib/capybara.rb +7 -0
- data/lib/capybara/node/element.rb +7 -1
- data/lib/capybara/node/matchers.rb +0 -18
- data/lib/capybara/queries/base_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +24 -39
- data/lib/capybara/result.rb +4 -4
- data/lib/capybara/selector.rb +7 -15
- data/lib/capybara/selector/builders/css_builder.rb +59 -27
- data/lib/capybara/selector/builders/xpath_builder.rb +50 -35
- data/lib/capybara/selector/css.rb +7 -7
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +17 -15
- data/lib/capybara/selector/filters/locator_filter.rb +19 -0
- data/lib/capybara/selector/regexp_disassembler.rb +104 -61
- data/lib/capybara/selector/selector.rb +14 -5
- data/lib/capybara/selenium/driver.rb +14 -9
- data/lib/capybara/selenium/driver_specializations/{marionette_driver.rb → firefox_driver.rb} +3 -3
- data/lib/capybara/selenium/nodes/{marionette_node.rb → firefox_node.rb} +1 -1
- data/lib/capybara/spec/session/all_spec.rb +8 -1
- data/lib/capybara/spec/session/assert_selector_spec.rb +0 -10
- data/lib/capybara/spec/session/click_button_spec.rb +5 -3
- data/lib/capybara/spec/session/click_link_spec.rb +5 -5
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/has_css_spec.rb +7 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +17 -0
- data/lib/capybara/spec/session/node_spec.rb +10 -3
- data/lib/capybara/spec/session/window/window_spec.rb +2 -2
- data/lib/capybara/spec/spec_helper.rb +1 -2
- data/lib/capybara/spec/views/obscured.erb +3 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/css_builder_spec.rb +99 -0
- data/spec/result_spec.rb +6 -0
- data/spec/selector_spec.rb +26 -1
- data/spec/selenium_spec_chrome.rb +18 -16
- data/spec/selenium_spec_chrome_remote.rb +0 -2
- data/spec/{selenium_spec_marionette.rb → selenium_spec_firefox.rb} +31 -25
- data/spec/selenium_spec_firefox_remote.rb +4 -6
- data/spec/shared_selenium_session.rb +2 -2
- data/spec/spec_helper.rb +5 -5
- data/spec/xpath_builder_spec.rb +91 -0
- metadata +10 -7
data/spec/result_spec.rb
CHANGED
@@ -72,6 +72,12 @@ RSpec.describe Capybara::Result do
|
|
72
72
|
expect(result[-1].text).to eq 'Delta'
|
73
73
|
expect(result[-2, 3].map(&:text)).to eq %w[Gamma Delta]
|
74
74
|
expect(result[1..7].map(&:text)).to eq %w[Beta Gamma Delta]
|
75
|
+
expect(result[1...3].map(&:text)).to eq %w[Beta Gamma]
|
76
|
+
expect(result[2..-1].map(&:text)).to eq %w[Gamma Delta]
|
77
|
+
expect(result[2...-1].map(&:text)).to eq %w[Gamma]
|
78
|
+
eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.5
|
79
|
+
expect(result[2..].map(&:text)).to eq %w[Gamma Delta]
|
80
|
+
TEST
|
75
81
|
end
|
76
82
|
|
77
83
|
it 'works with filter blocks' do
|
data/spec/selector_spec.rb
CHANGED
@@ -75,6 +75,7 @@ RSpec.describe Capybara do
|
|
75
75
|
|
76
76
|
Capybara.add_selector :custom_xpath_selector do
|
77
77
|
xpath(:valid1, :valid2) { |selector| selector }
|
78
|
+
match { |value| value == 'match_me' }
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
@@ -120,6 +121,26 @@ RSpec.describe Capybara do
|
|
120
121
|
end
|
121
122
|
end
|
122
123
|
|
124
|
+
describe '::[]' do
|
125
|
+
it 'can find a selector' do
|
126
|
+
expect(Capybara::Selector[:field]).not_to be_nil
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'raises if no selector found' do
|
130
|
+
expect { Capybara::Selector[:no_exist] }.to raise_error(ArgumentError, /Unknown selector type/)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '::for' do
|
135
|
+
it 'finds selector that matches the locator' do
|
136
|
+
expect(Capybara::Selector.for('match_me').name).to eq :custom_xpath_selector
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns nil if no match' do
|
140
|
+
expect(Capybara::Selector.for('nothing')).to be nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
123
144
|
describe 'xpath' do
|
124
145
|
it 'uses filter names passed in' do
|
125
146
|
selector = Capybara::Selector.new :test do
|
@@ -423,12 +444,16 @@ RSpec.describe Capybara do
|
|
423
444
|
expression_filter(:random) { |xpath, _| xpath } # do nothing filter
|
424
445
|
end
|
425
446
|
example.run
|
426
|
-
Capybara::Selector
|
447
|
+
Capybara::Selector[:link_or_button].expression_filters.delete(:random)
|
427
448
|
end
|
428
449
|
|
429
450
|
context 'when modified' do
|
430
451
|
it 'should still work' do
|
452
|
+
filter = Capybara::Selector[:link_or_button].expression_filters[:random]
|
453
|
+
allow(filter).to receive(:apply_filter).and_call_original
|
454
|
+
|
431
455
|
expect(string.find(:link_or_button, 'click me', random: 'blah').value).to eq 'click me'
|
456
|
+
expect(filter).to have_received(:apply_filter).with(anything, :random, 'blah', anything)
|
432
457
|
end
|
433
458
|
end
|
434
459
|
end
|
@@ -12,17 +12,17 @@ browser_options.headless! if ENV['HEADLESS']
|
|
12
12
|
browser_options.add_option(:w3c, !!ENV['W3C'])
|
13
13
|
|
14
14
|
Capybara.register_driver :selenium_chrome do |app|
|
15
|
-
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options).tap do |driver|
|
15
|
+
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options, timeout: 30).tap do |driver|
|
16
16
|
driver.browser.download_path = Capybara.save_path
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
Capybara.register_driver :
|
20
|
+
Capybara.register_driver :selenium_chrome_not_clear_storage do |app|
|
21
21
|
chrome_options = {
|
22
22
|
browser: :chrome,
|
23
23
|
options: browser_options
|
24
24
|
}
|
25
|
-
Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage:
|
25
|
+
Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: false, clear_session_storage: false))
|
26
26
|
end
|
27
27
|
|
28
28
|
module TestSessions
|
@@ -30,8 +30,6 @@ module TestSessions
|
|
30
30
|
end
|
31
31
|
|
32
32
|
skipped_tests = %i[response_headers status_code trigger]
|
33
|
-
# skip window tests when headless for now - closing a window not supported by chromedriver/chrome
|
34
|
-
skipped_tests << :windows if ENV['TRAVIS'] && (ENV['SKIP_WINDOW'] || ENV['HEADLESS'])
|
35
33
|
|
36
34
|
$stdout.puts `#{Selenium::WebDriver::Chrome.driver_path} --version` if ENV['CI']
|
37
35
|
|
@@ -39,6 +37,8 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybar
|
|
39
37
|
case example.metadata[:full_description]
|
40
38
|
when /#click_link can download a file$/
|
41
39
|
skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
|
40
|
+
when /Capybara::Session selenium_chrome Capybara::Window#maximize/
|
41
|
+
pending "Chrome headless doesn't support maximize" if ENV['HEADLESS']
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -49,32 +49,34 @@ RSpec.describe 'Capybara::Session with chrome' do
|
|
49
49
|
|
50
50
|
context 'storage' do
|
51
51
|
describe '#reset!' do
|
52
|
-
it '
|
52
|
+
it 'clears storage by default' do
|
53
53
|
@session = TestSessions::Chrome
|
54
54
|
@session.visit('/with_js')
|
55
55
|
@session.find(:css, '#set-storage').click
|
56
56
|
@session.reset!
|
57
57
|
@session.visit('/with_js')
|
58
|
-
|
59
|
-
|
60
|
-
expect(@session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
|
61
|
-
expect(@session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
|
58
|
+
expect(@session.evaluate_script('Object.keys(localStorage)')).to be_empty
|
59
|
+
expect(@session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
|
62
60
|
end
|
63
61
|
|
64
|
-
it '
|
65
|
-
@session = Capybara::Session.new(:
|
62
|
+
it 'does not clear storage when false' do
|
63
|
+
@session = Capybara::Session.new(:selenium_chrome_not_clear_storage, TestApp)
|
66
64
|
@session.visit('/with_js')
|
67
65
|
@session.find(:css, '#set-storage').click
|
68
66
|
@session.reset!
|
69
67
|
@session.visit('/with_js')
|
70
|
-
|
71
|
-
|
72
|
-
expect(@session.evaluate_script('Object.keys(localStorage)')).to be_empty
|
73
|
-
expect(@session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
|
68
|
+
expect(@session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
|
69
|
+
expect(@session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
|
74
70
|
end
|
75
71
|
end
|
76
72
|
end
|
77
73
|
|
74
|
+
context 'timeout' do
|
75
|
+
it 'sets the http client read timeout' do
|
76
|
+
expect(TestSessions::Chrome.driver.browser.send(:bridge).http.read_timeout).to eq 30
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
78
80
|
describe 'filling in Chrome-specific date and time fields with keystrokes' do
|
79
81
|
let(:datetime) { Time.new(1983, 6, 19, 6, 30) }
|
80
82
|
|
@@ -60,8 +60,6 @@ TestSessions::Chrome.driver.browser.file_detector = lambda do |args|
|
|
60
60
|
end
|
61
61
|
|
62
62
|
skipped_tests = %i[response_headers status_code trigger download]
|
63
|
-
# skip window tests when headless for now - closing a window not supported by chromedriver/chrome
|
64
|
-
skipped_tests << :windows if ENV['TRAVIS'] && (ENV['SKIP_WINDOW'] || ENV['HEADLESS'])
|
65
63
|
|
66
64
|
Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
|
67
65
|
case example.metadata[:full_description]
|
@@ -15,51 +15,51 @@ browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |prof
|
|
15
15
|
profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv'
|
16
16
|
end
|
17
17
|
|
18
|
-
Capybara.register_driver :
|
18
|
+
Capybara.register_driver :selenium_firefox do |app|
|
19
19
|
# ::Selenium::WebDriver.logger.level = "debug"
|
20
20
|
Capybara::Selenium::Driver.new(
|
21
21
|
app,
|
22
22
|
browser: :firefox,
|
23
23
|
options: browser_options,
|
24
|
+
timeout: 31,
|
24
25
|
# Get a trace level log from geckodriver
|
25
26
|
# :driver_opts => { args: ['-vv'] }
|
26
27
|
)
|
27
28
|
end
|
28
29
|
|
29
|
-
Capybara.register_driver :
|
30
|
+
Capybara.register_driver :selenium_firefox_not_clear_storage do |app|
|
30
31
|
Capybara::Selenium::Driver.new(
|
31
32
|
app,
|
32
33
|
browser: :firefox,
|
33
|
-
clear_local_storage:
|
34
|
-
clear_session_storage:
|
34
|
+
clear_local_storage: false,
|
35
|
+
clear_session_storage: false,
|
35
36
|
options: browser_options
|
36
37
|
)
|
37
38
|
end
|
38
39
|
|
39
40
|
module TestSessions
|
40
|
-
|
41
|
+
SeleniumFirefox = Capybara::Session.new(:selenium_firefox, TestApp)
|
41
42
|
end
|
42
43
|
|
43
44
|
skipped_tests = %i[response_headers status_code trigger]
|
44
|
-
skipped_tests << :windows if ENV['TRAVIS'] && ENV['SKIP_WINDOW']
|
45
45
|
|
46
46
|
$stdout.puts `#{Selenium::WebDriver::Firefox.driver_path} --version` if ENV['CI']
|
47
47
|
|
48
|
-
Capybara::SpecHelper.run_specs TestSessions::
|
48
|
+
Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capybara_skip: skipped_tests do |example|
|
49
49
|
case example.metadata[:full_description]
|
50
50
|
when 'Capybara::Session selenium node #click should allow multiple modifiers'
|
51
|
-
pending "Firefox doesn't generate an event for shift+control+click" if
|
51
|
+
pending "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session) && !Gem.win_platform?
|
52
52
|
when /^Capybara::Session selenium node #double_click/
|
53
|
-
pending "selenium-webdriver/geckodriver doesn't generate double click event" if
|
53
|
+
pending "selenium-webdriver/geckodriver doesn't generate double click event" if firefox_lt?(59, @session)
|
54
54
|
when 'Capybara::Session selenium #accept_prompt should accept the prompt with a blank response when there is a default'
|
55
|
-
pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if
|
55
|
+
pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if firefox_lt?(63, @session)
|
56
56
|
when 'Capybara::Session selenium #attach_file with multipart form should fire change once for each set of files uploaded'
|
57
57
|
pending 'Gekcodriver appends files so we have to first call clear for multiple files which creates an extra change ' \
|
58
58
|
'if files are already set'
|
59
59
|
when 'Capybara::Session selenium #attach_file with multipart form should fire change once when uploading multiple files from empty'
|
60
|
-
pending "FF < 62 doesn't support setting all files at once" if
|
60
|
+
pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session)
|
61
61
|
when 'Capybara::Session selenium #accept_confirm should work with nested modals'
|
62
|
-
skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if
|
62
|
+
skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session)
|
63
63
|
when 'Capybara::Session selenium #click_link can download a file'
|
64
64
|
skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
|
65
65
|
when 'Capybara::Session selenium #reset_session! removes ALL cookies'
|
@@ -69,8 +69,8 @@ end
|
|
69
69
|
|
70
70
|
RSpec.describe 'Capybara::Session with firefox' do # rubocop:disable RSpec/MultipleDescribes
|
71
71
|
include Capybara::SpecHelper
|
72
|
-
include_examples 'Capybara::Session', TestSessions::
|
73
|
-
include_examples Capybara::RSpecMatchers, TestSessions::
|
72
|
+
include_examples 'Capybara::Session', TestSessions::SeleniumFirefox, :selenium_firefox
|
73
|
+
include_examples Capybara::RSpecMatchers, TestSessions::SeleniumFirefox, :selenium_firefox
|
74
74
|
end
|
75
75
|
|
76
76
|
RSpec.describe Capybara::Selenium::Driver do
|
@@ -125,33 +125,39 @@ RSpec.describe Capybara::Selenium::Driver do
|
|
125
125
|
|
126
126
|
context 'storage' do
|
127
127
|
describe '#reset!' do
|
128
|
-
it '
|
129
|
-
@session = TestSessions::
|
128
|
+
it 'clears storage by default' do
|
129
|
+
@session = TestSessions::SeleniumFirefox
|
130
130
|
@session.visit('/with_js')
|
131
131
|
@session.find(:css, '#set-storage').click
|
132
132
|
@session.reset!
|
133
133
|
@session.visit('/with_js')
|
134
|
-
expect(@session.driver.browser.local_storage.keys).
|
135
|
-
expect(@session.driver.browser.session_storage.keys).
|
134
|
+
expect(@session.driver.browser.local_storage.keys).to be_empty
|
135
|
+
expect(@session.driver.browser.session_storage.keys).to be_empty
|
136
136
|
end
|
137
137
|
|
138
|
-
it '
|
139
|
-
@session = Capybara::Session.new(:
|
138
|
+
it 'does not clear storage when false' do
|
139
|
+
@session = Capybara::Session.new(:selenium_firefox_not_clear_storage, TestApp)
|
140
140
|
@session.visit('/with_js')
|
141
141
|
@session.find(:css, '#set-storage').click
|
142
142
|
@session.reset!
|
143
143
|
@session.visit('/with_js')
|
144
|
-
expect(@session.driver.browser.local_storage.keys).
|
145
|
-
expect(@session.driver.browser.session_storage.keys).
|
144
|
+
expect(@session.driver.browser.local_storage.keys).not_to be_empty
|
145
|
+
expect(@session.driver.browser.session_storage.keys).not_to be_empty
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
149
|
+
|
150
|
+
context 'timeout' do
|
151
|
+
it 'sets the http client read timeout' do
|
152
|
+
expect(TestSessions::SeleniumFirefox.driver.browser.send(:bridge).http.read_timeout).to eq 31
|
153
|
+
end
|
154
|
+
end
|
149
155
|
end
|
150
156
|
|
151
157
|
RSpec.describe Capybara::Selenium::Node do
|
152
158
|
context '#click' do
|
153
159
|
it 'warns when attempting on a table row' do
|
154
|
-
session = TestSessions::
|
160
|
+
session = TestSessions::SeleniumFirefox
|
155
161
|
session.visit('/tables')
|
156
162
|
tr = session.find(:css, '#agent_table tr:first-child')
|
157
163
|
allow(tr.base).to receive(:warn)
|
@@ -160,7 +166,7 @@ RSpec.describe Capybara::Selenium::Node do
|
|
160
166
|
end
|
161
167
|
|
162
168
|
it 'should allow multiple modifiers', requires: [:js] do
|
163
|
-
session = TestSessions::
|
169
|
+
session = TestSessions::SeleniumFirefox
|
164
170
|
session.visit('with_js')
|
165
171
|
# Firefox v62+ doesn't generate an event for control+shift+click
|
166
172
|
session.find(:css, '#click-test').click(:alt, :ctrl, :meta)
|
@@ -171,7 +177,7 @@ RSpec.describe Capybara::Selenium::Node do
|
|
171
177
|
|
172
178
|
context '#send_keys' do
|
173
179
|
it 'should process space' do
|
174
|
-
session = TestSessions::
|
180
|
+
session = TestSessions::SeleniumFirefox
|
175
181
|
session.visit('/form')
|
176
182
|
session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side'])
|
177
183
|
expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE'
|
@@ -56,25 +56,23 @@ TestSessions::RemoteFirefox.driver.browser.file_detector = lambda do |args|
|
|
56
56
|
end
|
57
57
|
|
58
58
|
skipped_tests = %i[response_headers status_code trigger download]
|
59
|
-
# skip window tests when headless for now - closing a window not supported by chromedriver/chrome
|
60
|
-
skipped_tests << :windows if ENV['TRAVIS'] && (ENV['SKIP_WINDOW'] || ENV['HEADLESS'])
|
61
59
|
|
62
60
|
Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
|
63
61
|
case example.metadata[:full_description]
|
64
62
|
when 'Capybara::Session selenium_firefox_remote node #click should allow multiple modifiers'
|
65
|
-
skip "Firefox doesn't generate an event for shift+control+click" if
|
63
|
+
skip "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session)
|
66
64
|
when 'Capybara::Session selenium_firefox_remote #accept_prompt should accept the prompt with a blank response when there is a default'
|
67
|
-
pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if
|
65
|
+
pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if firefox_lt?(63, @session)
|
68
66
|
when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once for each set of files uploaded'
|
69
67
|
pending 'Gekcodriver appends files so we have to first call clear for multiple files which creates an extra change ' \
|
70
68
|
'if files are already set'
|
71
69
|
when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once when uploading multiple files from empty'
|
72
|
-
pending "FF < 62 doesn't support setting all files at once" if
|
70
|
+
pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session)
|
73
71
|
when 'Capybara::Session selenium_firefox_remote #reset_session! removes ALL cookies'
|
74
72
|
pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
|
75
73
|
when /#accept_confirm should work with nested modals$/
|
76
74
|
# skip because this is timing based and hence flaky when set to pending
|
77
|
-
skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if
|
75
|
+
skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session)
|
78
76
|
end
|
79
77
|
end
|
80
78
|
|
@@ -295,7 +295,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
295
295
|
|
296
296
|
describe 'Element#drag_to' do
|
297
297
|
before do
|
298
|
-
skip "Firefox < 62 doesn't support a DataTransfer constuctor" if
|
298
|
+
skip "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, session)
|
299
299
|
skip "IE doesn't support a DataTransfer constuctor" if ie?(session)
|
300
300
|
end
|
301
301
|
|
@@ -336,7 +336,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
336
336
|
|
337
337
|
describe 'Capybara#Node#attach_file' do
|
338
338
|
it 'can attach a directory' do
|
339
|
-
pending "Geckodriver doesn't support uploading a directory" if
|
339
|
+
pending "Geckodriver doesn't support uploading a directory" if firefox?(session)
|
340
340
|
pending "Selenium remote doesn't support transferring a directory" if remote?(session)
|
341
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
342
|
|
data/spec/spec_helper.rb
CHANGED
@@ -6,17 +6,17 @@ require 'webdrivers' if ENV['CI']
|
|
6
6
|
|
7
7
|
module Capybara
|
8
8
|
module SpecHelper
|
9
|
-
def
|
9
|
+
def firefox?(session)
|
10
10
|
browser_name(session) == :firefox &&
|
11
11
|
session.driver.browser.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
14
|
+
def firefox_lt?(version, session)
|
15
|
+
firefox?(session) && (session.driver.browser.capabilities[:browser_version].to_f < version)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
18
|
+
def firefox_gte?(version, session)
|
19
|
+
firefox?(session) && (session.driver.browser.capabilities[:browser_version].to_f >= version)
|
20
20
|
end
|
21
21
|
|
22
22
|
def chrome?(session)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Capybara::Selector::XPathBuilder do
|
6
|
+
let :builder do
|
7
|
+
::Capybara::Selector::XPathBuilder.new(@xpath)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'add_attribute_conditions' do
|
11
|
+
it 'adds a single string condition to a single selector' do
|
12
|
+
@xpath = './/div'
|
13
|
+
selector = builder.add_attribute_conditions(random: 'abc')
|
14
|
+
expect(selector).to eq %((.//div)[(./@random = 'abc')])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'adds multiple string conditions to a single selector' do
|
18
|
+
@xpath = './/div'
|
19
|
+
selector = builder.add_attribute_conditions(random: 'abc', other: 'def')
|
20
|
+
expect(selector).to eq %(((.//div)[(./@random = 'abc')])[(./@other = 'def')])
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds a single string condition to a multiple selector' do
|
24
|
+
@xpath = XPath.descendant(:div, :ul)
|
25
|
+
selector = builder.add_attribute_conditions(random: 'abc')
|
26
|
+
expect(selector.to_s).to eq @xpath[XPath.attr(:random) == 'abc'].to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'adds multiple string conditions to a multiple selector' do
|
30
|
+
@xpath = XPath.descendant(:div, :ul)
|
31
|
+
selector = builder.add_attribute_conditions(random: 'abc', other: 'def')
|
32
|
+
expect(selector.to_s).to eq %(.//*[self::div | self::ul][(./@random = 'abc')][(./@other = 'def')])
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'adds simple regexp conditions to a single selector' do
|
36
|
+
@xpath = XPath.descendant(:div)
|
37
|
+
selector = builder.add_attribute_conditions(random: /abc/, other: /def/)
|
38
|
+
expect(selector.to_s).to eq %(.//div[./@random[contains(., 'abc')]][./@other[contains(., 'def')]])
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'adds wildcard regexp conditions to a single selector' do
|
42
|
+
@xpath = './/div'
|
43
|
+
selector = builder.add_attribute_conditions(random: /abc.*def/, other: /def.*ghi/)
|
44
|
+
expect(selector).to eq %(((.//div)[./@random[(contains(., 'abc') and contains(., 'def'))]])[./@other[(contains(., 'def') and contains(., 'ghi'))]])
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds alternated regexp conditions to a single selector' do
|
48
|
+
@xpath = XPath.descendant(:div)
|
49
|
+
selector = builder.add_attribute_conditions(random: /abc|def/, other: /def|ghi/)
|
50
|
+
expect(selector.to_s).to eq %(.//div[./@random[(contains(., 'abc') or contains(., 'def'))]][./@other[(contains(., 'def') or contains(., 'ghi'))]])
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'adds alternated regexp conditions to a multiple selector' do
|
54
|
+
@xpath = XPath.descendant(:div, :ul)
|
55
|
+
selector = builder.add_attribute_conditions(other: /def.*ghi|jkl/)
|
56
|
+
expect(selector.to_s).to eq %(.//*[self::div | self::ul][./@other[((contains(., 'def') and contains(., 'ghi')) or contains(., 'jkl'))]])
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns original selector when regexp can't be substringed" do
|
60
|
+
@xpath = './/div'
|
61
|
+
selector = builder.add_attribute_conditions(other: /.+/)
|
62
|
+
expect(selector).to eq '(.//div)[./@other]'
|
63
|
+
end
|
64
|
+
|
65
|
+
context ':class' do
|
66
|
+
it 'handles string' do
|
67
|
+
@xpath = './/a'
|
68
|
+
selector = builder.add_attribute_conditions(class: 'my_class')
|
69
|
+
expect(selector).to eq %((.//a)[contains(concat(' ', normalize-space(./@class), ' '), ' my_class ')])
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles negated strings' do
|
73
|
+
@xpath = XPath.descendant(:a)
|
74
|
+
selector = builder.add_attribute_conditions(class: '!my_class')
|
75
|
+
expect(selector.to_s).to eq @xpath[!XPath.attr(:class).contains_word('my_class')].to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'handles array of strings' do
|
79
|
+
@xpath = './/a'
|
80
|
+
selector = builder.add_attribute_conditions(class: %w[my_class my_other_class])
|
81
|
+
expect(selector).to eq %((.//a)[(contains(concat(' ', normalize-space(./@class), ' '), ' my_class ') and contains(concat(' ', normalize-space(./@class), ' '), ' my_other_class '))])
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'handles array of string when negated included' do
|
85
|
+
@xpath = XPath.descendant(:a)
|
86
|
+
selector = builder.add_attribute_conditions(class: %w[my_class !my_other_class])
|
87
|
+
expect(selector.to_s).to eq @xpath[XPath.attr(:class).contains_word('my_class') & !XPath.attr(:class).contains_word('my_other_class')].to_s
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|