rubocop-rspec 2.23.0 → 2.26.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -12
  3. data/config/default.yml +69 -3
  4. data/lib/rubocop/cop/rspec/around_block.rb +3 -3
  5. data/lib/rubocop/cop/rspec/be.rb +1 -1
  6. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  7. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  8. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  9. data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
  10. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -2
  11. data/lib/rubocop/cop/rspec/change_by_zero.rb +3 -3
  12. data/lib/rubocop/cop/rspec/context_method.rb +2 -2
  13. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  14. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  15. data/lib/rubocop/cop/rspec/described_class.rb +5 -5
  16. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +1 -1
  17. data/lib/rubocop/cop/rspec/empty_example_group.rb +2 -2
  18. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
  19. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  20. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  21. data/lib/rubocop/cop/rspec/example_length.rb +11 -5
  22. data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
  23. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +4 -2
  24. data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
  25. data/lib/rubocop/cop/rspec/file_path.rb +6 -0
  26. data/lib/rubocop/cop/rspec/focus.rb +4 -2
  27. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  28. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  29. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
  30. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  31. data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
  32. data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
  33. data/lib/rubocop/cop/rspec/instance_variable.rb +2 -2
  34. data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
  35. data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
  36. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  37. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
  38. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  39. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  40. data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
  41. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
  42. data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -2
  43. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  44. data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
  45. data/lib/rubocop/cop/rspec/predicate_matcher.rb +6 -6
  46. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -1
  47. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +1 -1
  48. data/lib/rubocop/cop/rspec/rails/http_status.rb +29 -18
  49. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +2 -2
  50. data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +10 -0
  51. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  52. data/lib/rubocop/cop/rspec/receive_messages.rb +11 -5
  53. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +65 -0
  54. data/lib/rubocop/cop/rspec/remove_const.rb +40 -0
  55. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
  56. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
  57. data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
  58. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  59. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  60. data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
  61. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
  62. data/lib/rubocop/cop/rspec/sort_metadata.rb +2 -1
  63. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  64. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  65. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  66. data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
  67. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
  68. data/lib/rubocop/cop/rspec/variable_definition.rb +4 -4
  69. data/lib/rubocop/cop/rspec/verified_double_reference.rb +5 -5
  70. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  71. data/lib/rubocop/cop/rspec/void_expect.rb +4 -3
  72. data/lib/rubocop/cop/rspec_cops.rb +7 -0
  73. data/lib/rubocop/rspec/language.rb +8 -8
  74. data/lib/rubocop/rspec/version.rb +1 -1
  75. data/lib/rubocop/rspec/wording.rb +8 -0
  76. data/lib/rubocop-rspec.rb +1 -0
  77. metadata +16 -8
@@ -70,7 +70,7 @@ module RuboCop
70
70
  TRUE = ->(node) { node.true_type? }
71
71
 
72
72
  # @!method aggregate_failures?(node)
