rubocop-rspec 1.24.0 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +1 -2
  4. data/README.md +7 -4
  5. data/Rakefile +18 -3
  6. data/config/default.yml +25 -0
  7. data/lib/rubocop-rspec.rb +3 -0
  8. data/lib/rubocop/cop/rspec/be.rb +35 -0
  9. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +34 -0
  10. data/lib/rubocop/cop/rspec/describe_symbol.rb +2 -2
  11. data/lib/rubocop/cop/rspec/empty_example_group.rb +3 -3
  12. data/lib/rubocop/cop/rspec/example_without_description.rb +1 -2
  13. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +148 -0
  14. data/lib/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically.rb +1 -3
  15. data/lib/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically.rb +3 -3
  16. data/lib/rubocop/cop/rspec/instance_variable.rb +2 -2
  17. data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -2
  18. data/lib/rubocop/cop/rspec/nested_groups.rb +6 -3
  19. data/lib/rubocop/cop/rspec/pending.rb +71 -0
  20. data/lib/rubocop/cop/rspec/predicate_matcher.rb +11 -13
  21. data/lib/rubocop/cop/rspec/return_from_stub.rb +9 -16
  22. data/lib/rubocop/cop/rspec/shared_examples.rb +76 -0
  23. data/lib/rubocop/cop/rspec_cops.rb +4 -0
  24. data/lib/rubocop/rspec/example.rb +1 -1
  25. data/lib/rubocop/rspec/node.rb +19 -0
  26. data/lib/rubocop/rspec/top_level_describe.rb +3 -6
  27. data/lib/rubocop/rspec/version.rb +1 -1
  28. data/rubocop-rspec.gemspec +6 -1
  29. data/spec/rubocop/cop/rspec/be_spec.rb +33 -0
  30. data/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb +75 -18
  31. data/spec/rubocop/cop/rspec/cop_spec.rb +0 -4
  32. data/spec/rubocop/cop/rspec/described_class_spec.rb +1 -1
  33. data/spec/rubocop/cop/rspec/example_without_description_spec.rb +8 -0
  34. data/spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb +140 -0
  35. data/spec/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically_spec.rb +11 -1
  36. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +15 -0
  37. data/spec/rubocop/cop/rspec/pending_spec.rb +162 -0
  38. data/spec/rubocop/cop/rspec/predicate_matcher_spec.rb +13 -9
  39. data/spec/rubocop/cop/rspec/return_from_stub_spec.rb +9 -0
  40. data/spec/rubocop/cop/rspec/shared_examples_spec.rb +93 -0
  41. data/spec/spec_helper.rb +1 -1
  42. metadata +19 -4
@@ -56,9 +56,7 @@ module RuboCop
56
56
  private
57
57
 
58
58
  def static?(attribute)
59
- value_matcher(attribute).to_a.all? do |node|
60
- node.recursive_literal? || node.const_type?
61
- end
59
+ value_matcher(attribute).to_a.all?(&:recursive_literal_or_const?)
62
60
  end
63
61
 
64
62
  def value_hash_without_braces?(node)
@@ -41,7 +41,7 @@ module RuboCop
41
41
  def on_block(node)
42
42
  factory_attributes(node).to_a.flatten.each do |attribute|
43
43
  values = block_value_matcher(attribute)
44
- next if values.to_a.none? { |v| static?(v) }
44
+ next if values.to_a.all? { |v| dynamic?(v) }
45
45
  add_offense(attribute, location: :expression)
46
46
  end
47
47
  end
@@ -57,8 +57,8 @@ module RuboCop
57
57
 
58
58
  private
59
59
 
60
- def static?(node)
61
- node.nil? || node.recursive_literal? || node.const_type?
60
+ def dynamic?(node)
61
+ node && !node.recursive_literal_or_const?
62
62
  end
63
63
 
64
64
  def autocorrected_source(node)
@@ -26,8 +26,8 @@ module RuboCop
26
26
  # @example with AssignmentOnly configuration
27
27
  #
28
28
  # # rubocop.yml
29
- # RSpec/InstanceVariable:
30
- # AssignmentOnly: false
29
+ # # RSpec/InstanceVariable:
30
+ # # AssignmentOnly: false
31
31
  #
32
32
  # # bad
33
33
  # describe MyClass do
@@ -34,8 +34,8 @@ module RuboCop
34
34
  # @example configuration
35
35
  #
36
36
  # # .rubocop.yml
37
- # RSpec/MultipleExpectations:
38
- # Max: 2
37
+ # # RSpec/MultipleExpectations:
38
+ # # Max: 2
39
39
  #
40
40
  # # not flagged by rubocop
41
41
  # describe UserCreator do
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # Checks for nested example groups.
7
7
  #
