rubocop-rspec 1.21.0 → 1.22.0

Sign up to get free protection for your applications and to get access to all the features.
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'