rubocop-rspec 2.12.1 → 2.13.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/CHANGELOG.md +110 -86
- data/config/default.yml +44 -6
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +8 -9
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +8 -9
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -0
- data/lib/rubocop/cop/rspec/around_block.rb +26 -3
- data/lib/rubocop/cop/rspec/be.rb +0 -1
- data/lib/rubocop/cop/rspec/be_eq.rb +0 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +0 -1
- data/lib/rubocop/cop/rspec/before_after_all.rb +1 -0
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +9 -3
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -1
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +86 -0
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +91 -10
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -1
- data/lib/rubocop/cop/rspec/change_by_zero.rb +60 -5
- data/lib/rubocop/cop/rspec/class_check.rb +101 -0
- data/lib/rubocop/cop/rspec/context_method.rb +2 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +49 -18
- data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
- data/lib/rubocop/cop/rspec/describe_method.rb +1 -0
- data/lib/rubocop/cop/rspec/described_class.rb +4 -14
- data/lib/rubocop/cop/rspec/dialect.rb +1 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +19 -4
- data/lib/rubocop/cop/rspec/empty_hook.rb +2 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +4 -9
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +2 -1
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +32 -2
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -1
- data/lib/rubocop/cop/rspec/example_length.rb +2 -1
- data/lib/rubocop/cop/rspec/example_without_description.rb +2 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +2 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -0
- data/lib/rubocop/cop/rspec/expect_actual.rb +3 -0
- data/lib/rubocop/cop/rspec/expect_change.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +4 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -0
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +2 -1
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +26 -12
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -0
- data/lib/rubocop/cop/rspec/file_path.rb +6 -3
- data/lib/rubocop/cop/rspec/focus.rb +18 -0
- data/lib/rubocop/cop/rspec/hook_argument.rb +7 -2
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +10 -9
- data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +0 -1
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -0
- data/lib/rubocop/cop/rspec/implicit_expect.rb +0 -2
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_variable.rb +0 -1
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +16 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +15 -15
- data/lib/rubocop/cop/rspec/let_before_examples.rb +7 -8
- data/lib/rubocop/cop/rspec/let_setup.rb +4 -4
- data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +2 -1
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +99 -0
- data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -5
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -3
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -2
- data/lib/rubocop/cop/rspec/named_subject.rb +2 -1
- data/lib/rubocop/cop/rspec/nested_groups.rb +45 -25
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +64 -0
- data/lib/rubocop/cop/rspec/not_to_not.rb +1 -2
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -0
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -1
- data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -2
- data/lib/rubocop/cop/rspec/receive_counts.rb +14 -15
- data/lib/rubocop/cop/rspec/receive_never.rb +4 -5
- data/lib/rubocop/cop/rspec/repeated_description.rb +25 -26
- data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +28 -29
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +28 -29
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +32 -33
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +1 -5
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +0 -1
- data/lib/rubocop/cop/rspec/subject_declaration.rb +0 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +15 -15
- data/lib/rubocop/cop/rspec/variable_definition.rb +1 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +6 -7
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -0
- data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
- data/lib/rubocop/cop/rspec/yield.rb +2 -1
- data/lib/rubocop/cop/rspec_cops.rb +3 -0
- data/lib/rubocop/rspec/config_formatter.rb +14 -3
- data/lib/rubocop/rspec/inject.rb +1 -3
- data/lib/rubocop/rspec/language/node_pattern.rb +4 -0
- data/lib/rubocop/rspec/language.rb +6 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop/rspec/wording.rb +2 -2
- data/lib/rubocop/rspec.rb +14 -0
- data/lib/rubocop-rspec.rb +3 -0
- metadata +10 -88
@@ -12,19 +12,21 @@ module RuboCop
|
|
12
12
|
# expect(page).to have_selector('button')
|
13
13
|
# expect(page).to have_no_selector('button.cls')
|
14
14
|
# expect(page).to have_css('button')
|
15
|
-
# expect(page).to have_no_css('a.cls',
|
15
|
+
# expect(page).to have_no_css('a.cls', href: 'http://example.com')
|
16
16
|
# expect(page).to have_css('table.cls')
|
17
17
|
# expect(page).to have_css('select')
|
18
|
+
# expect(page).to have_css('input', exact_text: 'foo')
|
18
19
|
#
|
19
20
|
# # good
|
20
21
|
# expect(page).to have_button
|
21
22
|
# expect(page).to have_no_button(class: 'cls')
|
22
23
|
# expect(page).to have_button
|
23
|
-
# expect(page).to have_no_link('foo', class: 'cls')
|
24
|
+
# expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
|
24
25
|
# expect(page).to have_table(class: 'cls')
|
25
26
|
# expect(page).to have_select
|
27
|
+
# expect(page).to have_field('foo')
|
26
28
|
#
|
27
|
-
class SpecificMatcher < Base
|
29
|
+
class SpecificMatcher < Base # rubocop:disable Metrics/ClassLength
|
28
30
|
MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
|
29
31
|
RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
|
30
32
|
have_no_css].freeze
|
@@ -32,20 +34,57 @@ module RuboCop
|
|
32
34
|
'button' => 'button',
|
33
35
|
'a' => 'link',
|
34
36
|
'table' => 'table',
|
35
|
-
'select' => 'select'
|
37
|
+
'select' => 'select',
|
38
|
+
'input' => 'field'
|
36
39
|
}.freeze
|
40
|
+
SPECIFIC_MATCHER_OPTIONS = {
|
41
|
+
'button' => (
|
42
|
+
CssSelector::COMMON_OPTIONS + %w[disabled name value title type]
|
43
|
+
).freeze,
|
44
|
+
'link' => (
|
45
|
+
CssSelector::COMMON_OPTIONS + %w[href alt title download]
|
46
|
+
).freeze,
|
47
|
+
'table' => (
|
48
|
+
CssSelector::COMMON_OPTIONS + %w[
|
49
|
+
caption with_cols cols with_rows rows
|
50
|
+
]
|
51
|
+
).freeze,
|
52
|
+
'select' => (
|
53
|
+
CssSelector::COMMON_OPTIONS + %w[
|
54
|
+
disabled name placeholder options enabled_options
|
55
|
+
disabled_options selected with_selected multiple with_options
|
56
|
+
]
|
57
|
+
).freeze,
|
58
|
+
'field' => (
|
59
|
+
CssSelector::COMMON_OPTIONS + %w[
|
60
|
+
checked unchecked disabled valid name placeholder
|
61
|
+
validation_message readonly with type multiple
|
62
|
+
]
|
63
|
+
).freeze
|
64
|
+
}.freeze
|
65
|
+
SPECIFIC_MATCHER_PSEUDO_CLASSES = %w[
|
66
|
+
not() disabled enabled checked unchecked
|
67
|
+
].freeze
|
37
68
|
|
38
69
|
# @!method first_argument(node)
|
39
70
|
def_node_matcher :first_argument, <<-PATTERN
|
40
71
|
(send nil? _ (str $_) ... )
|
41
72
|
PATTERN
|
42
73
|
|
74
|
+
# @!method option?(node)
|
75
|
+
def_node_search :option?, <<-PATTERN
|
76
|
+
(pair (sym %) _)
|
77
|
+
PATTERN
|
78
|
+
|
43
79
|
def on_send(node)
|
44
|
-
|
45
|
-
|
46
|
-
|
80
|
+
first_argument(node) do |arg|
|
81
|
+
next unless (matcher = specific_matcher(arg))
|
82
|
+
next if CssSelector.multiple_selectors?(arg)
|
83
|
+
next unless specific_matcher_option?(node, arg, matcher)
|
84
|
+
next unless specific_matcher_pseudo_classes?(arg)
|
47
85
|
|
48
|
-
|
86
|
+
add_offense(node, message: message(node, matcher))
|
87
|
+
end
|
49
88
|
end
|
50
89
|
|
51
90
|
private
|
@@ -55,8 +94,50 @@ module RuboCop
|
|
55
94
|
SPECIFIC_MATCHER[splitted_arg]
|
56
95
|
end
|
57
96
|
|
58
|
-
def
|
59
|
-
|
97
|
+
def specific_matcher_option?(node, arg, matcher)
|
98
|
+
attrs = CssSelector.attributes(arg).keys
|
99
|
+
return true if attrs.empty?
|
100
|
+
return false unless replaceable_matcher?(node, matcher, attrs)
|
101
|
+
|
102
|
+
attrs.all? do |attr|
|
103
|
+
SPECIFIC_MATCHER_OPTIONS.fetch(matcher, []).include?(attr)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def specific_matcher_pseudo_classes?(arg)
|
108
|
+
CssSelector.pseudo_classes(arg).all? do |pseudo_class|
|
109
|
+
replaceable_pseudo_class?(pseudo_class, arg)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def replaceable_pseudo_class?(pseudo_class, arg)
|
114
|
+
unless SPECIFIC_MATCHER_PSEUDO_CLASSES.include?(pseudo_class)
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
|
118
|
+
case pseudo_class
|
119
|
+
when 'not()' then replaceable_pseudo_class_not?(arg)
|
120
|
+
else true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def replaceable_pseudo_class_not?(arg)
|
125
|
+
arg.scan(/not\(.*?\)/).all? do |not_arg|
|
126
|
+
CssSelector.attributes(not_arg).values.all? do |v|
|
127
|
+
v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def replaceable_matcher?(node, matcher, attrs)
|
133
|
+
case matcher
|
134
|
+
when 'link' then replaceable_to_have_link?(node, attrs)
|
135
|
+
else true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def replaceable_to_have_link?(node, attrs)
|
140
|
+
option?(node, :href) || attrs.include?('href')
|
60
141
|
end
|
61
142
|
|
62
143
|
def message(node, matcher)
|
@@ -5,12 +5,20 @@ module RuboCop
|
|
5
5
|
module RSpec
|
6
6
|
# Prefer negated matchers over `to change.by(0)`.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# In the case of composite expectations, cop suggest using the
|
9
|
+
# negation matchers of `RSpec::Matchers#change`.
|
10
|
+
#
|
11
|
+
# By default the cop does not support autocorrect of
|
12
|
+
# compound expectations, but if you set the
|
13
|
+
# negated matcher for `change`, e.g. `not_change` with
|
14
|
+
# the `NegatedMatcher` option, the cop will perform the autocorrection.
|
15
|
+
#
|
16
|
+
# @example NegatedMatcher: ~ (default)
|
9
17
|
# # bad
|
10
18
|
# expect { run }.to change(Foo, :bar).by(0)
|
11
19
|
# expect { run }.to change { Foo.bar }.by(0)
|
12
20
|
#
|
13
|
-
# # bad - compound expectations
|
21
|
+
# # bad - compound expectations (does not support autocorrection)
|
14
22
|
# expect { run }
|
15
23
|
# .to change(Foo, :bar).by(0)
|
16
24
|
# .and change(Foo, :baz).by(0)
|
@@ -31,10 +39,28 @@ module RuboCop
|
|
31
39
|
# .to not_change { Foo.bar }
|
32
40
|
# .and not_change { Foo.baz }
|
33
41
|
#
|
42
|
+
# @example NegatedMatcher: not_change
|
43
|
+
# # bad (support autocorrection to good case)
|
44
|
+
# expect { run }
|
45
|
+
# .to change(Foo, :bar).by(0)
|
46
|
+
# .and change(Foo, :baz).by(0)
|
47
|
+
# expect { run }
|
48
|
+
# .to change { Foo.bar }.by(0)
|
49
|
+
# .and change { Foo.baz }.by(0)
|
50
|
+
#
|
51
|
+
# # good
|
52
|
+
# define_negated_matcher :not_change, :change
|
53
|
+
# expect { run }
|
54
|
+
# .to not_change(Foo, :bar)
|
55
|
+
# .and not_change(Foo, :baz)
|
56
|
+
# expect { run }
|
57
|
+
# .to not_change { Foo.bar }
|
58
|
+
# .and not_change { Foo.baz }
|
59
|
+
#
|
34
60
|
class ChangeByZero < Base
|
35
61
|
extend AutoCorrector
|
36
62
|
MSG = 'Prefer `not_to change` over `to change.by(0)`.'
|
37
|
-
MSG_COMPOUND = 'Prefer
|
63
|
+
MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
|
38
64
|
'over `change.by(0)`.'
|
39
65
|
RESTRICT_ON_SEND = %i[change].freeze
|
40
66
|
|
@@ -55,6 +81,11 @@ module RuboCop
|
|
55
81
|
(int 0))
|
56
82
|
PATTERN
|
57
83
|
|
84
|
+
# @!method change_nodes(node)
|
85
|
+
def_node_search :change_nodes, <<-PATTERN
|
86
|
+
$(send nil? :change ...)
|
87
|
+
PATTERN
|
88
|
+
|
58
89
|
def on_send(node)
|
59
90
|
expect_change_with_arguments(node.parent) do
|
60
91
|
check_offense(node.parent)
|
@@ -70,7 +101,9 @@ module RuboCop
|
|
70
101
|
def check_offense(node)
|
71
102
|
expression = node.loc.expression
|
72
103
|
if compound_expectations?(node)
|
73
|
-
add_offense(expression, message:
|
104
|
+
add_offense(expression, message: message_compound) do |corrector|
|
105
|
+
autocorrect_compound(corrector, node)
|
106
|
+
end
|
74
107
|
else
|
75
108
|
add_offense(expression) do |corrector|
|
76
109
|
autocorrect(corrector, node)
|
@@ -79,7 +112,7 @@ module RuboCop
|
|
79
112
|
end
|
80
113
|
|
81
114
|
def compound_expectations?(node)
|
82
|
-
%i[and or].include?(node.parent.method_name)
|
115
|
+
%i[and or & |].include?(node.parent.method_name)
|
83
116
|
end
|
84
117
|
|
85
118
|
def autocorrect(corrector, node)
|
@@ -87,6 +120,28 @@ module RuboCop
|
|
87
120
|
range = node.loc.dot.with(end_pos: node.loc.expression.end_pos)
|
88
121
|
corrector.remove(range)
|
89
122
|
end
|
123
|
+
|
124
|
+
def autocorrect_compound(corrector, node)
|
125
|
+
return unless negated_matcher
|
126
|
+
|
127
|
+
change_nodes(node) do |change_node|
|
128
|
+
corrector.replace(change_node.loc.selector, negated_matcher)
|
129
|
+
range = node.loc.dot.with(end_pos: node.loc.expression.end_pos)
|
130
|
+
corrector.remove(range)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def negated_matcher
|
135
|
+
cop_config['NegatedMatcher']
|
136
|
+
end
|
137
|
+
|
138
|
+
def message_compound
|
139
|
+
format(MSG_COMPOUND, preferred: preferred_method)
|
140
|
+
end
|
141
|
+
|
142
|
+
def preferred_method
|
143
|
+
negated_matcher ? "`#{negated_matcher}`" : 'negated matchers'
|
144
|
+
end
|
90
145
|
end
|
91
146
|
end
|
92
147
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Enforces consistent use of `be_a` or `be_kind_of`.
|
7
|
+
#
|
8
|
+
# @example EnforcedStyle: be_a (default)
|
9
|
+
# # bad
|
10
|
+
# expect(object).to be_kind_of(String)
|
11
|
+
# expect(object).to be_a_kind_of(String)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# expect(object).to be_a(String)
|
15
|
+
# expect(object).to be_an(String)
|
16
|
+
#
|
17
|
+
# @example EnforcedStyle: be_kind_of
|
18
|
+
# # bad
|
19
|
+
# expect(object).to be_a(String)
|
20
|
+
# expect(object).to be_an(String)
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# expect(object).to be_kind_of(String)
|
24
|
+
# expect(object).to be_a_kind_of(String)
|
25
|
+
#
|
26
|
+
class ClassCheck < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
include ConfigurableEnforcedStyle
|
29
|
+
|
30
|
+
MSG = 'Prefer `%<preferred>s` over `%<current>s`.'
|
31
|
+
|
32
|
+
METHOD_NAMES_FOR_BE_A = ::Set[
|
33
|
+
:be_a,
|
34
|
+
:be_an
|
35
|
+
].freeze
|
36
|
+
|
37
|
+
METHOD_NAMES_FOR_KIND_OF = ::Set[
|
38
|
+
:be_a_kind_of,
|
39
|
+
:be_kind_of
|
40
|
+
].freeze
|
41
|
+
|
42
|
+
PREFERRED_METHOD_NAME_BY_STYLE = {
|
43
|
+
be_a: :be_a,
|
44
|
+
be_kind_of: :be_kind_of
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
RESTRICT_ON_SEND = %i[
|
48
|
+
be_a
|
49
|
+
be_a_kind_of
|
50
|
+
be_an
|
51
|
+
be_kind_of
|
52
|
+
].freeze
|
53
|
+
|
54
|
+
def on_send(node)
|
55
|
+
return unless offending?(node)
|
56
|
+
|
57
|
+
add_offense(
|
58
|
+
node.loc.selector,
|
59
|
+
message: format_message(node)
|
60
|
+
) do |corrector|
|
61
|
+
autocorrect(corrector, node)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def autocorrect(corrector, node)
|
68
|
+
corrector.replace(node.loc.selector, preferred_method_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def format_message(node)
|
72
|
+
format(
|
73
|
+
MSG,
|
74
|
+
current: node.method_name,
|
75
|
+
preferred: preferred_method_name
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def offending?(node)
|
80
|
+
!node.receiver && !preferred_method_name?(node.method_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
def preferred_method_name?(method_name)
|
84
|
+
preferred_method_names.include?(method_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
def preferred_method_name
|
88
|
+
PREFERRED_METHOD_NAME_BY_STYLE[style]
|
89
|
+
end
|
90
|
+
|
91
|
+
def preferred_method_names
|
92
|
+
if style == :be_a
|
93
|
+
METHOD_NAMES_FOR_BE_A
|
94
|
+
else
|
95
|
+
METHOD_NAMES_FOR_KIND_OF
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
# describe '.foo_bar' do
|
24
24
|
# # ...
|
25
25
|
# end
|
26
|
+
#
|
26
27
|
class ContextMethod < Base
|
27
28
|
extend AutoCorrector
|
28
29
|
|
@@ -33,7 +34,7 @@ module RuboCop
|
|
33
34
|
(block (send #rspec? :context $(str #method_name?) ...) ...)
|
34
35
|
PATTERN
|
35
36
|
|
36
|
-
def on_block(node)
|
37
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
37
38
|
context_method(node) do |context|
|
38
39
|
add_offense(context) do |corrector|
|
39
40
|
corrector.replace(node.send_node.loc.selector, 'describe')
|
@@ -14,7 +14,6 @@ module RuboCop
|
|
14
14
|
# @see http://www.betterspecs.org/#contexts
|
15
15
|
#
|
16
16
|
# @example `Prefixes` configuration
|
17
|
-
#
|
18
17
|
# # .rubocop.yml
|
19
18
|
# # RSpec/ContextWording:
|
20
19
|
# # Prefixes:
|
@@ -35,41 +34,73 @@ module RuboCop
|
|
35
34
|
# context 'when the display name is not present' do
|
36
35
|
# # ...
|
37
36
|
# end
|
37
|
+
#
|
38
|
+
# This cop can be customized allowed context description pattern
|
39
|
+
# with `AllowedPatterns`. By default, there are no checking by pattern.
|
40
|
+
#
|
41
|
+
# @example `AllowedPatterns` configuration
|
42
|
+
#
|
43
|
+
# # .rubocop.yml
|
44
|
+
# # RSpec/ContextWording:
|
45
|
+
# # AllowedPatterns:
|
46
|
+
# # - /とき$/
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# # bad
|
50
|
+
# context '条件を満たす' do
|
51
|
+
# # ...
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # good
|
55
|
+
# context '条件を満たすとき' do
|
56
|
+
# # ...
|
57
|
+
# end
|
58
|
+
#
|
38
59
|
class ContextWording < Base
|
39
|
-
|
60
|
+
include AllowedPattern
|
61
|
+
|
62
|
+
MSG = 'Context description should match %<patterns>s.'
|
40
63
|
|
41
64
|
# @!method context_wording(node)
|
42
65
|
def_node_matcher :context_wording, <<-PATTERN
|
43
|
-
(block (send #rspec? { :context :shared_context } $(str
|
66
|
+
(block (send #rspec? { :context :shared_context } $(str $_) ...) ...)
|
44
67
|
PATTERN
|
45
68
|
|
46
|
-
def on_block(node)
|
47
|
-
context_wording(node) do |context|
|
48
|
-
|
49
|
-
|
69
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
70
|
+
context_wording(node) do |context, description|
|
71
|
+
if bad_pattern?(description)
|
72
|
+
message = format(MSG, patterns: expect_patterns)
|
73
|
+
add_offense(context, message: message)
|
74
|
+
end
|
50
75
|
end
|
51
76
|
end
|
52
77
|
|
53
78
|
private
|
54
79
|
|
55
|
-
def
|
56
|
-
|
80
|
+
def allowed_patterns
|
81
|
+
super + prefix_regexes
|
57
82
|
end
|
58
83
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
84
|
+
def prefix_regexes
|
85
|
+
@prefix_regexes ||= prefixes.map { |pre| /^#{Regexp.escape(pre)}\b/ }
|
86
|
+
end
|
87
|
+
|
88
|
+
def bad_pattern?(description)
|
89
|
+
return false if allowed_patterns.empty?
|
62
90
|
|
63
|
-
|
64
|
-
quoted.join(', ')
|
91
|
+
!matches_allowed_pattern?(description)
|
65
92
|
end
|
66
93
|
|
67
|
-
def
|
68
|
-
|
94
|
+
def expect_patterns
|
95
|
+
inspected = allowed_patterns.map(&:inspect)
|
96
|
+
return inspected.first if inspected.size == 1
|
97
|
+
|
98
|
+
inspected << "or #{inspected.pop}"
|
99
|
+
inspected.join(', ')
|
69
100
|
end
|
70
101
|
|
71
|
-
def
|
72
|
-
|
102
|
+
def prefixes
|
103
|
+
Array(cop_config.fetch('Prefixes', []))
|
73
104
|
end
|
74
105
|
end
|
75
106
|
end
|
@@ -10,7 +10,6 @@ module RuboCop
|
|
10
10
|
# Ignores Rails and Aruba `type` metadata by default.
|
11
11
|
#
|
12
12
|
# @example `IgnoredMetadata` configuration
|
13
|
-
#
|
14
13
|
# # .rubocop.yml
|
15
14
|
# # RSpec/DescribeClass:
|
16
15
|
# # IgnoredMetadata:
|
@@ -34,6 +33,7 @@ module RuboCop
|
|
34
33
|
#
|
35
34
|
# describe "A feature example", type: :feature do
|
36
35
|
# end
|
36
|
+
#
|
37
37
|
class DescribeClass < Base
|
38
38
|
include TopLevelGroup
|
39
39
|
|
@@ -57,6 +57,7 @@ module RuboCop
|
|
57
57
|
class DescribedClass < Base
|
58
58
|
extend AutoCorrector
|
59
59
|
include ConfigurableEnforcedStyle
|
60
|
+
include Namespace
|
60
61
|
|
61
62
|
DESCRIBED_CLASS = 'described_class'
|
62
63
|
MSG = 'Use `%<replacement>s` instead of `%<src>s`.'
|
@@ -81,7 +82,7 @@ module RuboCop
|
|
81
82
|
def_node_search :contains_described_class?,
|
82
83
|
'(send nil? :described_class)'
|
83
84
|
|
84
|
-
def on_block(node)
|
85
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
85
86
|
# In case the explicit style is used, we need to remember what's
|
86
87
|
# being described.
|
87
88
|
@described_class, body = described_constant(node)
|
@@ -160,7 +161,8 @@ module RuboCop
|
|
160
161
|
end
|
161
162
|
|
162
163
|
def full_const_name(node)
|
163
|
-
|
164
|
+
symbolized_namespace = namespace(node).map(&:to_sym)
|
165
|
+
collapse_namespace(symbolized_namespace, const_name(node))
|
164
166
|
end
|
165
167
|
|
166
168
|
# @param namespace [Array<Symbol>]
|
@@ -200,18 +202,6 @@ module RuboCop
|
|
200
202
|
[nil, name]
|
201
203
|
end
|
202
204
|
end
|
203
|
-
|
204
|
-
# @param node [RuboCop::AST::Node]
|
205
|
-
# @return [Array<Symbol>]
|
206
|
-
# @example
|
207
|
-
# namespace(node) # => [:A, :B, :C]
|
208
|
-
def namespace(node)
|
209
|
-
node
|
210
|
-
.each_ancestor(:class, :module)
|
211
|
-
.reverse_each
|
212
|
-
.flat_map { |ancestor| ancestor.defined_module_name.split('::') }
|
213
|
-
.map(&:to_sym)
|
214
|
-
end
|
215
205
|
end
|
216
206
|
end
|
217
207
|
end
|
@@ -6,7 +6,6 @@ module RuboCop
|
|
6
6
|
# Checks if an example group does not include any tests.
|
7
7
|
#
|
8
8
|
# @example usage
|
9
|
-
#
|
10
9
|
# # bad
|
11
10
|
# describe Bacon do
|
12
11
|
# let(:bacon) { Bacon.new(chunkiness) }
|
@@ -35,7 +34,12 @@ module RuboCop
|
|
35
34
|
# describe Bacon do
|
36
35
|
# pending 'will add tests later'
|
37
36
|
# end
|
37
|
+
#
|
38
38
|
class EmptyExampleGroup < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
|
41
|
+
include RangeHelp
|
42
|
+
|
39
43
|
MSG = 'Empty example group detected.'
|
40
44
|
|
41
45
|
# @!method example_group_body(node)
|
@@ -119,7 +123,7 @@ module RuboCop
|
|
119
123
|
# describe { it { i_run_as_well } }
|
120
124
|
#
|
121
125
|
# @example source that does not match
|
122
|
-
# before { it { whatever here
|
126
|
+
# before { it { whatever here won't run anyway } }
|
123
127
|
#
|
124
128
|
# @param node [RuboCop::AST::Node]
|
125
129
|
# @return [Array<RuboCop::AST::Node>] matching nodes
|
@@ -130,12 +134,16 @@ module RuboCop
|
|
130
134
|
}
|
131
135
|
PATTERN
|
132
136
|
|
133
|
-
def on_block(node)
|
137
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
134
138
|
return if node.each_ancestor(:def, :defs).any?
|
135
139
|
return if node.each_ancestor(:block).any? { |block| example?(block) }
|
136
140
|
|
137
141
|
example_group_body(node) do |body|
|
138
|
-
|
142
|
+
next unless offensive?(body)
|
143
|
+
|
144
|
+
add_offense(node.send_node) do |corrector|
|
145
|
+
corrector.remove(removed_range(node))
|
146
|
+
end
|
139
147
|
end
|
140
148
|
end
|
141
149
|
|
@@ -163,6 +171,13 @@ module RuboCop
|
|
163
171
|
def examples_in_branches?(condition_node)
|
164
172
|
condition_node.branches.any? { |branch| examples?(branch) }
|
165
173
|
end
|
174
|
+
|
175
|
+
def removed_range(node)
|
176
|
+
range_by_whole_lines(
|
177
|
+
node.location.expression,
|
178
|
+
include_final_newline: true
|
179
|
+
)
|
180
|
+
end
|
166
181
|
end
|
167
182
|
end
|
168
183
|
end
|
@@ -22,6 +22,7 @@ module RuboCop
|
|
22
22
|
# create_feed
|
23
23
|
# end
|
24
24
|
# after(:all) { cleanup_feed }
|
25
|
+
#
|
25
26
|
class EmptyHook < Base
|
26
27
|
extend AutoCorrector
|
27
28
|
include RuboCop::Cop::RangeHelp
|
@@ -33,7 +34,7 @@ module RuboCop
|
|
33
34
|
(block $#{send_pattern('#Hooks.all')} _ nil?)
|
34
35
|
PATTERN
|
35
36
|
|
36
|
-
def on_block(node)
|
37
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
37
38
|
empty_hook?(node) do |hook|
|
38
39
|
add_offense(hook) do |corrector|
|
39
40
|
corrector.remove(
|
@@ -30,7 +30,6 @@ module RuboCop
|
|
30
30
|
# end
|
31
31
|
#
|
32
32
|
# @example with AllowConsecutiveOneLiners configuration
|
33
|
-
#
|
34
33
|
# # rubocop.yml
|
35
34
|
# # RSpec/EmptyLineAfterExample:
|
36
35
|
# # AllowConsecutiveOneLiners: false
|
@@ -47,7 +46,7 @@ module RuboCop
|
|
47
46
|
|
48
47
|
MSG = 'Add an empty line after `%<example>s`.'
|
49
48
|
|
50
|
-
def on_block(node)
|
49
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
51
50
|
return unless example?(node)
|
52
51
|
return if allowed_one_liner?(node)
|
53
52
|
|
@@ -65,19 +64,15 @@ module RuboCop
|
|
65
64
|
end
|
66
65
|
|
67
66
|
def consecutive_one_liner?(node)
|
68
|
-
node.
|
67
|
+
node.single_line? && next_one_line_example?(node)
|
69
68
|
end
|
70
69
|
|
71
70
|
def next_one_line_example?(node)
|
72
|
-
next_sibling =
|
71
|
+
next_sibling = node.right_sibling
|
73
72
|
return unless next_sibling
|
74
73
|
return unless example?(next_sibling)
|
75
74
|
|
76
|
-
next_sibling.
|
77
|
-
end
|
78
|
-
|
79
|
-
def next_sibling(node)
|
80
|
-
node.parent.children[node.sibling_index + 1]
|
75
|
+
next_sibling.single_line?
|
81
76
|
end
|
82
77
|
end
|
83
78
|
end
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
29
29
|
|
30
30
|
MSG = 'Add an empty line after `%<example_group>s`.'
|
31
31
|
|
32
|
-
def on_block(node)
|
32
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
33
33
|
return unless example_group?(node)
|
34
34
|
|
35
35
|
missing_separating_line_offense(node) do |method|
|