rubocop-rspec 1.21.0 → 1.22.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Rakefile +19 -0
  4. data/config/default.yml +19 -0
  5. data/lib/rubocop-rspec.rb +17 -72
  6. data/lib/rubocop/cop/rspec/context_wording.rb +2 -2
  7. data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
  8. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  9. data/lib/rubocop/cop/rspec/described_class.rb +2 -4
  10. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +6 -10
  11. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +4 -4
  12. data/lib/rubocop/cop/rspec/example_without_description.rb +89 -0
  13. data/lib/rubocop/cop/rspec/expect_change.rb +102 -0
  14. data/lib/rubocop/cop/rspec/hook_argument.rb +9 -9
  15. data/lib/rubocop/cop/rspec/instance_variable.rb +9 -0
  16. data/lib/rubocop/cop/rspec/leading_subject.rb +1 -8
  17. data/lib/rubocop/cop/rspec/let_before_examples.rb +46 -6
  18. data/lib/rubocop/cop/rspec/multiple_describes.rb +2 -2
  19. data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -4
  20. data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -2
  21. data/lib/rubocop/cop/rspec/nested_groups.rb +2 -2
  22. data/lib/rubocop/cop/rspec/overwriting_setup.rb +11 -13
  23. data/lib/rubocop/cop/rspec/return_from_stub.rb +85 -0
  24. data/lib/rubocop/cop/rspec/scattered_let.rb +9 -13
  25. data/lib/rubocop/cop/rspec/shared_context.rb +2 -2
  26. data/lib/rubocop/cop/rspec_cops.rb +59 -0
  27. data/lib/rubocop/rspec/description_extractor.rb +13 -1
  28. data/lib/rubocop/rspec/version.rb +1 -1
  29. data/rubocop-rspec.gemspec +1 -1
  30. data/spec/rubocop/cop/rspec/describe_class_spec.rb +7 -0
  31. data/spec/rubocop/cop/rspec/example_without_description_spec.rb +82 -0
  32. data/spec/rubocop/cop/rspec/expect_change_spec.rb +78 -0
  33. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +18 -0
  34. data/spec/rubocop/cop/rspec/let_before_examples_spec.rb +48 -0
  35. data/spec/rubocop/cop/rspec/return_from_stub_spec.rb +65 -0
  36. data/spec/rubocop/rspec/description_extractor_spec.rb +6 -2
  37. metadata +11 -4
@@ -13,49 +13,49 @@ module RuboCop
13
13
  # @example when configuration is `EnforcedStyle: implicit`
14
14
  # # bad
15
15
  # before(:each) do
16
- # ...
16
+ # # ...
17
17
  # end
18
18
  #
19
19
  # # bad
20
20
  # before(:example) do
21
- # ...
21
+ # # ...
22
22
  # end
23
23
  #
24
24
  # # good
25
25
  # before do
26
- # ...
26
+ # # ...
27
27
  # end
28
28
  #
29
29
  # @example when configuration is `EnforcedStyle: each`
30
30
  # # bad
31
31
  # before(:example) do
32
- # ...
32
+ # # ...
33
33
  # end
34
34
  #
35
35
  # # good
36
36
  # before do
37
- # ...
37
+ # # ...
38
38
  # end
39
39
  #
40
40
  # # good
41
41
  # before(:each) do
42
- # ...
42
+ # # ...
43
43
  # end
44
44
  #
45
45
  # @example when configuration is `EnforcedStyle: example`
46
46
  # # bad
47
47
  # before(:each) do
48
- # ...
48
+ # # ...
49
49
  # end
50
50
  #
51
51
  # # bad
52
52
  # before do
53
- # ...
53
+ # # ...
54
54
  # end
55
55
  #
56
56
  # # good
57
57
  # before(:example) do
58
- # ...
58
+ # # ...
59
59
  # end
60
60
  class HookArgument < Cop
61
61
  include ConfigurableEnforcedStyle
@@ -53,6 +53,10 @@ module RuboCop
53
53
 
54
54
  def_node_matcher :spec_group?, EXAMPLE_GROUP_METHODS.block_pattern
55
55
 
56
+ def_node_matcher :dynamic_class?, <<-PATTERN
57
+ (block (send (const nil? :Class) :new ...) ...)
58
+ PATTERN
59
+
56
60
  def_node_search :ivar_usage, '$(ivar $_)'
57
61
 
58
62
  def_node_search :ivar_assigned?, '(ivasgn % ...)'
@@ -61,6 +65,7 @@ module RuboCop
61
65
  return unless spec_group?(node)
62
66
 
63
67
  ivar_usage(node) do |ivar, name|