8
8
  # This cop is configurable using the `Max` option
9
+ # and supports `--auto-gen-config
9
10
  #
10
11
  # @example
11
12
  # # bad
@@ -55,8 +56,8 @@ module RuboCop
55
56
  # @example configuration
56
57
  #
57
58
  # # .rubocop.yml
58
- # RSpec/NestedGroups:
59
- # Max: 2
59
+ # # RSpec/NestedGroups:
60
+ # # Max: 2
60
61
  #
61
62
  # context 'when using some feature' do
62
63
  # let(:some) { :various }
@@ -70,7 +71,7 @@ module RuboCop
70
71
  # let(:user_attributes) do
71
72
  # {
72
73
  # name: 'John',
73
- # age: 22
74
+ # age: 22,
74
75
  # role: role
75
76
  # }
76
77
  # end
@@ -85,6 +86,7 @@ module RuboCop
85
86
  # end
86
87
  #
87
88
  class NestedGroups < Cop
89
+ include ConfigurableMax
88
90
  include RuboCop::RSpec::TopLevelDescribe
89
91
 
90
92
  MSG = 'Maximum example group nesting exceeded ' \
@@ -100,6 +102,7 @@ module RuboCop
100
102
 
101
103
  def on_top_level_describe(node, _args)
102
104
  find_nested_contexts(node.parent) do |context, nesting|
