capybara 3.11.1 → 3.12.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 +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
|