rubocop-rspec 1.7.0 → 3.0.2
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/CHANGELOG.md +955 -79
- data/CODE_OF_CONDUCT.md +17 -0
- data/MIT-LICENSE.md +1 -2
- data/README.md +35 -35
- data/config/default.yml +940 -52
- data/config/obsoletion.yml +30 -0
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +49 -0
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +49 -0
- data/lib/rubocop/cop/rspec/any_instance.rb +10 -13
- data/lib/rubocop/cop/rspec/around_block.rb +97 -0
- data/lib/rubocop/cop/rspec/base.rb +26 -0
- data/lib/rubocop/cop/rspec/be.rb +39 -0
- data/lib/rubocop/cop/rspec/be_empty.rb +45 -0
- data/lib/rubocop/cop/rspec/be_eq.rb +47 -0
- data/lib/rubocop/cop/rspec/be_eql.rb +18 -15
- data/lib/rubocop/cop/rspec/be_nil.rb +74 -0
- data/lib/rubocop/cop/rspec/before_after_all.rb +45 -0
- data/lib/rubocop/cop/rspec/change_by_zero.rb +184 -0
- data/lib/rubocop/cop/rspec/class_check.rb +101 -0
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_method.rb +57 -0
- data/lib/rubocop/cop/rspec/context_wording.rb +117 -0
- data/lib/rubocop/cop/rspec/describe_class.rb +52 -21
- data/lib/rubocop/cop/rspec/describe_method.rb +26 -11
- data/lib/rubocop/cop/rspec/describe_symbol.rb +37 -0
- data/lib/rubocop/cop/rspec/described_class.rb +181 -34
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +38 -0
- data/lib/rubocop/cop/rspec/dialect.rb +84 -0
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +58 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +134 -47
- data/lib/rubocop/cop/rspec/empty_hook.rb +49 -0
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +82 -0
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +42 -0
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +40 -0
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +82 -0
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +36 -0
- data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
- data/lib/rubocop/cop/rspec/empty_output.rb +47 -0
- data/lib/rubocop/cop/rspec/eq.rb +47 -0
- data/lib/rubocop/cop/rspec/example_length.rb +38 -20
- data/lib/rubocop/cop/rspec/example_without_description.rb +98 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +117 -27
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +110 -0
- data/lib/rubocop/cop/rspec/expect_actual.rb +46 -20
- data/lib/rubocop/cop/rspec/expect_change.rb +86 -0
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +50 -0
- data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
- data/lib/rubocop/cop/rspec/expect_output.rb +50 -0
- data/lib/rubocop/cop/rspec/focus.rb +79 -25
- data/lib/rubocop/cop/rspec/hook_argument.rb +48 -36
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +81 -0
- data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +37 -0
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +68 -0
- data/lib/rubocop/cop/rspec/implicit_expect.rb +100 -0
- data/lib/rubocop/cop/rspec/implicit_subject.rb +167 -0
- data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
- data/lib/rubocop/cop/rspec/instance_spy.rb +74 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +28 -14
- data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +49 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +74 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +57 -29
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +127 -0
- data/lib/rubocop/cop/rspec/let_before_examples.rb +101 -0
- data/lib/rubocop/cop/rspec/let_setup.rb +32 -16
- data/lib/rubocop/cop/rspec/match_array.rb +59 -0
- data/lib/rubocop/cop/rspec/message_chain.rb +10 -15
- data/lib/rubocop/cop/rspec/message_expectation.rb +12 -9
- data/lib/rubocop/cop/rspec/message_spies.rb +88 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
- data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
- data/lib/rubocop/cop/rspec/mixin/comments_help.rb +38 -0
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +59 -0
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
- data/lib/rubocop/cop/rspec/mixin/inside_example_group.rb +29 -0
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +63 -0
- data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +39 -0
- data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
- data/lib/rubocop/cop/rspec/mixin/variable.rb +21 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +14 -12
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +86 -26
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +146 -0
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +97 -0
- data/lib/rubocop/cop/rspec/named_subject.rb +107 -27
- data/lib/rubocop/cop/rspec/nested_groups.rb +84 -47
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +102 -0
- data/lib/rubocop/cop/rspec/not_to_not.rb +30 -27
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +74 -0
- data/lib/rubocop/cop/rspec/pending.rb +80 -0
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +159 -0
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +341 -0
- data/lib/rubocop/cop/rspec/receive_counts.rb +89 -0
- data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
- data/lib/rubocop/cop/rspec/receive_never.rb +41 -0
- data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
- data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
- data/lib/rubocop/cop/rspec/remove_const.rb +39 -0
- data/lib/rubocop/cop/rspec/repeated_description.rb +98 -0
- data/lib/rubocop/cop/rspec/repeated_example.rb +53 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +100 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +96 -0
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +105 -0
- data/lib/rubocop/cop/rspec/repeated_subject_call.rb +125 -0
- data/lib/rubocop/cop/rspec/return_from_stub.rb +169 -0
- data/lib/rubocop/cop/rspec/scattered_let.rb +59 -0
- data/lib/rubocop/cop/rspec/scattered_setup.rb +92 -0
- data/lib/rubocop/cop/rspec/shared_context.rb +107 -0
- data/lib/rubocop/cop/rspec/shared_examples.rb +125 -0
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +93 -0
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
- data/lib/rubocop/cop/rspec/sort_metadata.rb +71 -0
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
- data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +176 -0
- data/lib/rubocop/cop/rspec/subject_declaration.rb +46 -0
- data/lib/rubocop/cop/rspec/subject_stub.rb +93 -74
- data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +69 -0
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +67 -0
- data/lib/rubocop/cop/rspec/variable_definition.rb +77 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +68 -0
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +111 -0
- data/lib/rubocop/cop/rspec/verified_doubles.rb +28 -14
- data/lib/rubocop/cop/rspec/void_expect.rb +60 -0
- data/lib/rubocop/cop/rspec/yield.rb +82 -0
- data/lib/rubocop/cop/rspec_cops.rb +112 -0
- data/lib/rubocop/rspec/align_let_brace.rb +63 -0
- data/lib/rubocop/rspec/concept.rb +33 -0
- data/lib/rubocop/rspec/config_formatter.rb +27 -4
- data/lib/rubocop/rspec/cop/generator.rb +25 -0
- data/lib/rubocop/rspec/corrector/move_node.rb +51 -0
- data/lib/rubocop/rspec/description_extractor.rb +60 -18
- data/lib/rubocop/rspec/example.rb +37 -0
- data/lib/rubocop/rspec/example_group.rb +67 -0
- data/lib/rubocop/rspec/hook.rb +79 -0
- data/lib/rubocop/rspec/inject.rb +3 -1
- data/lib/rubocop/rspec/language.rb +184 -41
- data/lib/rubocop/rspec/node.rb +19 -0
- data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +29 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop/rspec/wording.rb +61 -19
- data/lib/rubocop/rspec.rb +6 -2
- data/lib/rubocop-rspec.rb +45 -34
- metadata +130 -195
- data/Gemfile +0 -13
- data/Rakefile +0 -48
- data/lib/rubocop/cop/rspec/file_path.rb +0 -83
- data/lib/rubocop/rspec/language/node_pattern.rb +0 -16
- data/lib/rubocop/rspec/spec_only.rb +0 -61
- data/lib/rubocop/rspec/top_level_describe.rb +0 -61
- data/lib/rubocop/rspec/util.rb +0 -19
- data/rubocop-rspec.gemspec +0 -42
- data/spec/expect_violation/expectation_spec.rb +0 -85
- data/spec/project/changelog_spec.rb +0 -81
- data/spec/project/default_config_spec.rb +0 -52
- data/spec/project/project_requires_spec.rb +0 -8
- data/spec/rubocop/cop/rspec/any_instance_spec.rb +0 -30
- data/spec/rubocop/cop/rspec/be_eql_spec.rb +0 -59
- data/spec/rubocop/cop/rspec/describe_class_spec.rb +0 -113
- data/spec/rubocop/cop/rspec/describe_method_spec.rb +0 -32
- data/spec/rubocop/cop/rspec/described_class_spec.rb +0 -219
- data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +0 -79
- data/spec/rubocop/cop/rspec/example_length_spec.rb +0 -117
- data/spec/rubocop/cop/rspec/example_wording_spec.rb +0 -82
- data/spec/rubocop/cop/rspec/expect_actual_spec.rb +0 -136
- data/spec/rubocop/cop/rspec/file_path_spec.rb +0 -236
- data/spec/rubocop/cop/rspec/focus_spec.rb +0 -130
- data/spec/rubocop/cop/rspec/hook_argument_spec.rb +0 -189
- data/spec/rubocop/cop/rspec/instance_variable_spec.rb +0 -75
- data/spec/rubocop/cop/rspec/leading_subject_spec.rb +0 -54
- data/spec/rubocop/cop/rspec/let_setup_spec.rb +0 -66
- data/spec/rubocop/cop/rspec/message_chain_spec.rb +0 -21
- data/spec/rubocop/cop/rspec/message_expectation_spec.rb +0 -63
- data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +0 -28
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +0 -84
- data/spec/rubocop/cop/rspec/named_subject_spec.rb +0 -62
- data/spec/rubocop/cop/rspec/nested_groups_spec.rb +0 -55
- data/spec/rubocop/cop/rspec/not_to_not_spec.rb +0 -57
- data/spec/rubocop/cop/rspec/subject_stub_spec.rb +0 -183
- data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +0 -71
- data/spec/rubocop/rspec/config_formatter_spec.rb +0 -48
- data/spec/rubocop/rspec/description_extractor_spec.rb +0 -35
- data/spec/rubocop/rspec/language/selector_set_spec.rb +0 -29
- data/spec/rubocop/rspec/spec_only_spec.rb +0 -97
- data/spec/rubocop/rspec/util/one_spec.rb +0 -21
- data/spec/rubocop/rspec/wording_spec.rb +0 -44
- data/spec/shared/rspec_only_cop_behavior.rb +0 -68
- data/spec/spec_helper.rb +0 -41
- data/spec/support/expect_violation.rb +0 -166
@@ -3,55 +3,83 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
|
-
#
|
6
|
+
# Enforce that subject is the first definition in the test.
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# # bad
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# subject { described_class.new(params) }
|
10
|
+
# let(:params) { blah }
|
11
|
+
# subject { described_class.new(params) }
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
13
|
+
# before { do_something }
|
14
|
+
# subject { described_class.new(params) }
|
15
|
+
#
|
16
|
+
# it { expect_something }
|
17
|
+
# subject { described_class.new(params) }
|
18
|
+
# it { expect_something_else }
|
19
|
+
#
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# subject { described_class.new(params) }
|
23
|
+
# let(:params) { blah }
|
18
24
|
#
|
19
25
|
# # good
|
20
|
-
#
|
21
|
-
#
|
26
|
+
# subject { described_class.new(params) }
|
27
|
+
# before { do_something }
|
22
28
|
#
|
23
|
-
#
|
29
|
+
# # good
|
30
|
+
# subject { described_class.new(params) }
|
31
|
+
# it { expect_something }
|
32
|
+
# it { expect_something_else }
|
24
33
|
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# end
|
29
|
-
class LeadingSubject < Cop
|
30
|
-
include RuboCop::RSpec::SpecOnly, RuboCop::RSpec::Language
|
34
|
+
class LeadingSubject < Base
|
35
|
+
extend AutoCorrector
|
36
|
+
include InsideExampleGroup
|
31
37
|
|
32
|
-
MSG = 'Declare `subject` above any other `
|
38
|
+
MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
|
33
39
|
|
34
|
-
|
40
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
41
|
+
return unless subject?(node)
|
42
|
+
return unless inside_example_group?(node)
|
35
43
|
|
36
|
-
|
37
|
-
|
44
|
+
check_previous_nodes(node)
|
45
|
+
end
|
38
46
|
|
39
|
-
|
40
|
-
break if sibling.equal?(node)
|
47
|
+
private
|
41
48
|
|
42
|
-
|
43
|
-
|
49
|
+
def check_previous_nodes(node)
|
50
|
+
offending_node(node) do |offender|
|
51
|
+
msg = format(MSG, offending: offender.method_name)
|
52
|
+
add_offense(node, message: msg) do |corrector|
|
53
|
+
autocorrect(corrector, node, offender)
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
|
48
|
-
|
58
|
+
def offending_node(node)
|
59
|
+
parent(node).each_child_node.find do |sibling|
|
60
|
+
break if sibling.equal?(node)
|
49
61
|
|
50
|
-
|
51
|
-
node.each_ancestor(:block).any? do |ancestor|
|
52
|
-
Examples::ALL.include?(ancestor.method_name)
|
62
|
+
yield sibling if offending?(sibling)
|
53
63
|
end
|
54
64
|
end
|
65
|
+
|
66
|
+
def parent(node)
|
67
|
+
node.each_ancestor(:block).first.body
|
68
|
+
end
|
69
|
+
|
70
|
+
def autocorrect(corrector, node, sibling)
|
71
|
+
RuboCop::RSpec::Corrector::MoveNode.new(
|
72
|
+
node, corrector, processed_source
|
73
|
+
).move_before(sibling)
|
74
|
+
end
|
75
|
+
|
76
|
+
def offending?(node)
|
77
|
+
let?(node) ||
|
78
|
+
hook?(node) ||
|
79
|
+
example?(node) ||
|
80
|
+
spec_group?(node) ||
|
81
|
+
include?(node)
|
82
|
+
end
|
55
83
|
end
|
56
84
|
end
|
57
85
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that no class, module, or constant is declared.
|
7
|
+
#
|
8
|
+
# Constants, including classes and modules, when declared in a block
|
9
|
+
# scope, are defined in global namespace, and leak between examples.
|
10
|
+
#
|
11
|
+
# If several examples may define a `DummyClass`, instead of being a
|
12
|
+
# blank slate class as it will be in the first example, subsequent
|
13
|
+
# examples will be reopening it and modifying its behavior in
|
14
|
+
# unpredictable ways.
|
15
|
+
# Even worse when a class that exists in the codebase is reopened.
|
16
|
+
#
|
17
|
+
# Anonymous classes are fine, since they don't result in global
|
18
|
+
# namespace name clashes.
|
19
|
+
#
|
20
|
+
# @see https://rspec.info/features/3-12/rspec-mocks/mutating-constants
|
21
|
+
#
|
22
|
+
# @example Constants leak between examples
|
23
|
+
# # bad
|
24
|
+
# describe SomeClass do
|
25
|
+
# OtherClass = Struct.new
|
26
|
+
# CONSTANT_HERE = 'I leak into global namespace'
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# describe SomeClass do
|
31
|
+
# before do
|
32
|
+
# stub_const('OtherClass', Struct.new)
|
33
|
+
# stub_const('CONSTANT_HERE', 'I only exist during this example')
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# # bad
|
39
|
+
# describe SomeClass do
|
40
|
+
# class FooClass < described_class
|
41
|
+
# def double_that
|
42
|
+
# some_base_method * 2
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# it { expect(FooClass.new.double_that).to eq(4) }
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # good - anonymous class, no constant needs to be defined
|
50
|
+
# describe SomeClass do
|
51
|
+
# let(:foo_class) do
|
52
|
+
# Class.new(described_class) do
|
53
|
+
# def double_that
|
54
|
+
# some_base_method * 2
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# it { expect(foo_class.new.double_that).to eq(4) }
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# # good - constant is stubbed
|
63
|
+
# describe SomeClass do
|
64
|
+
# before do
|
65
|
+
# foo_class = Class.new(described_class) do
|
66
|
+
# def do_something
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
# stub_const('FooClass', foo_class)
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# it { expect(FooClass.new.double_that).to eq(4) }
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# # bad
|
77
|
+
# describe SomeClass do
|
78
|
+
# module SomeModule
|
79
|
+
# class SomeClass
|
80
|
+
# def do_something
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# # good
|
87
|
+
# describe SomeClass do
|
88
|
+
# before do
|
89
|
+
# foo_class = Class.new(described_class) do
|
90
|
+
# def do_something
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# stub_const('SomeModule::SomeClass', foo_class)
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
class LeakyConstantDeclaration < Base
|
97
|
+
MSG_CONST = 'Stub constant instead of declaring explicitly.'
|
98
|
+
MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
|
99
|
+
MSG_MODULE = 'Stub module constant instead of declaring explicitly.'
|
100
|
+
|
101
|
+
def on_casgn(node)
|
102
|
+
return unless inside_describe_block?(node)
|
103
|
+
|
104
|
+
add_offense(node, message: MSG_CONST)
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_class(node)
|
108
|
+
return unless inside_describe_block?(node)
|
109
|
+
|
110
|
+
add_offense(node, message: MSG_CLASS)
|
111
|
+
end
|
112
|
+
|
113
|
+
def on_module(node)
|
114
|
+
return unless inside_describe_block?(node)
|
115
|
+
|
116
|
+
add_offense(node, message: MSG_MODULE)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def inside_describe_block?(node)
|
122
|
+
node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for `let` definitions that come after an example.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# let(:foo) { bar }
|
11
|
+
#
|
12
|
+
# it 'checks what foo does' do
|
13
|
+
# expect(foo).to be
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# let(:some) { other }
|
17
|
+
#
|
18
|
+
# it 'checks what some does' do
|
19
|
+
# expect(some).to be
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# let(:foo) { bar }
|
24
|
+
# let(:some) { other }
|
25
|
+
#
|
26
|
+
# it 'checks what foo does' do
|
27
|
+
# expect(foo).to be
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# it 'checks what some does' do
|
31
|
+
# expect(some).to be
|
32
|
+
# end
|
33
|
+
class LetBeforeExamples < Base
|
34
|
+
extend AutoCorrector
|
35
|
+
|
36
|
+
MSG = 'Move `let` before the examples in the group.'
|
37
|
+
|
38
|
+
# @!method example_or_group?(node)
|
39
|
+
def_node_matcher :example_or_group?, <<~PATTERN
|
40
|
+
{
|
41
|
+
(block (send nil? {#ExampleGroups.all #Examples.all} ...) ...)
|
42
|
+
(send nil? #Includes.examples ...)
|
43
|
+
}
|
44
|
+
PATTERN
|
45
|
+
|
46
|
+
# @!method include_examples?(node)
|
47
|
+
def_node_matcher :include_examples?, <<~PATTERN
|
48
|
+
{
|
49
|
+
(block (send nil? :include_examples ...) ...)
|
50
|
+
(send nil? :include_examples ...)
|
51
|
+
}
|
52
|
+
PATTERN
|
53
|
+
|
54
|
+
def self.autocorrect_incompatible_with
|
55
|
+
[RSpec::ScatteredLet]
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
59
|
+
return unless example_group_with_body?(node)
|
60
|
+
|
61
|
+
check_let_declarations(node.body) if multiline_block?(node.body)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def example_group_with_include_examples?(body)
|
67
|
+
body.children.any? { |sibling| include_examples?(sibling) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def multiline_block?(block)
|
71
|
+
block.begin_type?
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_let_declarations(node)
|
75
|
+
first_example = find_first_example(node)
|
76
|
+
return unless first_example
|
77
|
+
|
78
|
+
correct = !example_group_with_include_examples?(node)
|
79
|
+
|
80
|
+
first_example.right_siblings.each do |sibling|
|
81
|
+
next unless let?(sibling)
|
82
|
+
|
83
|
+
add_offense(sibling) do |corrector|
|
84
|
+
autocorrect(corrector, sibling, first_example) if correct
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_first_example(node)
|
90
|
+
node.children.find { |sibling| example_or_group?(sibling) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def autocorrect(corrector, node, first_example)
|
94
|
+
RuboCop::RSpec::Corrector::MoveNode.new(
|
95
|
+
node, corrector, processed_source
|
96
|
+
).move_before(first_example)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -6,50 +6,66 @@ module RuboCop
|
|
6
6
|
# Checks unreferenced `let!` calls being used for test setup.
|
7
7
|
#
|
8
8
|
# @example
|
9
|
-
# #
|
9
|
+
# # bad
|
10
10
|
# let!(:my_widget) { create(:widget) }
|
11
11
|
#
|
12
12
|
# it 'counts widgets' do
|
13
13
|
# expect(Widget.count).to eq(1)
|
14
14
|
# end
|
15
15
|
#
|
16
|
-
# #
|
16
|
+
# # good
|
17
17
|
# it 'counts widgets' do
|
18
18
|
# create(:widget)
|
19
19
|
# expect(Widget.count).to eq(1)
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# #
|
22
|
+
# # good
|
23
23
|
# before { create(:widget) }
|
24
24
|
#
|
25
25
|
# it 'counts widgets' do
|
26
26
|
# expect(Widget.count).to eq(1)
|
27
27
|
# end
|
28
|
-
class LetSetup <
|
29
|
-
|
30
|
-
RuboCop::RSpec::TopLevelDescribe,
|
31
|
-
RuboCop::RSpec::Language,
|
32
|
-
RuboCop::RSpec::Language::NodePattern
|
28
|
+
class LetSetup < Base
|
29
|
+
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
|
33
30
|
|
34
|
-
|
31
|
+
# @!method example_or_shared_group_or_including?(node)
|
32
|
+
def_node_matcher :example_or_shared_group_or_including?, <<~PATTERN
|
33
|
+
(block {
|
34
|
+
(send #rspec? {#SharedGroups.all #ExampleGroups.all} ...)
|
35
|
+
(send nil? #Includes.all ...)
|
36
|
+
} ...)
|
37
|
+
PATTERN
|
35
38
|
|
36
|
-
|
39
|
+
# @!method let_bang(node)
|
40
|
+
def_node_matcher :let_bang, <<~PATTERN
|
41
|
+
{
|
42
|
+
(block $(send nil? :let! {(sym $_) (str $_)}) ...)
|
43
|
+
$(send nil? :let! {(sym $_) (str $_)} block_pass)
|
44
|
+
}
|
45
|
+
PATTERN
|
37
46
|
|
38
|
-
|
47
|
+
# @!method method_called?(node)
|
48
|
+
def_node_search :method_called?, '(send nil? %)'
|
39
49
|
|
40
|
-
def on_block(node)
|
41
|
-
return unless
|
50
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
51
|
+
return unless example_or_shared_group_or_including?(node)
|
42
52
|
|
43
53
|
unused_let_bang(node) do |let|
|
44
|
-
add_offense(let
|
54
|
+
add_offense(let)
|
45
55
|
end
|
46
56
|
end
|
47
57
|
|
48
58
|
private
|
49
59
|
|
50
60
|
def unused_let_bang(node)
|
51
|
-
|
52
|
-
yield(method_send) unless method_called?(node, method_name)
|
61
|
+
child_let_bang(node) do |method_send, method_name|
|
62
|
+
yield(method_send) unless method_called?(node, method_name.to_sym)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def child_let_bang(node, &block)
|
67
|
+
RuboCop::RSpec::ExampleGroup.new(node).lets.each do |let|
|
68
|
+
let_bang(let, &block)
|
53
69
|
end
|
54
70
|
end
|
55
71
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks where `match_array` is used.
|
7
|
+
#
|
8
|
+
# This cop checks for the following:
|
9
|
+
# - Prefer `contain_exactly` when matching an array with values.
|
10
|
+
# - Prefer `eq` when using `match_array` with an empty array literal.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# it { is_expected.to match_array([content1, content2]) }
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# it { is_expected.to contain_exactly(content1, content2) }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# it { is_expected.to match_array([content] + array) }
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
|
24
|
+
#
|
25
|
+
class MatchArray < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
MSG = 'Prefer `contain_exactly` when matching an array literal.'
|
29
|
+
RESTRICT_ON_SEND = %i[match_array].freeze
|
30
|
+
|
31
|
+
# @!method match_array_with_empty_array?(node)
|
32
|
+
def_node_matcher :match_array_with_empty_array?, <<~PATTERN
|
33
|
+
(send nil? :match_array (array))
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_send(node)
|
37
|
+
return unless node.first_argument&.array_type?
|
38
|
+
return if match_array_with_empty_array?(node)
|
39
|
+
|
40
|
+
check_populated_array(node)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def check_populated_array(node)
|
46
|
+
return if node.first_argument.percent_literal?
|
47
|
+
|
48
|
+
add_offense(node) do |corrector|
|
49
|
+
array_contents = node.arguments.flat_map(&:to_a)
|
50
|
+
corrector.replace(
|
51
|
+
node,
|
52
|
+
"contain_exactly(#{array_contents.map(&:source).join(', ')})"
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RuboCop
|
2
4
|
module Cop
|
3
5
|
module RSpec
|
@@ -7,25 +9,18 @@ module RuboCop
|
|
7
9
|
# # bad
|
8
10
|
# allow(foo).to receive_message_chain(:bar, :baz).and_return(42)
|
9
11
|
#
|
10
|
-
# #
|
12
|
+
# # good
|
11
13
|
# thing = Thing.new(baz: 42)
|
12
|
-
# allow(foo).to receive(bar
|
14
|
+
# allow(foo).to receive(:bar).and_return(thing)
|
13
15
|
#
|
14
|
-
class MessageChain <
|
15
|
-
|
16
|
-
|
17
|
-
MESSAGE = 'Avoid stubbing using `%<method>s`'.freeze
|
18
|
-
|
19
|
-
MESSAGE_CHAIN_METHODS = [
|
20
|
-
:receive_message_chain,
|
21
|
-
:stub_chain
|
22
|
-
].freeze
|
16
|
+
class MessageChain < Base
|
17
|
+
MSG = 'Avoid stubbing using `%<method>s`.'
|
18
|
+
RESTRICT_ON_SEND = %i[receive_message_chain stub_chain].freeze
|
23
19
|
|
24
20
|
def on_send(node)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
add_offense(node, :selector, MESSAGE % { method: method_name })
|
21
|
+
add_offense(
|
22
|
+
node.loc.selector, message: format(MSG, method: node.method_name)
|
23
|
+
)
|
29
24
|
end
|
30
25
|
end
|
31
26
|
end
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
8
8
|
# This cop can be configured in your configuration using the
|
9
9
|
# `EnforcedStyle` option and supports `--auto-gen-config`.
|
10
10
|
#
|
11
|
-
# @example `EnforcedStyle: allow`
|
11
|
+
# @example `EnforcedStyle: allow` (default)
|
12
12
|
#
|
13
13
|
# # bad
|
14
14
|
# expect(foo).to receive(:bar)
|
@@ -24,24 +24,27 @@ module RuboCop
|
|
24
24
|
# # good
|
25
25
|
# expect(foo).to receive(:bar)
|
26
26
|
#
|
27
|
-
class MessageExpectation <
|
28
|
-
include
|
27
|
+
class MessageExpectation < Base
|
28
|
+
include ConfigurableEnforcedStyle
|
29
29
|
|
30
|
-
MSG = 'Prefer
|
30
|
+
MSG = 'Prefer `%<style>s` for setting message expectations.'
|
31
31
|
|
32
|
-
|
32
|
+
RESTRICT_ON_SEND = %i[to].freeze
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
# @!method message_expectation(node)
|
35
|
+
def_node_matcher :message_expectation, <<~PATTERN
|
36
|
+
(send $(send nil? {:expect :allow} ...) :to #receive_message?)
|
36
37
|
PATTERN
|
37
38
|
|
38
|
-
|
39
|
+
# @!method receive_message?(node)
|
40
|
+
def_node_search :receive_message?, '(send nil? :receive ...)'
|
39
41
|
|
40
42
|
def on_send(node)
|
41
43
|
message_expectation(node) do |match|
|
42
44
|
return correct_style_detected if preferred_style?(match)
|
43
45
|
|
44
|
-
|
46
|
+
message = format(MSG, style: style)
|
47
|
+
add_offense(match.loc.selector, message: message) do
|
45
48
|
opposite_style_detected
|
46
49
|
end
|
47
50
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that message expectations are set using spies.
|
7
|
+
#
|
8
|
+
# This cop can be configured in your configuration using the
|
9
|
+
# `EnforcedStyle` option and supports `--auto-gen-config`.
|
10
|
+
#
|
11
|
+
# @example `EnforcedStyle: have_received` (default)
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# expect(foo).to receive(:bar)
|
15
|
+
# do_something
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# allow(foo).to receive(:bar) # or use instance_spy
|
19
|
+
# do_something
|
20
|
+
# expect(foo).to have_received(:bar)
|
21
|
+
#
|
22
|
+
# @example `EnforcedStyle: receive`
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# allow(foo).to receive(:bar)
|
26
|
+
# do_something
|
27
|
+
# expect(foo).to have_received(:bar)
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# expect(foo).to receive(:bar)
|
31
|
+
# do_something
|
32
|
+
#
|
33
|
+
class MessageSpies < Base
|
34
|
+
include ConfigurableEnforcedStyle
|
35
|
+
|
36
|
+
MSG_RECEIVE = 'Prefer `receive` for setting message expectations.'
|
37
|
+
|
38
|
+
MSG_HAVE_RECEIVED = 'Prefer `have_received` for setting message ' \
|
39
|
+
'expectations. Setup `%<source>s` as a spy using ' \
|
40
|
+
'`allow` or `instance_spy`.'
|
41
|
+
|
42
|
+
RESTRICT_ON_SEND = Runners.all
|
43
|
+
|
44
|
+
# @!method message_expectation(node)
|
45
|
+
def_node_matcher :message_expectation, %(
|
46
|
+
(send (send nil? :expect $_) #Runners.all ...)
|
47
|
+
)
|
48
|
+
|
49
|
+
# @!method receive_message(node)
|
50
|
+
def_node_search :receive_message, %(
|
51
|
+
$(send nil? {:receive :have_received} ...)
|
52
|
+
)
|
53
|
+
|
54
|
+
def on_send(node)
|
55
|
+
receive_message_matcher(node) do |receiver, message_matcher|
|
56
|
+
return correct_style_detected if preferred_style?(message_matcher)
|
57
|
+
|
58
|
+
add_offense(
|
59
|
+
message_matcher.loc.selector,
|
60
|
+
message: error_message(receiver)
|
61
|
+
) { opposite_style_detected }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def receive_message_matcher(node)
|
68
|
+
return unless (receiver = message_expectation(node))
|
69
|
+
|
70
|
+
receive_message(node) { |match| yield(receiver, match) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def preferred_style?(expectation)
|
74
|
+
expectation.method_name.equal?(style)
|
75
|
+
end
|
76
|
+
|
77
|
+
def error_message(receiver)
|
78
|
+
case style
|
79
|
+
when :receive
|
80
|
+
MSG_RECEIVE
|
81
|
+
when :have_received
|
82
|
+
format(MSG_HAVE_RECEIVED, source: receiver.source)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|