capybara 2.13.0 → 2.18.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 +5 -5
- data/History.md +218 -18
- data/README.md +54 -23
- data/lib/capybara/config.rb +132 -0
- data/lib/capybara/cucumber.rb +1 -0
- data/lib/capybara/driver/base.rb +14 -0
- data/lib/capybara/dsl.rb +1 -3
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/minitest/spec.rb +14 -37
- data/lib/capybara/minitest.rb +95 -114
- data/lib/capybara/node/actions.rb +10 -10
- data/lib/capybara/node/base.rb +7 -2
- data/lib/capybara/node/element.rb +9 -3
- data/lib/capybara/node/finders.rb +92 -18
- data/lib/capybara/node/matchers.rb +21 -9
- data/lib/capybara/node/simple.rb +5 -0
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/base_query.rb +12 -3
- data/lib/capybara/queries/current_path_query.rb +13 -9
- data/lib/capybara/queries/selector_query.rb +62 -23
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/queries/text_query.rb +10 -5
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/rack_test/browser.rb +13 -5
- data/lib/capybara/rack_test/driver.rb +6 -1
- data/lib/capybara/rack_test/form.rb +4 -3
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/matcher_proxies.rb +45 -0
- data/lib/capybara/rspec/matchers.rb +108 -7
- data/lib/capybara/rspec.rb +3 -1
- data/lib/capybara/selector/filter.rb +13 -41
- data/lib/capybara/selector/filter_set.rb +30 -4
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +36 -15
- data/lib/capybara/selector.rb +63 -42
- data/lib/capybara/selenium/driver.rb +177 -33
- data/lib/capybara/selenium/node.rb +106 -55
- data/lib/capybara/server.rb +6 -5
- data/lib/capybara/session/config.rb +114 -0
- data/lib/capybara/session/matchers.rb +15 -4
- data/lib/capybara/session.rb +178 -65
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/test.js +18 -3
- data/lib/capybara/spec/session/accept_alert_spec.rb +9 -1
- data/lib/capybara/spec/session/accept_prompt_spec.rb +29 -1
- data/lib/capybara/spec/session/all_spec.rb +13 -1
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
- data/lib/capybara/spec/session/assert_selector.rb +1 -1
- data/lib/capybara/spec/session/assert_text.rb +8 -0
- data/lib/capybara/spec/session/assert_title.rb +22 -9
- data/lib/capybara/spec/session/attach_file_spec.rb +8 -1
- data/lib/capybara/spec/session/check_spec.rb +4 -4
- data/lib/capybara/spec/session/choose_spec.rb +2 -2
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
- data/lib/capybara/spec/session/click_link_spec.rb +1 -1
- data/lib/capybara/spec/session/current_url_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +8 -2
- data/lib/capybara/spec/session/find_field_spec.rb +1 -0
- data/lib/capybara/spec/session/find_spec.rb +8 -6
- data/lib/capybara/spec/session/first_spec.rb +10 -5
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +11 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +52 -7
- data/lib/capybara/spec/session/has_link_spec.rb +4 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +64 -6
- data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
- data/lib/capybara/spec/session/has_text_spec.rb +5 -3
- data/lib/capybara/spec/session/has_title_spec.rb +4 -2
- data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
- data/lib/capybara/spec/session/node_spec.rb +50 -26
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +3 -2
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
- data/lib/capybara/spec/session/unselect_spec.rb +2 -2
- data/lib/capybara/spec/session/visit_spec.rb +56 -1
- data/lib/capybara/spec/session/window/become_closed_spec.rb +11 -11
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
- data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
- data/lib/capybara/spec/spec_helper.rb +28 -4
- data/lib/capybara/spec/test_app.rb +3 -1
- data/lib/capybara/spec/views/form.erb +27 -1
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +5 -0
- data/lib/capybara/spec/views/with_html.erb +33 -2
- data/lib/capybara/spec/views/with_js.erb +12 -0
- data/lib/capybara/spec/views/with_windows.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +102 -124
- data/spec/capybara_spec.rb +43 -21
- data/spec/dsl_spec.rb +1 -0
- data/spec/filter_set_spec.rb +28 -0
- data/spec/minitest_spec.rb +9 -1
- data/spec/minitest_spec_spec.rb +19 -5
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/result_spec.rb +20 -0
- data/spec/rspec/shared_spec_matchers.rb +148 -44
- data/spec/rspec/views_spec.rb +4 -0
- data/spec/rspec_matchers_spec.rb +46 -0
- data/spec/rspec_spec.rb +77 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +25 -17
- data/spec/selenium_spec_firefox.rb +2 -1
- data/spec/selenium_spec_marionette.rb +18 -5
- data/spec/session_spec.rb +44 -0
- data/spec/shared_selenium_session.rb +72 -8
- data/spec/spec_helper.rb +4 -0
- metadata +55 -8
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'rack/test'
|
3
3
|
require 'rack/utils'
|
4
|
-
require '
|
4
|
+
require 'mini_mime'
|
5
5
|
require 'nokogiri'
|
6
6
|
require 'cgi'
|
7
7
|
|
@@ -15,6 +15,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
15
15
|
|
16
16
|
def initialize(app, options={})
|
17
17
|
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
18
|
+
@session = nil
|
18
19
|
@app = app
|
19
20
|
@options = DEFAULT_OPTIONS.merge(options)
|
20
21
|
end
|
@@ -43,6 +44,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
43
44
|
browser.visit(path, attributes)
|
44
45
|
end
|
45
46
|
|
47
|
+
def refresh
|
48
|
+
browser.refresh
|
49
|
+
end
|
50
|
+
|
46
51
|
def submit(method, path, attributes)
|
47
52
|
browser.submit(method, path, attributes)
|
48
53
|
end
|
@@ -13,6 +13,8 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
13
13
|
def original_filename; ""; end
|
14
14
|
def content_type; "application/octet-stream"; end
|
15
15
|
def path; @empty_file.path; end
|
16
|
+
def size; 0; end
|
17
|
+
def read; ""; end
|
16
18
|
end
|
17
19
|
|
18
20
|
def params(button)
|
@@ -42,9 +44,8 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
42
44
|
if (value = field['value']).to_s.empty?
|
43
45
|
NilUploadedFile.new
|
44
46
|
else
|
45
|
-
|
46
|
-
|
47
|
-
Rack::Test::UploadedFile.new(value, content_type)
|
47
|
+
mime_info = MiniMime.lookup_by_filename(value)
|
48
|
+
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
48
49
|
end
|
49
50
|
merge_param!(params, field['name'].to_s, file)
|
50
51
|
else
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Capybara
|
2
|
+
module RSpecMatchers
|
3
|
+
module Compound
|
4
|
+
include ::RSpec::Matchers::Composable
|
5
|
+
|
6
|
+
def and(matcher)
|
7
|
+
Capybara::RSpecMatchers::Compound::And.new(self,matcher)
|
8
|
+
end
|
9
|
+
|
10
|
+
def and_then(matcher)
|
11
|
+
::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
|
12
|
+
end
|
13
|
+
|
14
|
+
def or(matcher)
|
15
|
+
Capybara::RSpecMatchers::Compound::Or.new(self, matcher)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class CapybaraEvaluator
|
20
|
+
def initialize(actual, matcher_1, matcher_2)
|
21
|
+
@actual = actual
|
22
|
+
@matcher_1 = matcher_1
|
23
|
+
@matcher_2 = matcher_2
|
24
|
+
@match_results = Hash.new { |h, matcher| h[matcher] = matcher.matches?(@actual) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def matcher_matches?(matcher)
|
28
|
+
@match_results[matcher]
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@match_results.clear
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def match(_expected, actual)
|
41
|
+
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
42
|
+
syncer = sync_element(actual)
|
43
|
+
begin
|
44
|
+
syncer.synchronize do
|
45
|
+
@evaluator.reset
|
46
|
+
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].all?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
rescue
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def sync_element(el)
|
55
|
+
if el.respond_to? :synchronize
|
56
|
+
el
|
57
|
+
elsif el.respond_to? :current_scope
|
58
|
+
el.current_scope
|
59
|
+
else
|
60
|
+
Capybara.string(el)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def match(_expected, actual)
|
70
|
+
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
71
|
+
syncer = sync_element(actual)
|
72
|
+
begin
|
73
|
+
syncer.synchronize do
|
74
|
+
@evaluator.reset
|
75
|
+
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].any?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
rescue
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def sync_element(el)
|
84
|
+
if el.respond_to? :synchronize
|
85
|
+
el
|
86
|
+
elsif el.respond_to? :current_scope
|
87
|
+
el.current_scope
|
88
|
+
else
|
89
|
+
Capybara.string(el)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Capybara
|
3
|
+
module RSpecMatcherProxies
|
4
|
+
def all(*args)
|
5
|
+
if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
|
6
|
+
::RSpec::Matchers::BuiltIn::All.new(*args)
|
7
|
+
else
|
8
|
+
find_all(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def within(*args)
|
13
|
+
if block_given?
|
14
|
+
within_element(*args, &Proc.new)
|
15
|
+
else
|
16
|
+
be_within(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module DSL
|
22
|
+
class <<self
|
23
|
+
remove_method :included
|
24
|
+
|
25
|
+
def included(base)
|
26
|
+
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
|
27
|
+
|
28
|
+
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
|
29
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies)
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if defined?(::RSpec::Matchers)
|
39
|
+
module ::RSpec::Matchers
|
40
|
+
def self.included(base)
|
41
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies) if base.include?(::Capybara::DSL)
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -2,12 +2,15 @@
|
|
2
2
|
module Capybara
|
3
3
|
module RSpecMatchers
|
4
4
|
class Matcher
|
5
|
-
|
5
|
+
if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.0'))
|
6
|
+
require 'capybara/rspec/compound'
|
7
|
+
include ::Capybara::RSpecMatchers::Compound
|
8
|
+
end
|
6
9
|
|
7
10
|
attr_reader :failure_message, :failure_message_when_negated
|
8
11
|
|
9
12
|
def wrap(actual)
|
10
|
-
if actual.respond_to?("has_selector?")
|
13
|
+
@context_el = if actual.respond_to?("has_selector?")
|
11
14
|
actual
|
12
15
|
else
|
13
16
|
Capybara.string(actual.to_s)
|
@@ -33,10 +36,29 @@ module Capybara
|
|
33
36
|
@failure_message_when_negated = e.message
|
34
37
|
return false
|
35
38
|
end
|
39
|
+
|
40
|
+
def session_query_args
|
41
|
+
if @args.last.is_a? Hash
|
42
|
+
@args.last[:session_options] = session_options
|
43
|
+
else
|
44
|
+
@args.push(session_options: session_options)
|
45
|
+
end
|
46
|
+
@args
|
47
|
+
end
|
48
|
+
|
49
|
+
def session_options
|
50
|
+
@context_el ||= nil
|
51
|
+
if @context_el.respond_to? :session_options
|
52
|
+
@context_el.session_options
|
53
|
+
elsif @context_el.respond_to? :current_scope
|
54
|
+
@context_el.current_scope.session_options
|
55
|
+
else
|
56
|
+
Capybara.session_options
|
57
|
+
end
|
58
|
+
end
|
36
59
|
end
|
37
60
|
|
38
61
|
class HaveSelector < Matcher
|
39
|
-
|
40
62
|
def initialize(*args, &filter_block)
|
41
63
|
@args = args
|
42
64
|
@filter_block = filter_block
|
@@ -55,7 +77,45 @@ module Capybara
|
|
55
77
|
end
|
56
78
|
|
57
79
|
def query
|
58
|
-
@query ||= Capybara::Queries::SelectorQuery.new(
|
80
|
+
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class HaveAllSelectors < Matcher
|
85
|
+
def initialize(*args, &filter_block)
|
86
|
+
@args = args
|
87
|
+
@filter_block = filter_block
|
88
|
+
end
|
89
|
+
|
90
|
+
def matches?(actual)
|
91
|
+
wrap_matches?(actual){ |el| el.assert_all_of_selectors(*@args, &@filter_block) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def does_not_match?(actual)
|
95
|
+
raise ArgumentError, "The have_all_selectors matcher does not support use with not_to/should_not"
|
96
|
+
end
|
97
|
+
|
98
|
+
def description
|
99
|
+
"have all selectors"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class HaveNoSelectors < Matcher
|
104
|
+
def initialize(*args, &filter_block)
|
105
|
+
@args = args
|
106
|
+
@filter_block = filter_block
|
107
|
+
end
|
108
|
+
|
109
|
+
def matches?(actual)
|
110
|
+
wrap_matches?(actual){ |el| el.assert_none_of_selectors(*@args, &@filter_block) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def does_not_match?(actual)
|
114
|
+
raise ArgumentError, "The have_none_of_selectors matcher does not support use with not_to/should_not"
|
115
|
+
end
|
116
|
+
|
117
|
+
def description
|
118
|
+
"have no selectors"
|
59
119
|
end
|
60
120
|
end
|
61
121
|
|
@@ -73,7 +133,7 @@ module Capybara
|
|
73
133
|
end
|
74
134
|
|
75
135
|
def query
|
76
|
-
@query ||= Capybara::Queries::MatchQuery.new(
|
136
|
+
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
|
77
137
|
end
|
78
138
|
end
|
79
139
|
|
@@ -155,11 +215,12 @@ module Capybara
|
|
155
215
|
|
156
216
|
class BecomeClosed
|
157
217
|
def initialize(options)
|
158
|
-
@
|
218
|
+
@options = options
|
159
219
|
end
|
160
220
|
|
161
221
|
def matches?(window)
|
162
222
|
@window = window
|
223
|
+
@wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
|
163
224
|
start_time = Capybara::Helpers.monotonic_time
|
164
225
|
while window.exists?
|
165
226
|
return false if (Capybara::Helpers.monotonic_time - start_time) > @wait_time
|
@@ -181,10 +242,26 @@ module Capybara
|
|
181
242
|
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
182
243
|
end
|
183
244
|
|
245
|
+
# RSpec matcher for whether the element(s) matching a given selector exist
|
246
|
+
# See {Capybara::Node::Matcher#assert_selector}
|
184
247
|
def have_selector(*args, &optional_filter_block)
|
185
248
|
HaveSelector.new(*args, &optional_filter_block)
|
186
249
|
end
|
187
250
|
|
251
|
+
# RSpec matcher for whether the element(s) matching a group of selectors exist
|
252
|
+
# See {Capybara::Node::Matcher#assert_all_of_selectors}
|
253
|
+
def have_all_of_selectors(*args, &optional_filter_block)
|
254
|
+
HaveAllSelectors.new(*args, &optional_filter_block)
|
255
|
+
end
|
256
|
+
|
257
|
+
# RSpec matcher for whether no element(s) matching a group of selectors exist
|
258
|
+
# See {Capybara::Node::Matcher#assert_none_of_selectors}
|
259
|
+
def have_none_of_selectors(*args, &optional_filter_block)
|
260
|
+
HaveNoSelectors.new(*args, &optional_filter_block)
|
261
|
+
end
|
262
|
+
|
263
|
+
# RSpec matcher for whether the current element matches a given selector
|
264
|
+
# See {Capybara::Node::Matchers#assert_matches_selector}
|
188
265
|
def match_selector(*args, &optional_filter_block)
|
189
266
|
MatchSelector.new(*args, &optional_filter_block)
|
190
267
|
end
|
@@ -193,22 +270,30 @@ module Capybara
|
|
193
270
|
::RSpec::Matchers.define_negated_matcher :not_match_selector, :match_selector if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.1'))
|
194
271
|
|
195
272
|
|
273
|
+
# RSpec matcher for whether elements(s) matching a given xpath selector exist
|
274
|
+
# See {Capybara::Node::Matchers#has_xpath?}
|
196
275
|
def have_xpath(xpath, options={}, &optional_filter_block)
|
197
276
|
HaveSelector.new(:xpath, xpath, options, &optional_filter_block)
|
198
277
|
end
|
199
278
|
|
279
|
+
# RSpec matcher for whether the current element matches a given xpath selector
|
200
280
|
def match_xpath(xpath, options={}, &optional_filter_block)
|
201
281
|
MatchSelector.new(:xpath, xpath, options, &optional_filter_block)
|
202
282
|
end
|
203
283
|
|
284
|
+
# RSpec matcher for whether elements(s) matching a given css selector exist
|
285
|
+
# See {Capybara::Node::Matchers#has_css?}
|
204
286
|
def have_css(css, options={}, &optional_filter_block)
|
205
287
|
HaveSelector.new(:css, css, options, &optional_filter_block)
|
206
288
|
end
|
207
289
|
|
290
|
+
# RSpec matcher for whether the current element matches a given css selector
|
208
291
|
def match_css(css, options={}, &optional_filter_block)
|
209
292
|
MatchSelector.new(:css, css, options, &optional_filter_block)
|
210
293
|
end
|
211
294
|
|
295
|
+
# RSpec matcher for text on the page
|
296
|
+
# See {Capybara::SessionMatchers#assert_text}
|
212
297
|
def have_text(*args)
|
213
298
|
HaveText.new(*args)
|
214
299
|
end
|
@@ -218,40 +303,56 @@ module Capybara
|
|
218
303
|
HaveTitle.new(title, options)
|
219
304
|
end
|
220
305
|
|
306
|
+
# RSpec matcher for the current path
|
307
|
+
# See {Capybara::SessionMatchers#assert_current_path}
|
221
308
|
def have_current_path(path, options = {})
|
222
309
|
HaveCurrentPath.new(path, options)
|
223
310
|
end
|
224
311
|
|
312
|
+
# RSpec matcher for links
|
313
|
+
# See {Capybara::Node::Matchers#has_link?}
|
225
314
|
def have_link(locator=nil, options={}, &optional_filter_block)
|
226
315
|
locator, options = nil, locator if locator.is_a? Hash
|
227
316
|
HaveSelector.new(:link, locator, options, &optional_filter_block)
|
228
317
|
end
|
229
318
|
|
319
|
+
# RSpec matcher for buttons
|
320
|
+
# See {Capybara::Node::Matchers#has_button?}
|
230
321
|
def have_button(locator=nil, options={}, &optional_filter_block)
|
231
322
|
locator, options = nil, locator if locator.is_a? Hash
|
232
323
|
HaveSelector.new(:button, locator, options, &optional_filter_block)
|
233
324
|
end
|
234
325
|
|
326
|
+
# RSpec matcher for links
|
327
|
+
# See {Capybara::Node::Matchers#has_field?}
|
235
328
|
def have_field(locator=nil, options={}, &optional_filter_block)
|
236
329
|
locator, options = nil, locator if locator.is_a? Hash
|
237
330
|
HaveSelector.new(:field, locator, options, &optional_filter_block)
|
238
331
|
end
|
239
332
|
|
333
|
+
# RSpec matcher for checked fields
|
334
|
+
# See {Capybara::Node::Matchers#has_checked_field?}
|
240
335
|
def have_checked_field(locator=nil, options={}, &optional_filter_block)
|
241
336
|
locator, options = nil, locator if locator.is_a? Hash
|
242
337
|
HaveSelector.new(:field, locator, options.merge(checked: true), &optional_filter_block)
|
243
338
|
end
|
244
339
|
|
340
|
+
# RSpec matcher for unchecked fields
|
341
|
+
# See {Capybara::Node::Matchers#has_unchecked_field?}
|
245
342
|
def have_unchecked_field(locator=nil, options={}, &optional_filter_block)
|
246
343
|
locator, options = nil, locator if locator.is_a? Hash
|
247
344
|
HaveSelector.new(:field, locator, options.merge(unchecked: true), &optional_filter_block)
|
248
345
|
end
|
249
346
|
|
347
|
+
# RSpec matcher for select elements
|
348
|
+
# See {Capybara::Node::Matchers#has_select?}
|
250
349
|
def have_select(locator=nil, options={}, &optional_filter_block)
|
251
350
|
locator, options = nil, locator if locator.is_a? Hash
|
252
351
|
HaveSelector.new(:select, locator, options, &optional_filter_block)
|
253
352
|
end
|
254
353
|
|
354
|
+
# RSpec matcher for table elements
|
355
|
+
# See {Capybara::Node::Matchers#has_table?}
|
255
356
|
def have_table(locator=nil, options={}, &optional_filter_block)
|
256
357
|
locator, options = nil, locator if locator.is_a? Hash
|
257
358
|
HaveSelector.new(:table, locator, options, &optional_filter_block)
|
@@ -267,4 +368,4 @@ module Capybara
|
|
267
368
|
BecomeClosed.new(options)
|
268
369
|
end
|
269
370
|
end
|
270
|
-
end
|
371
|
+
end
|
data/lib/capybara/rspec.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'capybara/dsl'
|
3
2
|
require 'rspec/core'
|
3
|
+
require 'capybara/dsl'
|
4
4
|
require 'capybara/rspec/matchers'
|
5
5
|
require 'capybara/rspec/features'
|
6
|
+
require 'capybara/rspec/matcher_proxies'
|
6
7
|
|
7
8
|
RSpec.configure do |config|
|
8
9
|
config.include Capybara::DSL, :type => :feature
|
@@ -22,6 +23,7 @@ RSpec.configure do |config|
|
|
22
23
|
Capybara.use_default_driver
|
23
24
|
end
|
24
25
|
end
|
26
|
+
|
25
27
|
config.before do
|
26
28
|
if self.class.include?(Capybara::DSL)
|
27
29
|
example = fetch_current_example.call(self)
|
@@ -1,47 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'capybara/selector/filters/node_filter'
|
3
|
+
require 'capybara/selector/filters/expression_filter'
|
4
|
+
|
2
5
|
module Capybara
|
3
6
|
class Selector
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def default
|
17
|
-
@options[:default]
|
18
|
-
end
|
19
|
-
|
20
|
-
def matches?(node, value)
|
21
|
-
return true if skip?(value)
|
22
|
-
|
23
|
-
if !valid_value?(value)
|
24
|
-
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
25
|
-
if default?
|
26
|
-
warn msg + "defaulting to #{default}"
|
27
|
-
value = default
|
28
|
-
else
|
29
|
-
warn msg + "skipping"
|
30
|
-
return true
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
@block.call(node, value)
|
35
|
-
end
|
36
|
-
|
37
|
-
def skip?(value)
|
38
|
-
@options.has_key?(:skip_if) && value == @options[:skip_if]
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def valid_value?(value)
|
44
|
-
!@options.has_key?(:valid_values) || Array(@options[:valid_values]).include?(value)
|
7
|
+
def self.const_missing(const_name)
|
8
|
+
case const_name
|
9
|
+
when :Filter
|
10
|
+
warn "DEPRECATED: Capybara::Selector::Filter is deprecated, please use Capybara::Selector::Filters::NodeFilter instead"
|
11
|
+
Filters::NodeFilter
|
12
|
+
when :ExpressionFilter
|
13
|
+
warn "DEPRECATED: Capybara::Selector::ExpressionFilter is deprecated, please use Capybara::Selector::Filters::ExpressionFilter instead"
|
14
|
+
Filters::ExpressionFilter
|
15
|
+
else
|
16
|
+
super
|
45
17
|
end
|
46
18
|
end
|
47
19
|
end
|
@@ -13,9 +13,11 @@ module Capybara
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def filter(name, *types_and_options, &block)
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
add_filter(name, Filters::NodeFilter, *types_and_options, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def expression_filter(name, *types_and_options, &block)
|
20
|
+
add_filter(name, Filters::ExpressionFilter, *types_and_options, &block)
|
19
21
|
end
|
20
22
|
|
21
23
|
def describe(&block)
|
@@ -23,14 +25,30 @@ module Capybara
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def description(options={})
|
26
|
-
|
28
|
+
options_with_defaults = options.dup
|
29
|
+
filters.each do |name, filter|
|
30
|
+
options_with_defaults[name] = filter.default if filter.default? && !options_with_defaults.has_key?(name)
|
31
|
+
end
|
32
|
+
|
33
|
+
@descriptions.map do |desc|
|
34
|
+
desc.call(options_with_defaults).to_s
|
35
|
+
end.join
|
27
36
|
end
|
28
37
|
|
29
38
|
def filters
|
30
39
|
@filters ||= {}
|
31
40
|
end
|
32
41
|
|
42
|
+
def node_filters
|
43
|
+
filters.reject { |_n, f| f.nil? || f.is_a?(Filters::ExpressionFilter) }.freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
def expression_filters
|
47
|
+
filters.select { |_n, f| f.nil? || f.is_a?(Filters::ExpressionFilter) }.freeze
|
48
|
+
end
|
49
|
+
|
33
50
|
class << self
|
51
|
+
|
34
52
|
def all
|
35
53
|
@filter_sets ||= {}
|
36
54
|
end
|
@@ -43,6 +61,14 @@ module Capybara
|
|
43
61
|
all.delete(name.to_sym)
|
44
62
|
end
|
45
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def add_filter(name, filter_class, *types_and_options, &block)
|
68
|
+
options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
|
69
|
+
types_and_options.each { |k| options[k] = true}
|
70
|
+
filters[name] = filter_class.new(name, block, options)
|
71
|
+
end
|
46
72
|
end
|
47
73
|
end
|
48
74
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Capybara
|
3
|
+
class Selector
|
4
|
+
module Filters
|
5
|
+
class Base
|
6
|
+
def initialize(name, block, options={})
|
7
|
+
@name = name
|
8
|
+
@block = block
|
9
|
+
@options = options
|
10
|
+
@options[:valid_values] = [true,false] if options[:boolean]
|
11
|
+
end
|
12
|
+
|
13
|
+
def default?
|
14
|
+
@options.has_key?(:default)
|
15
|
+
end
|
16
|
+
|
17
|
+
def default
|
18
|
+
@options[:default]
|
19
|
+
end
|
20
|
+
|
21
|
+
def skip?(value)
|
22
|
+
@options.has_key?(:skip_if) && value == @options[:skip_if]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def valid_value?(value)
|
28
|
+
!@options.has_key?(:valid_values) || Array(@options[:valid_values]).include?(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'capybara/selector/filters/base'
|
3
|
+
|
4
|
+
module Capybara
|
5
|
+
class Selector
|
6
|
+
module Filters
|
7
|
+
class ExpressionFilter < Base
|
8
|
+
def apply_filter(expr, value)
|
9
|
+
return expr if skip?(value)
|
10
|
+
|
11
|
+
if !valid_value?(value)
|
12
|
+
msg = "Invalid value #{value.inspect} passed to expression filter #{@name} - "
|
13
|
+
if default?
|
14
|
+
warn msg + "defaulting to #{default}"
|
15
|
+
value = default
|
16
|
+
else
|
17
|
+
warn msg + "skipping"
|
18
|
+
return expr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@block.call(expr, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class IdentityExpressionFilter < ExpressionFilter
|
27
|
+
def initialize
|
28
|
+
end
|
29
|
+
|
30
|
+
def default?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def apply_filter(expr, _value)
|
35
|
+
return expr
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'capybara/selector/filters/base'
|
3
|
+
|
4
|
+
module Capybara
|
5
|
+
class Selector
|
6
|
+
module Filters
|
7
|
+
class NodeFilter < Base
|
8
|
+
def matches?(node, value)
|
9
|
+
return true if skip?(value)
|
10
|
+
|
11
|
+
if !valid_value?(value)
|
12
|
+
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
13
|
+
if default?
|
14
|
+
warn msg + "defaulting to #{default}"
|
15
|
+
value = default
|
16
|
+
else
|
17
|
+
warn msg + "skipping"
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@block.call(node, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|