105
+ self.max = nesting
103
106
  add_offense(
104
107
  context.children.first,
105
108
  location: :expression,
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for any pending or skipped examples.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # describe MyClass do
11
+ # it "should be true"
12
+ # end
13
+ #
14
+ # describe MyClass do
15
+ # it "should be true" do
16
+ # pending
17
+ # end
18
+ # end
19
+ #
20
+ # describe MyClass do
21
+ # xit "should be true" do
22
+ # end
23
+ # end
24
+ #
25
+ # # good
26
+ # describe MyClass do
27
+ # end
28
+ class Pending < Cop
29
+ MSG = 'Pending spec found.'.freeze
30
+
31
+ PENDING_EXAMPLES = Examples::PENDING + Examples::SKIPPED \
32
+ + ExampleGroups::SKIPPED
33
+ SKIPPABLE_EXAMPLES = ExampleGroups::GROUPS + Examples::EXAMPLES
34
+ SKIPPABLE_SELECTORS = SKIPPABLE_EXAMPLES.node_pattern_union
35
+
36
+ SKIP_SYMBOL = s(:sym, :skip)
37
+ PENDING_SYMBOL = s(:sym, :pending)
38
+
39
+ def_node_matcher :metadata, <<-PATTERN
40
+ {(send nil? #{SKIPPABLE_SELECTORS} ... (hash $...))
41
+ (send nil? #{SKIPPABLE_SELECTORS} $...)}
42
+ PATTERN
43
+
44
+ def_node_matcher :pending_block?, PENDING_EXAMPLES.send_pattern
45
+
46
+ def on_send(node)
47
+ return unless pending_block?(node) || skipped_from_metadata?(node)
48
+ add_offense(node, location: :expression)
49
+ end
50
+
51
+ private
52
+
53
+ def skipped_from_metadata?(node)
54
+ (metadata(node) || []).any? { |n| skip_node?(n) }
55
+ end
56
+
57
+ def skip_node?(node)
58
+ if node.respond_to?(:key)
59
+ skip_symbol?(node.key) && node.value.truthy_literal?
60
+ else
61
+ skip_symbol?(node)
62
+ end
63
+ end
64
+
65
+ def skip_symbol?(symbol_node)
66
+ symbol_node == SKIP_SYMBOL || symbol_node == PENDING_SYMBOL
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -50,10 +50,9 @@ module RuboCop
50
50
  end
51
51
 
52
52
  def message_inflected(predicate)
53
- _recv, predicate_name, = *predicate
54
53
  format(MSG_INFLECTED,
55
- predicate_name: predicate_name,
56
- matcher_name: to_predicate_matcher(predicate_name))
54
+ predicate_name: predicate.method_name,
55
+ matcher_name: to_predicate_matcher(predicate.method_name))
57
56
  end
58
57
 
59
58
  # rubocop:disable Metrics/MethodLength
@@ -99,17 +98,17 @@ module RuboCop
99
98
  args = args_loc(predicate).source
100
99
  block_loc = block_loc(predicate)
101
100
  block = block_loc ? block_loc.source : ''
102
- _recv, name, = *predicate
103
101
 
104
- corrector.replace(matcher.loc.expression,
105
- to_predicate_matcher(name) + args + block)
102
+ corrector.replace(
103
+ matcher.loc.expression,
104
+ to_predicate_matcher(predicate.method_name) + args + block
105
+ )
106
106
  end
107
107
 
108
108
  def true?(to_symbol, matcher)
109
- _recv, name, arg = *matcher
110
- result = case name
109
+ result = case matcher.method_name
111
110
  when :be, :eq
112
- arg.true_type?
111
+ matcher.first_argument.true_type?
113
112
  when :be_truthy, :a_truthy_value
114
113
  true
115
114
  when :be_falsey, :be_falsy, :a_falsey_value, :a_falsy_value
@@ -180,10 +179,9 @@ module RuboCop
180
179
  end
181
180
 
182
181
  def message_explicit(matcher)
183
- _recv, name, = *matcher
184
182
  format(MSG_EXPLICIT,
185
- predicate_name: to_predicate_method(name),
186
- matcher_name: name)
183
+ predicate_name: to_predicate_method(matcher.method_name),
184
+ matcher_name: matcher.method_name)
187
185
  end
188
186
 
189
187
  def autocorrect_explicit(node)
@@ -199,7 +197,7 @@ module RuboCop
199
197
 
200
198
  def autocorrect_explicit_block(node)
201
199
  predicate_matcher_block?(node) do |actual, matcher|
202
- to_node, = *node
200
+ to_node = node.send_node
203
201
  corrector_explicit(to_node, actual, matcher, to_node)
204
202
  end
205
203
  end
@@ -82,7 +82,7 @@ module RuboCop
82
82
 
83
83
  def check_block_body(block)
84
84
  body = block.body
85
- unless body && dynamic?(body) # rubocop:disable Style/GuardClause
85
+ unless dynamic?(body) # rubocop:disable Style/GuardClause
86
86
  add_offense(
87
87
  block,
88
88
  location: :begin,
@@ -92,14 +92,15 @@ module RuboCop
92
92
  end
93
93
 
94
94
  def dynamic?(node)
95
- !node.recursive_literal? && !node.const_type?
95
+ node && !node.recursive_literal_or_const?
96
96
  end
97
97
 
98
98
  # :nodoc:
99
99
  class AndReturnCallCorrector
100
100
  def initialize(node)
101
101
  @node = node
102
- @receiver, _method_name, @args = *node
102
+ @receiver = node.receiver
103
+ @arg = node.first_argument
103
104
  end
104
105
 
105
106
  def call(corrector)
@@ -111,10 +112,10 @@ module RuboCop
111
112
 
112
113
  private
113
114
 
114
- attr_reader :node, :receiver, :args
115
+ attr_reader :node, :receiver, :arg
115
116
 
116
117
  def heredoc?
117
- args.loc.is_a?(Parser::Source::Map::Heredoc)
118
+ arg.loc.is_a?(Parser::Source::Map::Heredoc)
118
119
  end
119
120
 
120
121
  def range
@@ -127,14 +128,14 @@ module RuboCop
127
128
 
128
129
  def replacement
129
130
  if hash_without_braces?
130
- "{ #{args.source} }"
131
+ "{ #{arg.source} }"
131
132
  else
132
- args.source
133
+ arg.source
133
134
  end
134
135
  end
135
136
 
136
137
  def hash_without_braces?
137
- args.hash_type? && !args.braces?
138
+ arg.hash_type? && !arg.braces?
138
139
  end
139
140
  end
140
141
 
@@ -160,14 +161,6 @@ module RuboCop
160
161
 
161
162
  attr_reader :node, :block, :body
162
163
 
163
- def range
164
- Parser::Source::Range.new(
165
- block.source_range.source_buffer,
166
- node.source_range.end_pos,
167
- block.source_range.end_pos
168
- )
169
- end
170
-
171
164
  def heredoc?
172
165
  body.loc.is_a?(Parser::Source::Map::Heredoc)
173
166
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Enforces use of string to titleize shared examples.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # it_behaves_like :foo_bar_baz
11
+ # it_should_behave_like :foo_bar_baz
12
+ # shared_examples :foo_bar_baz
13
+ # shared_examples_for :foo_bar_baz
14
+ # include_examples :foo_bar_baz
15
+ #
16
+ # # good
17
+ # it_behaves_like 'foo bar baz'
18
+ # it_should_behave_like 'foo bar baz'
19
+ # shared_examples 'foo bar baz'
20
+ # shared_examples_for 'foo bar baz'
21
+ # include_examples 'foo bar baz'
22
+ #
23
+ class SharedExamples < Cop
24
+ def_node_matcher :shared_examples, <<-PATTERN
25
+ (send
26
+ {(const nil? :RSpec) nil?}
27
+ {#{(SharedGroups::ALL + Includes::ALL).node_pattern}} $sym ...)
28
+ PATTERN
29
+
30
+ def on_send(node)
31
+ shared_examples(node) do |ast_node|
32
+ checker = Checker.new(ast_node)
33
+ add_offense(checker.node, message: checker.message)
34
+ end
35
+ end
36
+
37
+ def autocorrect(node)
38
+ lambda do |corrector|
39
+ checker = Checker.new(node)
40
+ corrector.replace(node.loc.expression, checker.preferred_style)
41
+ end
42
+ end
43
+
44
+ # :nodoc:
45
+ class Checker
46
+ MSG = 'Prefer %<prefer>s over `%<current>s` ' \
47
+ 'to titleize shared examples.'.freeze
48
+
49
+ attr_reader :node
50
+ def initialize(node)
51
+ @node = node
52
+ end
53
+
54
+ def message
55
+ format(MSG, prefer: preferred_style, current: symbol.inspect)
56
+ end
57
+
58
+ def preferred_style
59
+ string = symbol.to_s.tr('_', ' ')
60
+ wrap_with_single_quotes(string)
61
+ end
62
+
63
+ private
64
+
65
+ def symbol
66
+ node.value
67
+ end
68
+
69
+ def wrap_with_single_quotes(string)
70
+ "'#{string}'"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,6 +1,7 @@
1
1
  require_relative 'rspec/capybara/current_path_expectation'
2
2
  require_relative 'rspec/capybara/feature_methods'
3
3
 
4
+ require_relative 'rspec/factory_bot/create_list'
4
5
  require_relative 'rspec/factory_bot/dynamic_attribute_defined_statically'
5
6
  require_relative 'rspec/factory_bot/static_attribute_defined_dynamically'
6
7
 
@@ -14,6 +15,7 @@ require_relative 'rspec/align_left_let_brace'
14
15
  require_relative 'rspec/align_right_let_brace'
15
16
  require_relative 'rspec/any_instance'
16
17
  require_relative 'rspec/around_block'
18
+ require_relative 'rspec/be'
17
19
  require_relative 'rspec/be_eql'
18
20
  require_relative 'rspec/before_after_all'
19
21
  require_relative 'rspec/context_wording'
@@ -53,6 +55,7 @@ require_relative 'rspec/named_subject'
53
55
  require_relative 'rspec/nested_groups'
54
56
  require_relative 'rspec/not_to_not'
55
57
  require_relative 'rspec/overwriting_setup'
58
+ require_relative 'rspec/pending'
56
59
  require_relative 'rspec/predicate_matcher'
57
60
  require_relative 'rspec/repeated_description'
58
61
  require_relative 'rspec/repeated_example'
@@ -60,6 +63,7 @@ require_relative 'rspec/return_from_stub'
60
63
  require_relative 'rspec/scattered_let'
61
64
  require_relative 'rspec/scattered_setup'
62
65
  require_relative 'rspec/shared_context'
66
+ require_relative 'rspec/shared_examples'
63
67
  require_relative 'rspec/single_argument_message_chain'
64
68
  require_relative 'rspec/subject_stub'
65
69
  require_relative 'rspec/verified_doubles'
@@ -24,7 +24,7 @@ module RuboCop
24
24
  if node.send_type?
25
25
  node
26
26
  else
27
- node.children.first
27
+ node.send_node
28
28
  end
29
29
  end
30
30
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # RuboCop-RSpec specific extensions of RuboCop::AST::Node
6
+ module Node
7
+ # In various cops we want to regard const as literal althought it's not
8
+ # strictly literal.
9
+ def recursive_literal_or_const?
10
+ case type
11
+ when :begin, :pair, *AST::Node::COMPOSITE_LITERALS
12
+ children.all?(&:recursive_literal_or_const?)
13
+ else
14
+ literal? || const_type?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -12,16 +12,13 @@ module RuboCop
12
12
  return unless respond_to?(:on_top_level_describe)
13
13
  return unless top_level_describe?(node)
14
14
 
15
- _receiver, _method_name, *args = *node
16
-
17
- on_top_level_describe(node, args)
15
+ on_top_level_describe(node, node.arguments)
18
16
  end
19
17
 
20
18
  private
21
19
 
22
20
  def top_level_describe?(node)
23
- _receiver, method_name, *_args = *node
24
- return false unless method_name == :describe
21
+ return false unless node.method_name == :describe
25
22
 
26
23
  top_level_nodes.include?(node)
27
24
  end
@@ -49,7 +46,7 @@ module RuboCop
49
46
 
50
47
  def describe_statement_children(node)
51
48
  node.each_child_node(:send).select do |element|
52
- element.children[1] == :describe
49
+ element.method_name == :describe
53
50
  end
54
51
  end
55
52
  end