rubocop-rspec 2.16.0 → 2.18.1
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/CHANGELOG.md +25 -0
- data/README.md +1 -1
- data/config/default.yml +12 -0
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +29 -115
- data/lib/rubocop/cop/rspec/capybara/match_style.rb +38 -0
- data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +23 -96
- data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +19 -75
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +14 -83
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +25 -69
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +26 -63
- data/lib/rubocop/cop/rspec/context_method.rb +5 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +0 -1
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +1 -1
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +10 -12
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +37 -2
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +60 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +0 -1
- data/lib/rubocop/cop/rspec_cops.rb +2 -0
- data/lib/rubocop/rspec/config_formatter.rb +10 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +1 -2
- metadata +19 -5
- data/lib/rubocop/cop/rspec/mixin/capybara_help.rb +0 -80
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b2ef643f2d262910bba4db8e0e6a722b96d0cb6424d04d4261326155176f321
|
4
|
+
data.tar.gz: 29ae9b634075bd3dc11d44969fb00a7542aad3302f3a1aa360a2637e3875a3bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b14f91843dcbc6cd8abfca82a8f21244991aacb79d09a92e975c1eb6d36e554adc44bd1d8b14d2d014fed60b6383f1e102f5142c4a771532be1b60db47c01df1
|
7
|
+
data.tar.gz: 0626b43df05da5b5f386d18be195d2aaa14347a7f70cd99d2d1731e9f9107536c8a0076897b78d9f2f4e69cdeb2a79bc0caaab488a6bee822ea08add9b083fe9
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
## Master (Unreleased)
|
4
4
|
|
5
|
+
## 2.18.1 (2023-01-19)
|
6
|
+
|
7
|
+
- Add `rubocop-capybara` version constraint to prevent sudden cop enabling when it hits 3.0. ([@pirj])
|
8
|
+
|
9
|
+
## 2.18.0 (2023-01-16)
|
10
|
+
|
11
|
+
- Extract Capybara cops to a separate repository, [`rubocop-capybara`](https://github.com/rubocop/rubocop-capybara). The `rubocop-capybara` repository is a dependency of `rubocop-rspec` and the Capybara cops are aliased (`RSpec/Capybara/Foo` == `Capybara/Foo`) until v3.0 is released, so the change will be invisible to users until then. ([@pirj])
|
12
|
+
|
13
|
+
## 2.17.1 (2023-01-16)
|
14
|
+
|
15
|
+
- Fix a false negative for `RSpec/Pending` when using skipped in metadata is multiline string. ([@ydah])
|
16
|
+
- Fix a false positive for `RSpec/NoExpectationExample` when using skipped in metadata is multiline string. ([@ydah])
|
17
|
+
- Fix a false positive for `RSpec/ContextMethod` when multi-line context with `#` at the beginning. ([@ydah])
|
18
|
+
- Fix an incorrect autocorrect for `RSpec/PredicateMatcher` when multiline expect and predicate method with heredoc. ([@ydah])
|
19
|
+
- Fix a false positive for `RSpec/PredicateMatcher` when `include` with multiple argument. ([@ydah])
|
20
|
+
|
21
|
+
## 2.17.0 (2023-01-13)
|
22
|
+
|
23
|
+
- Fix a false positive for `RSpec/PendingWithoutReason` when pending/skip is argument of methods. ([@ydah])
|
24
|
+
- Add new `RSpec/Capybara/MatchStyle` cop. ([@ydah])
|
25
|
+
- Add new `RSpec/Rails/MinitestAssertions` cop. ([@ydah])
|
26
|
+
- Fix a false positive for `RSpec/PendingWithoutReason` when not inside example. ([@ydah])
|
27
|
+
- Fix a false negative for `RSpec/PredicateMatcher` when using `include` and `respond_to`. ([@ydah])
|
28
|
+
- Fix a false positive for `RSpec/StubbedMock` when stubbed message expectation with a block and block parameter. ([@ydah])
|
29
|
+
|
5
30
|
## 2.16.0 (2022-12-13)
|
6
31
|
|
7
32
|
- Add new `RSpec/FactoryBot/FactoryNameStyle` cop. ([@ydah])
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://gitter.im/rubocop-rspec/Lobby)
|
4
4
|
[](https://rubygems.org/gems/rubocop-rspec)
|
5
|
-

|
6
6
|
|
7
7
|
RSpec-specific analysis for your projects, as an extension to
|
8
8
|
[RuboCop](https://github.com/rubocop/rubocop).
|
data/config/default.yml
CHANGED
@@ -887,6 +887,12 @@ RSpec/Capybara/FeatureMethods:
|
|
887
887
|
VersionChanged: '2.0'
|
888
888
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
|
889
889
|
|
890
|
+
RSpec/Capybara/MatchStyle:
|
891
|
+
Description: Checks for usage of deprecated style methods.
|
892
|
+
Enabled: pending
|
893
|
+
VersionAdded: '2.17'
|
894
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/MatchStyle
|
895
|
+
|
890
896
|
RSpec/Capybara/NegationMatcher:
|
891
897
|
Description: Enforces use of `have_no_*` or `not_to` for negated expectations.
|
892
898
|
Enabled: pending
|
@@ -1044,3 +1050,9 @@ RSpec/Rails/InferredSpecType:
|
|
1044
1050
|
routing: routing
|
1045
1051
|
system: system
|
1046
1052
|
views: view
|
1053
|
+
|
1054
|
+
RSpec/Rails/MinitestAssertions:
|
1055
|
+
Description: Check if using Minitest matchers.
|
1056
|
+
Enabled: pending
|
1057
|
+
VersionAdded: '2.17'
|
1058
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/MinitestAssertions
|
data/config/obsoletion.yml
CHANGED
@@ -12,3 +12,12 @@ changed_parameters:
|
|
12
12
|
parameters: IgnoredPatterns
|
13
13
|
alternative: AllowedPatterns
|
14
14
|
severity: warning
|
15
|
+
|
16
|
+
renamed:
|
17
|
+
RSpec/Capybara/CurrentPathExpectation: Capybara/CurrentPathExpectation
|
18
|
+
RSpec/Capybara/MatchStyle: Capybara/MatchStyle
|
19
|
+
RSpec/Capybara/NegationMatcher: Capybara/NegationMatcher
|
20
|
+
RSpec/Capybara/SpecificActions: Capybara/SpecificActions
|
21
|
+
RSpec/Capybara/SpecificFinders: Capybara/SpecificFinders
|
22
|
+
RSpec/Capybara/SpecificMatcher: Capybara/SpecificMatcher
|
23
|
+
RSpec/Capybara/VisibilityMatcher: Capybara/VisibilityMatcher
|
@@ -4,121 +4,35 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module Capybara
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# #
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# #
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# #
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# #
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
'Capybara feature specs - instead, use the ' \
|
37
|
-
'`have_current_path` matcher on `page`'
|
38
|
-
|
39
|
-
RESTRICT_ON_SEND = %i[expect].freeze
|
40
|
-
|
41
|
-
# @!method expectation_set_on_current_path(node)
|
42
|
-
def_node_matcher :expectation_set_on_current_path, <<-PATTERN
|
43
|
-
(send nil? :expect (send {(send nil? :page) nil?} :current_path))
|
44
|
-
PATTERN
|
45
|
-
|
46
|
-
# Supported matchers: eq(...) / match(/regexp/) / match('regexp')
|
47
|
-
# @!method as_is_matcher(node)
|
48
|
-
def_node_matcher :as_is_matcher, <<-PATTERN
|
49
|
-
(send
|
50
|
-
#expectation_set_on_current_path ${:to :to_not :not_to}
|
51
|
-
${(send nil? :eq ...) (send nil? :match (regexp ...))})
|
52
|
-
PATTERN
|
53
|
-
|
54
|
-
# @!method regexp_str_matcher(node)
|
55
|
-
def_node_matcher :regexp_str_matcher, <<-PATTERN
|
56
|
-
(send
|
57
|
-
#expectation_set_on_current_path ${:to :to_not :not_to}
|
58
|
-
$(send nil? :match (str $_)))
|
59
|
-
PATTERN
|
60
|
-
|
61
|
-
def self.autocorrect_incompatible_with
|
62
|
-
[Style::TrailingCommaInArguments]
|
63
|
-
end
|
64
|
-
|
65
|
-
def on_send(node)
|
66
|
-
expectation_set_on_current_path(node) do
|
67
|
-
add_offense(node.loc.selector) do |corrector|
|
68
|
-
next unless node.chained?
|
69
|
-
|
70
|
-
autocorrect(corrector, node)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def autocorrect(corrector, node)
|
78
|
-
as_is_matcher(node.parent) do |to_sym, matcher_node|
|
79
|
-
rewrite_expectation(corrector, node, to_sym, matcher_node)
|
80
|
-
end
|
81
|
-
|
82
|
-
regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp|
|
83
|
-
rewrite_expectation(corrector, node, to_sym, matcher_node)
|
84
|
-
convert_regexp_str_to_literal(corrector, matcher_node, regexp)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def rewrite_expectation(corrector, node, to_symbol, matcher_node)
|
89
|
-
current_path_node = node.first_argument
|
90
|
-
corrector.replace(current_path_node, 'page')
|
91
|
-
corrector.replace(node.parent.loc.selector, 'to')
|
92
|
-
matcher_method = if to_symbol == :to
|
93
|
-
'have_current_path'
|
94
|
-
else
|
95
|
-
'have_no_current_path'
|
96
|
-
end
|
97
|
-
corrector.replace(matcher_node.loc.selector, matcher_method)
|
98
|
-
add_ignore_query_options(corrector, node)
|
99
|
-
end
|
100
|
-
|
101
|
-
def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str)
|
102
|
-
str_node = matcher_node.first_argument
|
103
|
-
regexp_expr = Regexp.new(regexp_str).inspect
|
104
|
-
corrector.replace(str_node, regexp_expr)
|
105
|
-
end
|
106
|
-
|
107
|
-
# `have_current_path` with no options will include the querystring
|
108
|
-
# while `page.current_path` does not.
|
109
|
-
# This ensures the option `ignore_query: true` is added
|
110
|
-
# except when the expectation is a regexp or string
|
111
|
-
def add_ignore_query_options(corrector, node)
|
112
|
-
expectation_node = node.parent.last_argument
|
113
|
-
expectation_last_child = expectation_node.children.last
|
114
|
-
return if %i[regexp str].include?(expectation_last_child.type)
|
115
|
-
|
116
|
-
corrector.insert_after(
|
117
|
-
expectation_last_child,
|
118
|
-
', ignore_query: true'
|
119
|
-
)
|
120
|
-
end
|
121
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks that no expectations are set on Capybara's `current_path`.
|
9
|
+
# #
|
10
|
+
# # The
|
11
|
+
# # https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method[`have_current_path` matcher]
|
12
|
+
# # should be used on `page` to set expectations on Capybara's
|
13
|
+
# # current path, since it uses
|
14
|
+
# # https://github.com/teamcapybara/capybara/blob/master/README.md#asynchronous-javascript-ajax-and-friends[Capybara's waiting functionality]
|
15
|
+
# # which ensures that preceding actions (like `click_link`) have
|
16
|
+
# # completed.
|
17
|
+
# #
|
18
|
+
# # This cop does not support autocorrection in some cases.
|
19
|
+
# #
|
20
|
+
# # @example
|
21
|
+
# # # bad
|
22
|
+
# # expect(current_path).to eq('/callback')
|
23
|
+
# #
|
24
|
+
# # # good
|
25
|
+
# # expect(page).to have_current_path('/callback')
|
26
|
+
# #
|
27
|
+
# # # bad (does not support autocorrection)
|
28
|
+
# # expect(page.current_path).to match(variable)
|
29
|
+
# #
|
30
|
+
# # # good
|
31
|
+
# # expect(page).to have_current_path('/callback')
|
32
|
+
# #
|
33
|
+
# class CurrentPathExpectation < ::RuboCop::Cop::Base; end
|
34
|
+
CurrentPathExpectation =
|
35
|
+
::RuboCop::Cop::Capybara::CurrentPathExpectation
|
122
36
|
end
|
123
37
|
end
|
124
38
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
module Capybara
|
7
|
+
# @!parse
|
8
|
+
# # Checks for usage of deprecated style methods.
|
9
|
+
# #
|
10
|
+
# # @example when using `assert_style`
|
11
|
+
# # # bad
|
12
|
+
# # page.find(:css, '#first').assert_style(display: 'block')
|
13
|
+
# #
|
14
|
+
# # # good
|
15
|
+
# # page.find(:css, '#first').assert_matches_style(display: 'block')
|
16
|
+
# #
|
17
|
+
# # @example when using `has_style?`
|
18
|
+
# # # bad
|
19
|
+
# # expect(page.find(:css, 'first')
|
20
|
+
# # .has_style?(display: 'block')).to be true
|
21
|
+
# #
|
22
|
+
# # # good
|
23
|
+
# # expect(page.find(:css, 'first')
|
24
|
+
# # .matches_style?(display: 'block')).to be true
|
25
|
+
# #
|
26
|
+
# # @example when using `have_style`
|
27
|
+
# # # bad
|
28
|
+
# # expect(page).to have_style(display: 'block')
|
29
|
+
# #
|
30
|
+
# # # good
|
31
|
+
# # expect(page).to match_style(display: 'block')
|
32
|
+
# #
|
33
|
+
# class MatchStyle < ::RuboCop::Cop::Base; end
|
34
|
+
MatchStyle = ::RuboCop::Cop::Capybara::MatchStyle
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -4,102 +4,29 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module Capybara
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# #
|
11
|
-
#
|
12
|
-
# expect(page).to
|
13
|
-
#
|
14
|
-
# #
|
15
|
-
#
|
16
|
-
# expect(page).not_to
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
# expect(page).not_to
|
22
|
-
#
|
23
|
-
# #
|
24
|
-
#
|
25
|
-
# expect(page).to
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
|
32
|
-
CAPYBARA_MATCHERS = %w[
|
33
|
-
selector css xpath text title current_path link button
|
34
|
-
field checked_field unchecked_field select table
|
35
|
-
sibling ancestor
|
36
|
-
].freeze
|
37
|
-
POSITIVE_MATCHERS =
|
38
|
-
Set.new(CAPYBARA_MATCHERS) { |element| :"have_#{element}" }.freeze
|
39
|
-
NEGATIVE_MATCHERS =
|
40
|
-
Set.new(CAPYBARA_MATCHERS) { |element| :"have_no_#{element}" }
|
41
|
-
.freeze
|
42
|
-
RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze
|
43
|
-
|
44
|
-
# @!method not_to?(node)
|
45
|
-
def_node_matcher :not_to?, <<~PATTERN
|
46
|
-
(send ... :not_to
|
47
|
-
(send nil? %POSITIVE_MATCHERS ...))
|
48
|
-
PATTERN
|
49
|
-
|
50
|
-
# @!method have_no?(node)
|
51
|
-
def_node_matcher :have_no?, <<~PATTERN
|
52
|
-
(send ... :to
|
53
|
-
(send nil? %NEGATIVE_MATCHERS ...))
|
54
|
-
PATTERN
|
55
|
-
|
56
|
-
def on_send(node)
|
57
|
-
return unless offense?(node.parent)
|
58
|
-
|
59
|
-
matcher = node.method_name.to_s
|
60
|
-
add_offense(offense_range(node),
|
61
|
-
message: message(matcher)) do |corrector|
|
62
|
-
corrector.replace(node.parent.loc.selector, replaced_runner)
|
63
|
-
corrector.replace(node.loc.selector,
|
64
|
-
replaced_matcher(matcher))
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def offense?(node)
|
71
|
-
(style == :have_no && not_to?(node)) ||
|
72
|
-
(style == :not_to && have_no?(node))
|
73
|
-
end
|
74
|
-
|
75
|
-
def offense_range(node)
|
76
|
-
node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
|
77
|
-
end
|
78
|
-
|
79
|
-
def message(matcher)
|
80
|
-
format(MSG,
|
81
|
-
runner: replaced_runner,
|
82
|
-
matcher: replaced_matcher(matcher))
|
83
|
-
end
|
84
|
-
|
85
|
-
def replaced_runner
|
86
|
-
case style
|
87
|
-
when :have_no
|
88
|
-
'to'
|
89
|
-
when :not_to
|
90
|
-
'not_to'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def replaced_matcher(matcher)
|
95
|
-
case style
|
96
|
-
when :have_no
|
97
|
-
matcher.sub('have_', 'have_no_')
|
98
|
-
when :not_to
|
99
|
-
matcher.sub('have_no_', 'have_')
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Enforces use of `have_no_*` or `not_to` for negated expectations.
|
9
|
+
# #
|
10
|
+
# # @example EnforcedStyle: not_to (default)
|
11
|
+
# # # bad
|
12
|
+
# # expect(page).to have_no_selector
|
13
|
+
# # expect(page).to have_no_css('a')
|
14
|
+
# #
|
15
|
+
# # # good
|
16
|
+
# # expect(page).not_to have_selector
|
17
|
+
# # expect(page).not_to have_css('a')
|
18
|
+
# #
|
19
|
+
# # @example EnforcedStyle: have_no
|
20
|
+
# # # bad
|
21
|
+
# # expect(page).not_to have_selector
|
22
|
+
# # expect(page).not_to have_css('a')
|
23
|
+
# #
|
24
|
+
# # # good
|
25
|
+
# # expect(page).to have_no_selector
|
26
|
+
# # expect(page).to have_no_css('a')
|
27
|
+
# #
|
28
|
+
# class NegationMatcher < ::RuboCop::Cop::Base; end
|
29
|
+
NegationMatcher = ::RuboCop::Cop::Capybara::NegationMatcher
|
103
30
|
end
|
104
31
|
end
|
105
32
|
end
|
@@ -4,81 +4,25 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module Capybara
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# #
|
12
|
-
#
|
13
|
-
# find('
|
14
|
-
# find('
|
15
|
-
# find('
|
16
|
-
#
|
17
|
-
# #
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
SPECIFIC_ACTION = {
|
27
|
-
'button' => 'button',
|
28
|
-
'a' => 'link'
|
29
|
-
}.freeze
|
30
|
-
|
31
|
-
# @!method click_on_selector(node)
|
32
|
-
def_node_matcher :click_on_selector, <<-PATTERN
|
33
|
-
(send _ :find (str $_) ...)
|
34
|
-
PATTERN
|
35
|
-
|
36
|
-
def on_send(node)
|
37
|
-
click_on_selector(node.receiver) do |arg|
|
38
|
-
next unless supported_selector?(arg)
|
39
|
-
# Always check the last selector in the case of multiple selectors
|
40
|
-
# separated by whitespace.
|
41
|
-
# because the `.click` is executed on the element to
|
42
|
-
# which the last selector points.
|
43
|
-
next unless (selector = last_selector(arg))
|
44
|
-
next unless (action = specific_action(selector))
|
45
|
-
next unless CapybaraHelp.specific_option?(node.receiver, arg,
|
46
|
-
action)
|
47
|
-
next unless CapybaraHelp.specific_pseudo_classes?(arg)
|
48
|
-
|
49
|
-
range = offense_range(node, node.receiver)
|
50
|
-
add_offense(range, message: message(action, selector))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def specific_action(selector)
|
57
|
-
SPECIFIC_ACTION[last_selector(selector)]
|
58
|
-
end
|
59
|
-
|
60
|
-
def supported_selector?(selector)
|
61
|
-
!selector.match?(/[>,+~]/)
|
62
|
-
end
|
63
|
-
|
64
|
-
def last_selector(arg)
|
65
|
-
arg.split.last[/^\w+/, 0]
|
66
|
-
end
|
67
|
-
|
68
|
-
def offense_range(node, receiver)
|
69
|
-
receiver.loc.selector.with(end_pos: node.loc.expression.end_pos)
|
70
|
-
end
|
71
|
-
|
72
|
-
def message(action, selector)
|
73
|
-
format(MSG,
|
74
|
-
good_action: good_action(action),
|
75
|
-
selector: selector)
|
76
|
-
end
|
77
|
-
|
78
|
-
def good_action(action)
|
79
|
-
"click_#{action}"
|
80
|
-
end
|
81
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks for there is a more specific actions offered by Capybara.
|
9
|
+
# #
|
10
|
+
# # @example
|
11
|
+
# #
|
12
|
+
# # # bad
|
13
|
+
# # find('a').click
|
14
|
+
# # find('button.cls').click
|
15
|
+
# # find('a', exact_text: 'foo').click
|
16
|
+
# # find('div button').click
|
17
|
+
# #
|
18
|
+
# # # good
|
19
|
+
# # click_link
|
20
|
+
# # click_button(class: 'cls')
|
21
|
+
# # click_link(exact_text: 'foo')
|
22
|
+
# # find('div').click_button
|
23
|
+
# #
|
24
|
+
# class SpecificActions < ::RuboCop::Cop::Base; end
|
25
|
+
SpecificActions = ::RuboCop::Cop::Capybara::SpecificActions
|
82
26
|
end
|
83
27
|
end
|
84
28
|
end
|
@@ -4,89 +4,20 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module Capybara
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# #
|
11
|
-
#
|
12
|
-
# find('
|
13
|
-
#
|
14
|
-
# #
|
15
|
-
#
|
16
|
-
# find_by_id('some-id'
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
include RangeHelp
|
22
|
-
|
23
|
-
MSG = 'Prefer `find_by` over `find`.'
|
24
|
-
RESTRICT_ON_SEND = %i[find].freeze
|
25
|
-
|
26
|
-
# @!method find_argument(node)
|
27
|
-
def_node_matcher :find_argument, <<~PATTERN
|
28
|
-
(send _ :find (str $_) ...)
|
29
|
-
PATTERN
|
30
|
-
|
31
|
-
def on_send(node)
|
32
|
-
find_argument(node) do |arg|
|
33
|
-
next if CssSelector.multiple_selectors?(arg)
|
34
|
-
|
35
|
-
on_attr(node, arg) if attribute?(arg)
|
36
|
-
on_id(node, arg) if CssSelector.id?(arg)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def on_attr(node, arg)
|
43
|
-
return unless (id = CssSelector.attributes(arg)['id'])
|
44
|
-
|
45
|
-
register_offense(node, replaced_arguments(arg, id))
|
46
|
-
end
|
47
|
-
|
48
|
-
def on_id(node, arg)
|
49
|
-
register_offense(node, "'#{arg.to_s.delete('#')}'")
|
50
|
-
end
|
51
|
-
|
52
|
-
def attribute?(arg)
|
53
|
-
CssSelector.attribute?(arg) &&
|
54
|
-
CssSelector.common_attributes?(arg)
|
55
|
-
end
|
56
|
-
|
57
|
-
def register_offense(node, arg_replacement)
|
58
|
-
add_offense(offense_range(node)) do |corrector|
|
59
|
-
corrector.replace(node.loc.selector, 'find_by_id')
|
60
|
-
corrector.replace(node.first_argument.loc.expression,
|
61
|
-
arg_replacement)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def replaced_arguments(arg, id)
|
66
|
-
options = to_options(CssSelector.attributes(arg))
|
67
|
-
options.empty? ? id : "#{id}, #{options}"
|
68
|
-
end
|
69
|
-
|
70
|
-
def to_options(attrs)
|
71
|
-
attrs.each.map do |key, value|
|
72
|
-
next if key == 'id'
|
73
|
-
|
74
|
-
"#{key}: #{value}"
|
75
|
-
end.compact.join(', ')
|
76
|
-
end
|
77
|
-
|
78
|
-
def offense_range(node)
|
79
|
-
range_between(node.loc.selector.begin_pos, end_pos(node))
|
80
|
-
end
|
81
|
-
|
82
|
-
def end_pos(node)
|
83
|
-
if node.loc.end
|
84
|
-
node.loc.end.end_pos
|
85
|
-
else
|
86
|
-
node.loc.expression.end_pos
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks if there is a more specific finder offered by Capybara.
|
9
|
+
# #
|
10
|
+
# # @example
|
11
|
+
# # # bad
|
12
|
+
# # find('#some-id')
|
13
|
+
# # find('[visible][id=some-id]')
|
14
|
+
# #
|
15
|
+
# # # good
|
16
|
+
# # find_by_id('some-id')
|
17
|
+
# # find_by_id('some-id', visible: true)
|
18
|
+
# #
|
19
|
+
# class SpecificFinders < ::RuboCop::Cop::Base; end
|
20
|
+
SpecificFinders = ::RuboCop::Cop::Capybara::SpecificFinders
|
90
21
|
end
|
91
22
|
end
|
92
23
|
end
|