73
- def_node_matcher :aggregate_failures?, <<-PATTERN
73
+ def_node_matcher :aggregate_failures?, <<~PATTERN
74
74
  (block {
75
75
  (send _ _ <(sym :aggregate_failures) ...>)
76
76
  (send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
@@ -81,7 +81,7 @@ module RuboCop
81
81
  def_node_matcher :expect?, '(send nil? #Expectations.all ...)'
82
82
 
83
83
  # @!method aggregate_failures_block?(node)
84
- def_node_matcher :aggregate_failures_block?, <<-PATTERN
84
+ def_node_matcher :aggregate_failures_block?, <<~PATTERN
85
85
  (block (send nil? :aggregate_failures ...) ...)
86
86
  PATTERN
87
87
 
@@ -67,7 +67,7 @@ module RuboCop
67
67
  private
68
68
 
69
69
  def skipped?(node)
70
- skippable?(node) && skipped_in_metadata?(node) ||
70
+ (skippable?(node) && skipped_in_metadata?(node)) ||
71
71
  skipped_regular_example_without_body?(node)
72
72
  end
73
73
 
@@ -103,7 +103,7 @@ module RuboCop
103
103
  on_pending_by_metadata(node)
104
104
  return unless (parent = parent_node(node))
105
105
 
106
- if example_group?(parent) || block_node_example_group?(node)
106
+ if spec_group?(parent) || block_node_example_group?(node)
107
107
  on_skipped_by_example_method(node)
108
108
  on_skipped_by_example_group_method(node)
109
109
  elsif example?(parent)
@@ -26,7 +26,7 @@ module RuboCop
26
26
  end
27
27
 
28
28
  # @!method predicate_in_actual?(node)
29
- def_node_matcher :predicate_in_actual?, <<-PATTERN
29
+ def_node_matcher :predicate_in_actual?, <<~PATTERN
30
30
  (send
31
31
  (send nil? :expect {
32
32
  (block $(send !nil? #predicate? ...) ...)
@@ -36,12 +36,12 @@ module RuboCop
36
36
  PATTERN
37
37
 
38
38
  # @!method be_bool?(node)
39
- def_node_matcher :be_bool?, <<-PATTERN
39
+ def_node_matcher :be_bool?, <<~PATTERN
40
40
  (send nil? {:be :eq :eql :equal} {true false})
41
41
  PATTERN
42
42
 
43
43
  # @!method be_boolthy?(node)
44
- def_node_matcher :be_boolthy?, <<-PATTERN
44
+ def_node_matcher :be_boolthy?, <<~PATTERN
45
45
  (send nil? {:be_truthy :be_falsey :be_falsy :a_truthy_value :a_falsey_value :a_falsy_value})
46
46
  PATTERN
47
47
 
@@ -179,7 +179,7 @@ module RuboCop
179
179
  end
180
180
 
181
181
  # @!method predicate_matcher?(node)
182
- def_node_matcher :predicate_matcher?, <<-PATTERN
182
+ def_node_matcher :predicate_matcher?, <<~PATTERN
183
183
  (send
184
184
  (send nil? :expect $!nil?)
185
185
  #Runners.all
@@ -188,7 +188,7 @@ module RuboCop
188
188
  PATTERN
189
189
 
190
190
  # @!method predicate_matcher_block?(node)
191
- def_node_matcher :predicate_matcher_block?, <<-PATTERN
191
+ def_node_matcher :predicate_matcher_block?, <<~PATTERN
192
192
  (block
193
193
  (send
194
194
  (send nil? :expect $!nil?)
@@ -202,7 +202,7 @@ module RuboCop
202
202
 
203
203
  return false if allowed_explicit_matchers.include?(name)
204
204
 
205
- name.start_with?('be_', 'have_') && !name.end_with?('?') ||
205
+ (name.start_with?('be_', 'have_') && !name.end_with?('?')) ||
206
206
  %w[include respond_to].include?(name)
207
207
  end
208
208
 
@@ -23,7 +23,7 @@ module RuboCop
23
23
  MSG = 'Use `before` instead of `setup`.'
24
24
 
25
25
  # @!method setup_call(node)
26
- def_node_matcher :setup_call, <<-PATTERN
26
+ def_node_matcher :setup_call, <<~PATTERN
27
27
  (block
28
28
  $(send nil? :setup)
29
29
  (args) _)
@@ -25,7 +25,7 @@ module RuboCop
25
25
  RESTRICT_ON_SEND = RUNNERS
26
26
 
27
27
  # @!method match_status(node)
28
- def_node_matcher :match_status, <<-PATTERN
28
+ def_node_matcher :match_status, <<~PATTERN
29
29
  (send
30
30
  (send nil? :expect
31
31
  $(send (send nil? :response) {:status :code})
@@ -17,10 +17,12 @@ module RuboCop
17
17
  # # bad
18
18
  # it { is_expected.to have_http_status 200 }
19
19
  # it { is_expected.to have_http_status 404 }
20
+ # it { is_expected.to have_http_status "403" }
20
21
  #
21
22
  # # good
22
23
  # it { is_expected.to have_http_status :ok }
23
24
  # it { is_expected.to have_http_status :not_found }
25
+ # it { is_expected.to have_http_status :forbidden }
24
26
  # it { is_expected.to have_http_status :success }
25
27
  # it { is_expected.to have_http_status :error }
26
28
  #
@@ -28,10 +30,12 @@ module RuboCop
28
30
  # # bad
29
31
  # it { is_expected.to have_http_status :ok }
30
32
  # it { is_expected.to have_http_status :not_found }
33
+ # it { is_expected.to have_http_status "forbidden" }
31
34
  #
32
35
  # # good
33
36
  # it { is_expected.to have_http_status 200 }
34
37
  # it { is_expected.to have_http_status 404 }
38
+ # it { is_expected.to have_http_status 403 }
35
39
  # it { is_expected.to have_http_status :success }
36
40
  # it { is_expected.to have_http_status :error }
37
41
  #
@@ -39,8 +43,10 @@ module RuboCop
39
43
  # # bad
40
44
  # it { is_expected.to have_http_status :ok }
41
45
  # it { is_expected.to have_http_status :not_found }
46
+ # it { is_expected.to have_http_status "forbidden" }
42
47
  # it { is_expected.to have_http_status 200 }
43
48
  # it { is_expected.to have_http_status 404 }
49
+ # it { is_expected.to have_http_status "403" }
44
50
  #
45
51
  # # good
46
52
  # it { is_expected.to be_ok }
@@ -54,12 +60,14 @@ module RuboCop
54
60
  RESTRICT_ON_SEND = %i[have_http_status].freeze
55
61
 
56
62
  # @!method http_status(node)
57
- def_node_matcher :http_status, <<-PATTERN
58
- (send nil? :have_http_status ${int sym})
63
+ def_node_matcher :http_status, <<~PATTERN
64
+ (send nil? :have_http_status ${int sym str})
59
65
  PATTERN
60
66
 
61
67
  def on_send(node)
62
68
  http_status(node) do |arg|
69
+ return if arg.str_type? && arg.heredoc?
70
+
63
71
  checker = checker_class.new(arg)
64
72
  return unless checker.offensive?
65
73
 
@@ -99,6 +107,10 @@ module RuboCop
99
107
  format(MSG, prefer: prefer, current: current)
100
108
  end
101
109
 
110
+ def current
111
+ offense_range.source
112
+ end
113
+
102
114
  def offense_range
103
115
  node
104
116
  end
@@ -123,10 +135,6 @@ module RuboCop
123
135
  symbol.inspect
124
136
  end
125
137
 
126
- def current
127
- number.inspect
128
- end
129
-
130
138
  private
131
139
 
132
140
  def symbol
@@ -134,7 +142,7 @@ module RuboCop
134
142
  end
135
143
 
136
144
  def number
137
- node.source.to_i
145
+ node.value.to_i
138
146
  end
139
147
  end
140
148
 
@@ -148,10 +156,6 @@ module RuboCop
148
156
  number.to_s
149
157
  end
150
158
 
151
- def current
152
- symbol.inspect
153
- end
154
-
155
159
  private
156
160
 
157
161
  def symbol
@@ -159,7 +163,7 @@ module RuboCop
159
163
  end
160
164
 
161
165
  def number
162
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
166
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol.to_sym]
163
167
  end
164
168
  end
165
169
 
@@ -177,15 +181,13 @@ module RuboCop
177
181
  def prefer
178
182
  if node.sym_type?
179
183
  "be_#{node.value}"
180
- else
184
+ elsif node.int_type?
181
185
  "be_#{symbol}"
186
+ elsif node.str_type?
187
+ "be_#{normalize_str}"
182
188
  end
183
189
  end
184
190
 
185
- def current
186
- offense_range.source
187
- end
188
-
189
191
  private
190
192
 
191
193
  def symbol
@@ -193,7 +195,16 @@ module RuboCop
193
195
  end
194
196
 
195
197
  def number
196
- node.source.to_i
198
+ node.value.to_i
199
+ end
200
+
201
+ def normalize_str
202
+ str = node.value.to_s
203
+ if str.match?(/\A\d+\z/)
204
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(str.to_i)
205
+ else
206
+ str
207
+ end
197
208
  end
198
209
  end
199
210
  end
@@ -24,8 +24,8 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = %i[assert_equal refute_equal].freeze
25
25
 
26
26
  # @!method minitest_assertion(node)
27
- def_node_matcher :minitest_assertion, <<-PATTERN
28
- (send nil? {:assert_equal :refute_equal} $_ $_ $_?)
27
+ def_node_matcher :minitest_assertion, <<~PATTERN
28
+ (send nil? {:assert_equal :refute_equal} $_ $_ $_?)
29
29
  PATTERN
30
30
 
31
31
  def on_send(node)
@@ -6,6 +6,10 @@ module RuboCop
6
6
  module Rails
7
7
  # Enforces use of `be_invalid` or `not_to` for negated be_valid.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because it cannot guarantee that
11
+ # the test target is an instance of `ActiveModel::Validations``.
12
+ #
9
13
  # @example EnforcedStyle: not_to (default)
10
14
  # # bad
11
15
  # expect(foo).to be_invalid
@@ -13,6 +17,9 @@ module RuboCop
13
17
  # # good
14
18
  # expect(foo).not_to be_valid
15
19
  #
20
+ # # good (with method chain)
21
+ # expect(foo).to be_invalid.and be_odd
22
+ #
16
23
  # @example EnforcedStyle: be_invalid
17
24
  # # bad
18
25
  # expect(foo).not_to be_valid
@@ -20,6 +27,9 @@ module RuboCop
20
27
  # # good
21
28
  # expect(foo).to be_invalid
22
29
  #
30
+ # # good (with method chain)
31
+ # expect(foo).to be_invalid.or be_even
32
+ #
23
33
  class NegationBeValid < Base
24
34
  extend AutoCorrector
25
35
  include ConfigurableEnforcedStyle
@@ -30,7 +30,7 @@ module RuboCop
30
30
  RESTRICT_ON_SEND = %i[times].freeze
31
31
 
32
32
  # @!method receive_counts(node)
33
- def_node_matcher :receive_counts, <<-PATTERN
33
+ def_node_matcher :receive_counts, <<~PATTERN
34
34
  (send $(send _ {:exactly :at_least :at_most} (int {1 2})) :times)
35
35
  PATTERN
36
36
 
@@ -5,6 +5,11 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for multiple messages stubbed on the same object.
7
7
  #
8
+ # @safety
9
+ # The autocorrection is marked as unsafe, because it may change the
10
+ # order of stubs. This in turn may cause e.g. variables to be called
11
+ # before they are defined.
12
+ #
8
13
  # @example
9
14
  # # bad
10
15
  # before do
@@ -32,7 +37,7 @@ module RuboCop
32
37
 
33
38
  # @!method allow_receive_message?(node)
34
39
  def_node_matcher :allow_receive_message?, <<~PATTERN
35
- (send (send nil? :allow ...) :to (send (send nil? :receive (sym _)) :and_return !#heredoc?))
40
+ (send (send nil? :allow ...) :to (send (send nil? :receive (sym _)) :and_return !#heredoc_or_splat?))
36
41
  PATTERN
37
42
 
38
43
  # @!method allow_argument(node)
@@ -119,7 +124,7 @@ module RuboCop
119
124
 
120
125
  def register_offense(item, repeated_lines, args)
121
126
  add_offense(item, message: message(repeated_lines)) do |corrector|
122
- if item.loc.line < repeated_lines.min
127
+ if item.loc.line > repeated_lines.max
123
128
  replace_to_receive_messages(corrector, item, args)
124
129
  else
125
130
  corrector.remove(item_range_by_whole_lines(item))
@@ -142,12 +147,13 @@ module RuboCop
142
147
  range_by_whole_lines(item.source_range, include_final_newline: true)
143
148
  end
144
149
 
145
- def heredoc?(node)
146
- (node.str_type? || node.dstr_type?) && node.heredoc?
150
+ def heredoc_or_splat?(node)
151
+ ((node.str_type? || node.dstr_type?) && node.heredoc?) ||
152
+ node.splat_type?
147
153
  end
148
154
 
149
155
  def requires_quotes?(value)
150
- value.match?(/^:".*?"|=$/)
156
+ value.match?(/^:".*?"|=$|^\W+$/)
151
157
  end
152
158
  end
153
159
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for redundant predicate matcher.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect(foo).to be_exist(bar)
11
+ # expect(foo).not_to be_include(bar)
12
+ #
13
+ # # good
14
+ # expect(foo).to exist(bar)
15
+ # expect(foo).not_to include(bar)
16
+ #
17
+ class RedundantPredicateMatcher < Base
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `%<good>s` instead of `%<bad>s`.'
21
+ RESTRICT_ON_SEND =
22
+ %i[be_all be_cover be_end_with be_eql be_equal
23
+ be_exist be_exists be_include be_match
24
+ be_respond_to be_start_with].freeze
25
+
26
+ def on_send(node)
27
+ return if node.parent.block_type? || node.arguments.empty?
28
+ return unless replacable_arguments?(node)
29
+
30
+ method_name = node.method_name.to_s
31
+ replaced = replaced_method_name(method_name)
32
+ add_offense(node, message: message(method_name,
33
+ replaced)) do |corrector|
34
+ unless node.method?(:be_all)
35
+ corrector.replace(node.loc.selector, replaced)
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def message(bad_method, good_method)
43
+ format(MSG, bad: bad_method, good: good_method)
44
+ end
45
+
46
+ def replacable_arguments?(node)
47
+ if node.method?(:be_all)
48
+ node.first_argument.send_type?
49
+ else
50
+ true
51
+ end
52
+ end
53
+
54
+ def replaced_method_name(method_name)
55
+ name = method_name.to_s.delete_prefix('be_')
56
+ if name == 'exists'
57
+ 'exist'
58
+ else
59
+ name
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks that `remove_const` is not used in specs.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # it 'does something' do
11
+ # Object.send(:remove_const, :SomeConstant)
12
+ # end
13
+ #
14
+ # before do
15
+ # SomeClass.send(:remove_const, :SomeConstant)
16
+ # end
17
+ #
18
+ class RemoveConst < Base
19
+ include RuboCop::RSpec::Language
20
+ extend RuboCop::RSpec::Language::NodePattern
21
+
22
+ MSG = 'Do not use remove_const in specs. ' \
23
+ 'Consider using e.g. `stub_const`.'
24
+ RESTRICT_ON_SEND = %i[send __send__].freeze
25
+
26
+ # @!method remove_const(node)
27
+ def_node_matcher :remove_const, <<~PATTERN
28
+ (send _ {:send | :__send__} (sym :remove_const) _)
29
+ PATTERN
30
+
31
+ # Check for offenses
32
+ def on_send(node)
33
+ remove_const(node) do
34
+ add_offense(node)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
49
49
 
50
50
  # @!method several_example_groups?(node)
51
- def_node_matcher :several_example_groups?, <<-PATTERN
51
+ def_node_matcher :several_example_groups?, <<~PATTERN
52
52
  (begin <#example_group_with_body? #example_group_with_body? ...>)
53
53
  PATTERN
54
54
 
@@ -48,12 +48,12 @@ module RuboCop
48
48
  MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
49
49
 
50
50
  # @!method several_example_groups?(node)
51
- def_node_matcher :several_example_groups?, <<-PATTERN
51
+ def_node_matcher :several_example_groups?, <<~PATTERN
52
52
  (begin <#example_group? #example_group? ...>)
53
53
  PATTERN
54
54
 
55
55
  # @!method doc_string_and_metadata(node)
56
- def_node_matcher :doc_string_and_metadata, <<-PATTERN
56
+ def_node_matcher :doc_string_and_metadata, <<~PATTERN
57
57
  (block (send _ _ $_ $...) ...)
58
58
  PATTERN
59
59
 
@@ -50,7 +50,7 @@ module RuboCop
50
50
  'on line(s) %<repeat>s'
51
51
 
52
52
  # @!method several_include_examples?(node)
53
- def_node_matcher :several_include_examples?, <<-PATTERN
53
+ def_node_matcher :several_include_examples?, <<~PATTERN
54
54
  (begin <#include_examples? #include_examples? ...>)
55
55
  PATTERN
56
56
 
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def_node_matcher :stub_with_block?, '(block #contains_stub? ...)'
49
49
 
50
50
  # @!method and_return_value(node)
51
- def_node_search :and_return_value, <<-PATTERN
51
+ def_node_search :and_return_value, <<~PATTERN
52
52
  $(send _ :and_return $(...))
53
53
  PATTERN
54
54
 
@@ -62,7 +62,7 @@ module RuboCop
62
62
  PATTERN
63
63
 
64
64
  # @!method context?(node)
65
- def_node_search :context?, <<-PATTERN
65
+ def_node_search :context?, <<~PATTERN
66
66
  (send nil?
67
67
  {#Subjects.all #Helpers.all #Includes.context #Hooks.all} ...
68
68
  )
@@ -3,9 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Enforces use of string to titleize shared examples.
6
+ # Checks for consistent style for shared example names.
7
7
  #
8
- # @example
8
+ # Enforces either `string` or `symbol` for shared example names.
9
+ #
10
+ # This cop can be configured using the `EnforcedStyle` option
11
+ #
12
+ # @example `EnforcedStyle: string` (default)
9
13
  # # bad
10
14
  # it_behaves_like :foo_bar_baz
11
15
  # it_should_behave_like :foo_bar_baz
@@ -20,33 +24,66 @@ module RuboCop
20
24
  # shared_examples_for 'foo bar baz'
21
25
  # include_examples 'foo bar baz'
22
26
  #
27
+ # @example `EnforcedStyle: symbol`
28
+ # # bad
29
+ # it_behaves_like 'foo bar baz'
30
+ # it_should_behave_like 'foo bar baz'
31
+ # shared_examples 'foo bar baz'
32
+ # shared_examples_for 'foo bar baz'
33
+ # include_examples 'foo bar baz'
34
+ #
35
+ # # good
36
+ # it_behaves_like :foo_bar_baz
37
+ # it_should_behave_like :foo_bar_baz
38
+ # shared_examples :foo_bar_baz
39
+ # shared_examples_for :foo_bar_baz
40
+ # include_examples :foo_bar_baz
41
+ #
23
42
  class SharedExamples < Base
24
43
  extend AutoCorrector
44
+ include ConfigurableEnforcedStyle
25
45
 
26
46
  # @!method shared_examples(node)
27
47
  def_node_matcher :shared_examples, <<~PATTERN
28
48
  {
29
- (send #rspec? #SharedGroups.all ...)
30
- (send nil? #Includes.all ...)
49
+ (send #rspec? #SharedGroups.all $_ ...)
50
+ (send nil? #Includes.all $_ ...)
31
51
  }
32
52
  PATTERN
33
53
 
34
54
  def on_send(node)
35
- shared_examples(node) do
36
- ast_node = node.first_argument
37
- next unless ast_node&.sym_type?
55
+ shared_examples(node) do |ast_node|
56
+ next unless offense?(ast_node)
38
57
 
39
- checker = Checker.new(ast_node)
40
- add_offense(checker.node, message: checker.message) do |corrector|
41
- corrector.replace(checker.node, checker.preferred_style)
58
+ checker = new_checker(ast_node)
59
+ add_offense(ast_node, message: checker.message) do |corrector|
60
+ corrector.replace(ast_node, checker.preferred_style)
42
61
  end
43
62
  end
44
63
  end
45
64
 
65
+ private
66
+
67
+ def offense?(ast_node)
68
+ if style == :symbol
69
+ ast_node.str_type?
70
+ else # string
71
+ ast_node.sym_type?
72
+ end
73
+ end
74
+
75
+ def new_checker(ast_node)
76
+ if style == :symbol
77
+ SymbolChecker.new(ast_node)
78
+ else # string
79
+ StringChecker.new(ast_node)
80
+ end
81
+ end
82
+
46
83
  # :nodoc:
47
- class Checker
84
+ class SymbolChecker
48
85
  MSG = 'Prefer %<prefer>s over `%<current>s` ' \
49
- 'to titleize shared examples.'
86
+ 'to symbolize shared examples.'
50
87
 
51
88
  attr_reader :node
52
89
 
@@ -55,22 +92,31 @@ module RuboCop
55
92
  end
56
93
 
57
94
  def message
58
- format(MSG, prefer: preferred_style, current: symbol.inspect)
95
+ format(MSG, prefer: preferred_style, current: node.value.inspect)
59
96
  end
60
97
 
61
98
  def preferred_style
62
- string = symbol.to_s.tr('_', ' ')
63
- wrap_with_single_quotes(string)
99
+ ":#{node.value.to_s.downcase.tr(' ', '_')}"
64
100
  end
101
+ end
102
+
103
+ # :nodoc:
104
+ class StringChecker
105
+ MSG = 'Prefer %<prefer>s over `%<current>s` ' \
106
+ 'to titleize shared examples.'
65
107
 
66
- private
108
+ attr_reader :node
67
109
 
68
- def symbol
69
- node.value
110
+ def initialize(node)
111
+ @node = node
70
112
  end
71
113
 
72
- def wrap_with_single_quotes(string)
73
- "'#{string}'"
114
+ def message
115
+ format(MSG, prefer: preferred_style, current: node.value.inspect)
116
+ end
117
+
118
+ def preferred_style
119
+ "'#{node.value.to_s.tr('_', ' ')}'"
74
120
  end
75
121
  end
76
122
  end
@@ -24,7 +24,7 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = %i[receive_message_chain stub_chain].freeze
25
25
 
26
26
  # @!method message_chain(node)
27
- def_node_matcher :message_chain, <<-PATTERN
27
+ def_node_matcher :message_chain, <<~PATTERN
28
28
  (send _ {:receive_message_chain :stub_chain} $_)
29
29
  PATTERN
30
30
 
@@ -23,7 +23,8 @@ module RuboCop
23
23
 
24
24
  MSG = 'Sort metadata alphabetically.'
25
25
 
26
- def on_metadata(symbols, pairs)
26
+ def on_metadata(symbols, hash)
27
+ pairs = hash&.pairs || []
27
28
  return if sorted?(symbols, pairs)
28
29
 
29
30
  crime_scene = crime_scene(symbols, pairs)