68
+ return if inside_dynamic_class?(ivar)
64
69
  return if assignment_only? && !ivar_assigned?(node, name)
65
70
 
66
71
  add_offense(ivar, location: :expression)
@@ -69,6 +74,10 @@ module RuboCop
69
74
 
70
75
  private
71
76
 
77
+ def inside_dynamic_class?(node)
78
+ node.each_ancestor(:block).any? { |block| dynamic_class?(block) }
79
+ end
80
+
72
81
  def assignment_only?
73
82
  cop_config['AssignmentOnly']
74
83
  end
@@ -62,14 +62,7 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def node_range(node)
65
- range = node.source_range
66
- range = range_with_surrounding_space(
67
- range: range, side: :left, newlines: false
68
- )
69
- range = range_with_surrounding_space(
70
- range: range, side: :right, newlines: true
71
- )
72
- range
65
+ range_by_whole_lines(node.source_range, include_final_newline: true)
73
66
  end
74
67
 
75
68
  def in_spec_block?(node)
@@ -47,6 +47,17 @@ module RuboCop
47
47
  check_let_declarations(node.body) if multiline_block?(node.body)
48
48
  end
49
49
 
50
+ def autocorrect(node)
51
+ lambda do |corrector|
52
+ first_example = find_first_example(node.parent)
53
+ first_example_pos = first_example.loc.expression
54
+ indent = "\n" + ' ' * first_example.loc.column
55
+
56
+ corrector.insert_before(first_example_pos, source(node) + indent)
57
+ corrector.remove(node_range_with_surrounding_space(node))
58
+ end
59
+ end
60
+
50
61
  private
51
62
 
52
63
  def multiline_block?(block)
@@ -54,16 +65,45 @@ module RuboCop
54
65
  end
55
66
 
56
67
  def check_let_declarations(node)
57
- example_found = false
68
+ first_example = find_first_example(node)
69
+ return unless first_example
58
70
 
59
71
  node.each_child_node do |child|
60
- if let?(child)
61
- add_offense(child, location: :expression) if example_found
62
- elsif example_or_group?(child)
63
- example_found = true
64
- end
72
+ next if child.sibling_index < first_example.sibling_index
73
+ add_offense(child, location: :expression) if let?(child)
74
+ end
75
+ end
76
+
77
+ def find_first_example(node)
78
+ node.children.find { |sibling| example_or_group?(sibling) }
79
+ end
80
+
81
+ def node_range_with_surrounding_space(node)
82
+ range = node_range(node)
83
+ range_by_whole_lines(range, include_final_newline: true)
84
+ end
85
+
86
+ def source(node)
87
+ node_range(node).source
88
+ end
89
+
90
+ def node_range(node)
91
+ range_between(node.loc.expression.begin_pos, last_node_loc(node))
92
+ end
93
+
94
+ def last_node_loc(node)
95
+ heredoc = heredoc_lines(node).last
96
+
97
+ if heredoc
98
+ heredoc.loc.heredoc_end.end_pos
99
+ else
100
+ node.loc.end.end_pos
65
101
  end
66
102
  end
103
+
104
+ def heredoc_lines(node)
105
+ node.body.child_nodes.select { |n| n.loc.respond_to?(:heredoc_end) }
106
+ end
67
107
  end
68
108
  end
69
109
  end
@@ -15,8 +15,8 @@ module RuboCop
15
15
  # describe MyClass, '.do_something_else' do
16
16
  # end
17
17
  #
18
- # #good
19
- # describe MyClass
18
+ # # good
19
+ # describe MyClass do
20
20
  # describe '.do_something' do
21
21
  # end
22
22
  # describe '.do_something_else' do
@@ -26,7 +26,7 @@ module RuboCop
26
26
  # expect(user.name).to eq("John")
27
27
  # end
28
28
  #
29
- # it 'sets the users age'
29
+ # it 'sets the users age' do
30
30
  # expect(user.age).to eq(22)
31
31
  # end
32
32
  # end
@@ -96,10 +96,8 @@ module RuboCop
96
96
  end
97
97
 
98
98
  def flag_example(node, expectation_count:)
