rubocop-rspec 1.24.0 → 1.25.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 (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