99
- method, = *node
100
-
101
99
  add_offense(
102
- method,
100
+ node.send_node,
103
101
  location: :expression,
104
102
  message: format(
105
103
  MSG,
@@ -64,8 +64,7 @@ module RuboCop
64
64
 
65
65
  def rename_autocorrect(node)
66
66
  lambda do |corrector|
67
- send_node, _args, _body = *node
68
- corrector.replace(send_node.loc.selector, 'let')
67
+ corrector.replace(node.send_node.loc.selector, 'let')
69
68
  end
70
69
  end
71
70
 
@@ -21,7 +21,7 @@ module RuboCop
21
21
  # let(:user_attributes) do
22
22
  # {
23
23
  # name: 'John',
24
- # age: 22
24
+ # age: 22,
25
25
  # role: role
26
26
  # }
27
27
  # end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # let(:user) do
44
44
  # UserCreate.call(
45
45
  # name: 'John',
46
- # age: 22
46
+ # age: 22,
47
47
  # role: 'admin'
48
48
  # )
49
49
  # end
@@ -7,20 +7,20 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # let(:foo) { bar }
11
- # let(:foo) { baz }
10
+ # let(:foo) { bar }
11
+ # let(:foo) { baz }
12
12
  #
13
- # subject(:foo) { bar }
14
- # let(:foo) { baz }
13
+ # subject(:foo) { bar }
14
+ # let(:foo) { baz }
15
15
  #
16
- # let(:foo) { bar }
17
- # let!(:foo) { baz }
16
+ # let(:foo) { bar }
17
+ # let!(:foo) { baz }
18
18
  #
19
19
  # # good
20
- # subject(:test) { something }
21
- # let(:foo) { bar }
22
- # let(:baz) { baz }
23
- # let!(:other) { other }
20
+ # subject(:test) { something }
21
+ # let(:foo) { bar }
22
+ # let(:baz) { baz }
23
+ # let!(:other) { other }
24
24
  class OverwritingSetup < Cop
25
25
  MSG = '`%<name>s` is already defined.'.freeze
26
26
 
@@ -31,9 +31,7 @@ module RuboCop
31
31
  def on_block(node)
32
32
  return unless example_group_with_body?(node)
33
33
 
34
- _describe, _args, body = *node
35
-
36
- find_duplicates(body) do |duplicate, name|
34
+ find_duplicates(node.body) do |duplicate, name|
37
35
  add_offense(
38
36
  duplicate,
39
37
  location: :expression,
@@ -53,6 +53,14 @@ module RuboCop
53
53
  end
54
54
  end
55
55
 
56
+ def autocorrect(node)
57
+ if style == :block
58
+ AndReturnCallCorrector.new(node)
59
+ else
60
+ BlockBodyCorrector.new(node)
61
+ end
62
+ end
63
+
56
64
  private
57
65
 
58
66
  def check_and_return_call(node)
@@ -84,6 +92,83 @@ module RuboCop
84
92
  def dynamic?(node)
85
93
  !node.recursive_literal?
86
94
  end
95
+
96
+ # :nodoc:
97
+ class AndReturnCallCorrector
98
+ def initialize(node)
99
+ @node = node
100
+ @receiver, _method_name, @args = *node
101
+ end
102
+
103
+ def call(corrector)
104
+ # Heredoc autocorrection is not yet implemented.
105
+ return if heredoc?
106
+
107
+ corrector.replace(range, " { #{replacement} }")
108
+ end
109
+
110
+ private
111
+
112
+ attr_reader :node, :receiver, :args
113
+
114
+ def heredoc?
115
+ args.loc.is_a?(Parser::Source::Map::Heredoc)
116
+ end
117
+
118
+ def range
119
+ Parser::Source::Range.new(
120
+ node.source_range.source_buffer,
121
+ receiver.source_range.end_pos,
122
+ node.source_range.end_pos
123
+ )
124
+ end
125
+
126
+ def replacement
127
+ if hash_without_braces?
128
+ "{ #{args.source} }"
129
+ else
130
+ args.source
131
+ end
132
+ end
133
+
134
+ def hash_without_braces?
135
+ args.hash_type? && !args.braces?
136
+ end
137
+ end
138
+
139
+ # :nodoc:
140
+ class BlockBodyCorrector
141
+ def initialize(node)
142
+ @block = node.each_ancestor(:block).first
143
+ @node = node
144
+ @body = block.body || NULL_BLOCK_BODY
145
+ end
146
+
147
+ def call(corrector)
148
+ # Heredoc autocorrection is not yet implemented.
149
+ return if heredoc?
150
+
151
+ corrector.replace(range, ".and_return(#{body.source})")
152
+ end
153
+
154
+ private
155
+
156
+ attr_reader :node, :block, :body
157
+
158
+ def range
159
+ Parser::Source::Range.new(
160
+ block.source_range.source_buffer,
161
+ node.source_range.end_pos,
162
+ block.source_range.end_pos
163
+ )
164
+ end
165
+
166
+ def heredoc?
167
+ body.loc.is_a?(Parser::Source::Map::Heredoc)
168
+ end
169
+
170
+ NULL_BLOCK_BODY = Struct.new(:loc, :source).new(nil, 'nil')
171
+ end
87
172
  end
88
173
  end
89
174
  end
@@ -34,22 +34,18 @@ module RuboCop
34
34
  def on_block(node)
35
35
  return unless example_group_with_body?(node)
36
36
 
37
- _describe, _args, body = *node
38
-
39
- check_let_declarations(body)
37
+ check_let_declarations(node.body)
40
38
  end
41
39
 
42
- def check_let_declarations(node)
43
- let_found = false
44
- mix_found = false
40
+ private
41
+
42
+ def check_let_declarations(body)
43
+ lets = body.each_child_node.select { |node| let?(node) }
45
44
 
46
- node.each_child_node do |child|
47
- if let?(child)
48
- add_offense(child, location: :expression) if mix_found
49
- let_found = true
50
- elsif let_found
51
- mix_found = true
52
- end
45
+ first_let = lets.first
46
+ lets.each_with_index do |node, idx|
47
+ next if node.sibling_index == first_let.sibling_index + idx
48
+ add_offense(node, location: :expression)
53
49
  end
54
50
  end
55
51
  end
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # it 'does x' do
15
15
  # end
16
16
  #
17
- # it 'does 'y' do
17
+ # it 'does y' do
18
18
  # end
19
19
  # end
20
20
  #
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # it 'does x' do
24
24
  # end
25
25
  #
26
- # it 'does 'y' do
26
+ # it 'does y' do
27
27
  # end
28
28
  # end
29
29
  #
@@ -0,0 +1,59 @@
1
+ require_relative 'rspec/capybara/current_path_expectation'
2
+ require_relative 'rspec/capybara/feature_methods'
3
+
4
+ require_relative 'rspec/factory_bot/dynamic_attribute_defined_statically'
5
+
6
+ require_relative 'rspec/align_left_let_brace'
7
+ require_relative 'rspec/align_right_let_brace'
8
+ require_relative 'rspec/any_instance'
9
+ require_relative 'rspec/around_block'
10
+ require_relative 'rspec/be_eql'
11
+ require_relative 'rspec/before_after_all'
12
+ require_relative 'rspec/context_wording'
13
+ require_relative 'rspec/describe_class'
14
+ require_relative 'rspec/described_class'
15
+ require_relative 'rspec/describe_method'
16
+ require_relative 'rspec/describe_symbol'
17
+ require_relative 'rspec/empty_example_group'
18
+ require_relative 'rspec/empty_line_after_final_let'
19
+ require_relative 'rspec/empty_line_after_subject'
20
+ require_relative 'rspec/example_length'
21
+ require_relative 'rspec/example_without_description'
22
+ require_relative 'rspec/example_wording'
23
+ require_relative 'rspec/expect_actual'
24
+ require_relative 'rspec/expect_change'
25
+ require_relative 'rspec/expect_in_hook'
26
+ require_relative 'rspec/expect_output'
27
+ require_relative 'rspec/file_path'
28
+ require_relative 'rspec/focus'
29
+ require_relative 'rspec/hook_argument'
30
+ require_relative 'rspec/implicit_expect'
31
+ require_relative 'rspec/instance_spy'
32
+ require_relative 'rspec/instance_variable'
33
+ require_relative 'rspec/invalid_predicate_matcher'
34
+ require_relative 'rspec/it_behaves_like'
35
+ require_relative 'rspec/iterated_expectation'
36
+ require_relative 'rspec/leading_subject'
37
+ require_relative 'rspec/let_before_examples'
38
+ require_relative 'rspec/let_setup'
39
+ require_relative 'rspec/message_chain'
40
+ require_relative 'rspec/message_expectation'
41
+ require_relative 'rspec/message_spies'
42
+ require_relative 'rspec/multiple_describes'
43
+ require_relative 'rspec/multiple_expectations'
44
+ require_relative 'rspec/multiple_subjects'
45
+ require_relative 'rspec/named_subject'
46
+ require_relative 'rspec/nested_groups'
47
+ require_relative 'rspec/not_to_not'
48
+ require_relative 'rspec/overwriting_setup'
49
+ require_relative 'rspec/predicate_matcher'
50
+ require_relative 'rspec/repeated_description'
51
+ require_relative 'rspec/repeated_example'
52
+ require_relative 'rspec/return_from_stub'
53
+ require_relative 'rspec/scattered_let'
54
+ require_relative 'rspec/scattered_setup'
55
+ require_relative 'rspec/shared_context'
56
+ require_relative 'rspec/single_argument_message_chain'
57
+ require_relative 'rspec/subject_stub'
58
+ require_relative 'rspec/verified_doubles'
59
+ require_relative 'rspec/void_expect'