rubocop-rspec 2.11.1 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/config/default.yml +15 -0
  4. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +77 -0
  5. data/lib/rubocop/cop/rspec/change_by_zero.rb +8 -3
  6. data/lib/rubocop/cop/rspec/described_class.rb +1 -1
  7. data/lib/rubocop/cop/rspec/empty_hook.rb +2 -4
  8. data/lib/rubocop/cop/rspec/example_length.rb +1 -1
  9. data/lib/rubocop/cop/rspec/example_without_description.rb +1 -1
  10. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -0
  11. data/lib/rubocop/cop/rspec/expect_change.rb +8 -8
  12. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +32 -9
  13. data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +1 -1
  14. data/lib/rubocop/cop/rspec/file_path.rb +2 -1
  15. data/lib/rubocop/cop/rspec/focus.rb +2 -4
  16. data/lib/rubocop/cop/rspec/hook_argument.rb +3 -3
  17. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  18. data/lib/rubocop/cop/rspec/it_behaves_like.rb +2 -2
  19. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  20. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
  21. data/lib/rubocop/cop/rspec/message_spies.rb +7 -1
  22. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +13 -4
  23. data/lib/rubocop/cop/rspec/multiple_expectations.rb +20 -0
  24. data/lib/rubocop/cop/rspec/not_to_not.rb +14 -1
  25. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +47 -0
  26. data/lib/rubocop/cop/rspec/return_from_stub.rb +11 -11
  27. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  28. data/lib/rubocop/cop/rspec/yield.rb +1 -1
  29. data/lib/rubocop/cop/rspec_cops.rb +2 -0
  30. data/lib/rubocop/rspec/node.rb +1 -1
  31. data/lib/rubocop/rspec/version.rb +1 -1
  32. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 741b2b87f823e6fab2b693b917568362674a686183a1d00e11fbc24fecf8ae53
4
- data.tar.gz: '089bcc60333425fe0469071f12b52f92671a442274b179f50c716fce1e0a1064'
3
+ metadata.gz: 06d6fd83af093abc4da74f5791334c364b86c8238610e53890f4b197f97ce1b6
4
+ data.tar.gz: 3e40ac4c4e15bee0892061da115755ad8a9cc872218b8cf2d8fe166ea40c6f83
5
5
  SHA512:
6
- metadata.gz: 4f32ff6a813bcb2b427f13541c519d1da616cd051b7fbfbf4d8100fecf4e120e6626271623bd35833555061ef0c19e858934e07c6a8ed72a24cade03b1a59fee
7
- data.tar.gz: 29929728390c1ff9d6804c5babb614649966a60faccacc140f626e4eb78154eff39bb91fc38162fcfcdcdcbbb9593bdad8256f8471867e0dcb5b00abc047c544
6
+ metadata.gz: d84bfa56ac9503ad57e0dea9e5207469a3acf8d086e1ef0bf3f8239a9a704f4ce40e7ce82e10596699d948217ff55347098e0421af042e3653e6e3ea0b7e3593
7
+ data.tar.gz: c643ac0b7e06972fb90347dff8684b4c94e08ca3e9a85a5406bf87c3ce6f88f4395e878dce7fae224049127cc53dcb4639e6fb5d5d6134da9fd4dff6013480ca
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.12.0 (2022-07-02)
6
+
7
+ * Fix incorrect path suggested by `RSpec/FilePath` cop when second argument contains spaces. ([@tejasbubane][])
8
+ * Fix autocorrect for EmptyLineSeparation. ([@johnny-miyake][])
9
+ * Add new `RSpec/Capybara/SpecificMatcher` cop. ([@ydah][])
10
+ * Fixed false offense detection in `FactoryBot/CreateList` when a n.times block is including method calls in the factory create arguments. ([@ngouy][])
11
+ * Fix error in `RSpec/RSpec/FactoryBot/CreateList` cop for empty block. ([@tejasbubane][])
12
+ * Update `RSpec/MultipleExpectations` cop documentation with examples of aggregate_failures use. ([@edgibbs][])
13
+ * Declare autocorrect as unsafe for `RSpec/VerifiedDoubleReference`. ([@Drowze][])
14
+ * Add new `RSpec/Rails/HaveHttpStatus` cop. ([@akiomik][])
15
+
5
16
  ## 2.11.1 (2022-05-18)
6
17
 
7
18
  * Fix a regression in `RSpec/ExpectChange` flagging chained method calls. ([@pirj][])
@@ -694,3 +705,8 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
694
705
  [@t3h2mas]: https://github.com/t3h2mas
695
706
  [@M-Yamashita01]: https://github.com/M-Yamashita01
696
707
  [@luke-hill]: https://github.com/luke-hill
708
+ [@johnny-miyake]: https://github.com/johnny-miyake
709
+ [@ngouy]: https://github.com/ngouy
710
+ [@edgibbs]: https://github.com/edgibbs
711
+ [@Drowze]: https://github.com/Drowze
712
+ [@akiomik]: https://github.com/akiomik
data/config/default.yml CHANGED
@@ -783,11 +783,13 @@ RSpec/VariableName:
783
783
  RSpec/VerifiedDoubleReference:
784
784
  Description: Checks for consistent verified double reference style.
785
785
  Enabled: pending
786
+ SafeAutoCorrect: false
786
787
  EnforcedStyle: constant
787
788
  SupportedStyles:
788
789
  - constant
789
790
  - string
790
791
  VersionAdded: 2.10.0
792
+ VersionChanged: '2.12'
791
793
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VerifiedDoubleReference
792
794
 
793
795
  RSpec/VerifiedDoubles:
@@ -832,6 +834,12 @@ RSpec/Capybara/FeatureMethods:
832
834
  VersionChanged: '2.0'
833
835
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
834
836
 
837
+ RSpec/Capybara/SpecificMatcher:
838
+ Description: Checks for there is a more specific matcher offered by Capybara.
839
+ Enabled: pending
840
+ VersionAdded: '2.12'
841
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificMatcher
842
+
835
843
  RSpec/Capybara/VisibilityMatcher:
836
844
  Description: Checks for boolean visibility in Capybara finders.
837
845
  Enabled: true
@@ -901,6 +909,13 @@ RSpec/Rails/AvoidSetupHook:
901
909
  VersionAdded: '2.4'
902
910
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/AvoidSetupHook
903
911
 
912
+ RSpec/Rails/HaveHttpStatus:
913
+ Description: Checks that tests use `have_http_status` instead of equality matchers.
914
+ Enabled: pending
915
+ SafeAutoCorrect: false
916
+ VersionAdded: '2.12'
917
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
918
+
904
919
  RSpec/Rails/HttpStatus:
905
920
  Description: Enforces use of symbolic or numeric value to describe HTTP status.
906
921
  Enabled: true
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module Capybara
7
+ # Checks for there is a more specific matcher offered by Capybara.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # expect(page).to have_selector('button')
13
+ # expect(page).to have_no_selector('button.cls')
14
+ # expect(page).to have_css('button')
15
+ # expect(page).to have_no_css('a.cls', exact_text: 'foo')
16
+ # expect(page).to have_css('table.cls')
17
+ # expect(page).to have_css('select')
18
+ #
19
+ # # good
20
+ # expect(page).to have_button
21
+ # expect(page).to have_no_button(class: 'cls')
22
+ # expect(page).to have_button
23
+ # expect(page).to have_no_link('foo', class: 'cls')
24
+ # expect(page).to have_table(class: 'cls')
25
+ # expect(page).to have_select
26
+ #
27
+ class SpecificMatcher < Base
28
+ MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
29
+ RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
30
+ have_no_css].freeze
31
+ SPECIFIC_MATCHER = {
32
+ 'button' => 'button',
33
+ 'a' => 'link',
34
+ 'table' => 'table',
35
+ 'select' => 'select'
36
+ }.freeze
37
+
38
+ # @!method first_argument(node)
39
+ def_node_matcher :first_argument, <<-PATTERN
40
+ (send nil? _ (str $_) ... )
41
+ PATTERN
42
+
43
+ def on_send(node)
44
+ return unless (arg = first_argument(node))
45
+ return unless (matcher = specific_matcher(arg))
46
+ return if acceptable_pattern?(arg)
47
+
48
+ add_offense(node, message: message(node, matcher))
49
+ end
50
+
51
+ private
52
+
53
+ def specific_matcher(arg)
54
+ splitted_arg = arg[/^\w+/, 0]
55
+ SPECIFIC_MATCHER[splitted_arg]
56
+ end
57
+
58
+ def acceptable_pattern?(arg)
59
+ arg.match?(/\[.+=\w+\]/)
60
+ end
61
+
62
+ def message(node, matcher)
63
+ format(MSG,
64
+ good_matcher: good_matcher(node, matcher),
65
+ bad_matcher: node.method_name)
66
+ end
67
+
68
+ def good_matcher(node, matcher)
69
+ node.method_name
70
+ .to_s
71
+ .gsub(/selector|css/, matcher.to_s)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -9,6 +9,8 @@ module RuboCop
9
9
  # # bad
10
10
  # expect { run }.to change(Foo, :bar).by(0)
11
11
  # expect { run }.to change { Foo.bar }.by(0)
12
+ #
13
+ # # bad - compound expectations
12
14
  # expect { run }
13
15
  # .to change(Foo, :bar).by(0)
14
16
  # .and change(Foo, :baz).by(0)
@@ -19,6 +21,9 @@ module RuboCop
19
21
  # # good
20
22
  # expect { run }.not_to change(Foo, :bar)
21
23
  # expect { run }.not_to change { Foo.bar }
24
+ #
25
+ # # good - compound expectations
26
+ # define_negated_matcher :not_change, :change
22
27
  # expect { run }
23
28
  # .to not_change(Foo, :bar)
24
29
  # .and not_change(Foo, :baz)
@@ -52,17 +57,17 @@ module RuboCop
52
57
 
53
58
  def on_send(node)
54
59
  expect_change_with_arguments(node.parent) do
55
- check_offence(node.parent)
60
+ check_offense(node.parent)
56
61
  end
57
62
 
58
63
  expect_change_with_block(node.parent.parent) do
59
- check_offence(node.parent.parent)
64
+ check_offense(node.parent.parent)
60
65
  end
61
66
  end
62
67
 
63
68
  private
64
69
 
65
- def check_offence(node)
70
+ def check_offense(node)
66
71
  expression = node.loc.expression
67
72
  if compound_expectations?(node)
68
73
  add_offense(expression, message: MSG_COMPOUND)
@@ -11,7 +11,7 @@ module RuboCop
11
11
  # This cop can be configured using the `EnforcedStyle` and `SkipBlocks`
12
12
  # options.
13
13
  #
14
- # @example `EnforcedStyle: described_class`
14
+ # @example `EnforcedStyle: described_class` (default)
15
15
  # # bad
16
16
  # describe MyClass do
17
17
  # subject { MyClass.do_something }
@@ -36,11 +36,9 @@ module RuboCop
36
36
  def on_block(node)
37
37
  empty_hook?(node) do |hook|
38
38
  add_offense(hook) do |corrector|
39
- range = range_with_surrounding_space(
40
- range: node.loc.expression,
41
- side: :left
39
+ corrector.remove(
40
+ range_with_surrounding_space(node.loc.expression, side: :left)
42
41
  )
43
- corrector.remove(range)
44
42
  end
45
43
  end
46
44
  end
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for long examples.
7
7
  #
8
8
  # A long example is usually more difficult to understand. Consider
9
- # extracting out some behaviour, e.g. with a `let` block, or a helper
9
+ # extracting out some behavior, e.g. with a `let` block, or a helper
10
10
  # method.
11
11
  #
12
12
  # @example
@@ -14,7 +14,7 @@ module RuboCop
14
14
  #
15
15
  # This cop can be configured using the `EnforcedStyle` option
16
16
  #
17
- # @example `EnforcedStyle: always_allow`
17
+ # @example `EnforcedStyle: always_allow` (default)
18
18
  # # bad
19
19
  # it('') { is_expected.to be_good }
20
20
  # it '' do
@@ -5,6 +5,8 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for `expect(...)` calls containing literal values.
7
7
  #
8
+ # Autocorrection is performed when the expected is not a literal.
9
+ #
8
10
  # @example
9
11
  # # bad
10
12
  # expect(5).to eq(price)
@@ -10,14 +10,7 @@ module RuboCop
10
10
  #
11
11
  # This cop can be configured using the `EnforcedStyle` option.
12
12
  #
13
- # @example `EnforcedStyle: block`
14
- # # bad
15
- # expect { run }.to change(Foo, :bar)
16
- #
17
- # # good
18
- # expect { run }.to change { Foo.bar }
19
- #
20
- # @example `EnforcedStyle: method_call`
13
+ # @example `EnforcedStyle: method_call` (default)
21
14
  # # bad
22
15
  # expect { run }.to change { Foo.bar }
23
16
  # expect { run }.to change { foo.baz }
@@ -29,6 +22,13 @@ module RuboCop
29
22
  # expect { run }.to change { Foo.bar(:count) }
30
23
  # expect { run }.to change { user.reload.name }
31
24
  #
25
+ # @example `EnforcedStyle: block`
26
+ # # bad
27
+ # expect { run }.to change(Foo, :bar)
28
+ #
29
+ # # good
30
+ # expect { run }.to change { Foo.bar }
31
+ #
32
32
  class ExpectChange < Base
33
33
  extend AutoCorrector
34
34
  include ConfigurableEnforcedStyle
@@ -8,15 +8,21 @@ module RuboCop
8
8
  #
9
9
  # This cop can be configured using the `EnforcedStyle` option
10
10
  #
11
- # @example `EnforcedStyle: create_list`
11
+ # @example `EnforcedStyle: create_list` (default)
12
12
  # # bad
13
13
  # 3.times { create :user }
14
14
  #
15
15
  # # good
16
16
  # create_list :user, 3
17
17
  #
18
- # # good
19
- # 3.times { |n| create :user, created_at: n.months.ago }
18
+ # # bad
19
+ # 3.times { create :user, age: 18 }
20
+ #
21
+ # # good - index is used to alter the created models attributes
22
+ # 3.times { |n| create :user, age: n }
23
+ #
24
+ # # good - contains a method call, may return different values
25
+ # 3.times { create :user, age: rand }
20
26
  #
21
27
  # @example `EnforcedStyle: n_times`
22
28
  # # bad
@@ -33,15 +39,28 @@ module RuboCop
33
39
  MSG_N_TIMES = 'Prefer %<number>s.times.'
34
40
  RESTRICT_ON_SEND = %i[create_list].freeze
35
41
 
36
- # @!method n_times_block_without_arg?(node)
37
- def_node_matcher :n_times_block_without_arg?, <<-PATTERN
42
+ # @!method n_times_block?(node)
43
+ def_node_matcher :n_times_block?, <<-PATTERN
38
44
  (block
39
45
  (send (int _) :times)
40
- (args)
41
46
  ...
42
47
  )
43
48
  PATTERN
44
49
 
50
+ # @!method n_times_block_with_arg_and_used?(node)
51
+ def_node_matcher :n_times_block_with_arg_and_used?, <<-PATTERN
52
+ (block
53
+ (send (int _) :times)
54
+ (args (arg _value))
55
+ `_value
56
+ )
57
+ PATTERN
58
+
59
+ # @!method arguments_include_method_call?(node)
60
+ def_node_matcher :arguments_include_method_call?, <<-PATTERN
61
+ (send ${nil? #factory_bot?} :create (sym $_) `$(send ...))
62
+ PATTERN
63
+
45
64
  # @!method factory_call(node)
46
65
  def_node_matcher :factory_call, <<-PATTERN
47
66
  (send ${nil? #factory_bot?} :create (sym $_) $...)
@@ -54,7 +73,11 @@ module RuboCop
54
73
 
55
74
  def on_block(node)
56
75
  return unless style == :create_list
57
- return unless n_times_block_without_arg?(node)
76
+
77
+ return unless n_times_block?(node)
78
+ return if n_times_block_with_arg_and_used?(node)
79
+ return unless node.body
80
+ return if arguments_include_method_call?(node.body)
58
81
  return unless contains_only_factory?(node.body)
59
82
 
60
83
  add_offense(node.send_node, message: MSG_CREATE_LIST) do |corrector|
@@ -193,7 +216,7 @@ module RuboCop
193
216
  if node.body.begin_type?
194
217
  format_multiline_block(node)
195
218
  else
196
- format_singeline_block(node)
219
+ format_singleline_block(node)
197
220
  end
198
221
  end
199
222
 
@@ -205,7 +228,7 @@ module RuboCop
205
228
  "#{indent_end}end"
206
229
  end
207
230
 
208
- def format_singeline_block(node)
231
+ def format_singleline_block(node)
209
232
  " { #{node.arguments.source} #{node.body.source} }"
210
233
  end
211
234
  end
@@ -7,7 +7,7 @@ module RuboCop
7
7
  # Use shorthands from `FactoryBot::Syntax::Methods` in your specs.
8
8
  #
9
9
  # @safety
10
- # The auto-correction is marked as unsafe because the cop
10
+ # The autocorrection is marked as unsafe because the cop
11
11
  # cannot verify whether you already include
12
12
  # `FactoryBot::Syntax::Methods` in your test suite.
13
13
  #
@@ -117,8 +117,9 @@ module RuboCop
117
117
 
118
118
  def name_pattern(method_name)
119
119
  return unless method_name&.str_type?
120
+ return if ignore_methods?
120
121
 
121
- ".*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
122
+ ".*#{method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')}"
122
123
  end
123
124
 
124
125
  def expected_path(constant)
@@ -69,10 +69,8 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def with_surrounding(focus)
72
- range_with_space = range_with_surrounding_space(
73
- range: focus.loc.expression,
74
- side: :left
75
- )
72
+ range_with_space =
73
+ range_with_surrounding_space(focus.loc.expression, side: :left)
76
74
 
77
75
  range_with_surrounding_comma(range_with_space, :left)
78
76
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # styles: "implicit", "each", and "example." All styles have
11
11
  # the same behavior.
12
12
  #
13
- # @example when configuration is `EnforcedStyle: implicit`
13
+ # @example `EnforcedStyle: implicit` (default)
14
14
  # # bad
15
15
  # before(:each) do
16
16
  # # ...
@@ -26,7 +26,7 @@ module RuboCop
26
26
  # # ...
27
27
  # end
28
28
  #
29
- # @example when configuration is `EnforcedStyle: each`
29
+ # @example `EnforcedStyle: each`
30
30
  # # bad
31
31
  # before(:example) do
32
32
  # # ...
@@ -42,7 +42,7 @@ module RuboCop
42
42
  # # ...
43
43
  # end
44
44
  #
45
- # @example when configuration is `EnforcedStyle: example`
45
+ # @example `EnforcedStyle: example`
46
46
  # # bad
47
47
  # before(:each) do
48
48
  # # ...
@@ -8,7 +8,7 @@ module RuboCop
8
8
  # This cop can be configured using the `EnforcedStyle` option
9
9
  # and supports the `--auto-gen-config` flag.
10
10
  #
11
- # @example `EnforcedStyle: is_expected`
11
+ # @example `EnforcedStyle: is_expected` (default)
12
12
  #
13
13
  # # bad
14
14
  # it { should be_truthy }
@@ -5,14 +5,14 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks that only one `it_behaves_like` style is used.
7
7
  #
8
- # @example when configuration is `EnforcedStyle: it_behaves_like`
8
+ # @example `EnforcedStyle: it_behaves_like` (default)
9
9
  # # bad
10
10
  # it_should_behave_like 'a foo'
11
11
  #
12
12
  # # good
13
13
  # it_behaves_like 'a foo'
14
14
  #
15
- # @example when configuration is `EnforcedStyle: it_should_behave_like`
15
+ # @example `EnforcedStyle: it_should_behave_like`
16
16
  # # bad
17
17
  # it_behaves_like 'a foo'
18
18
  #
@@ -10,7 +10,7 @@ module RuboCop
10
10
  #
11
11
  # If several examples may define a `DummyClass`, instead of being a
12
12
  # blank slate class as it will be in the first example, subsequent
13
- # examples will be reopening it and modifying its behaviour in
13
+ # examples will be reopening it and modifying its behavior in
14
14
  # unpredictable ways.
15
15
  # Even worse when a class that exists in the codebase is reopened.
16
16
  #
@@ -8,7 +8,7 @@ module RuboCop
8
8
  # This cop can be configured in your configuration using the
9
9
  # `EnforcedStyle` option and supports `--auto-gen-config`.
10
10
  #
11
- # @example `EnforcedStyle: allow`
11
+ # @example `EnforcedStyle: allow` (default)
12
12
  #
13
13
  # # bad
14
14
  # expect(foo).to receive(:bar)
@@ -8,21 +8,27 @@ module RuboCop
8
8
  # This cop can be configured in your configuration using the
9
9
  # `EnforcedStyle` option and supports `--auto-gen-config`.
10
10
  #
11
- # @example `EnforcedStyle: have_received`
11
+ # @example `EnforcedStyle: have_received` (default)
12
12
  #
13
13
  # # bad
14
14
  # expect(foo).to receive(:bar)
15
+ # do_something
15
16
  #
16
17
  # # good
18
+ # allow(foo).to receive(:bar) # or use instance_spy
19
+ # do_something
17
20
  # expect(foo).to have_received(:bar)
18
21
  #
19
22
  # @example `EnforcedStyle: receive`
20
23
  #
21
24
  # # bad
25
+ # allow(foo).to receive(:bar)
26
+ # do_something
22
27
  # expect(foo).to have_received(:bar)
23
28
  #
24
29
  # # good
25
30
  # expect(foo).to receive(:bar)
31
+ # do_something
26
32
  #
27
33
  class MessageSpies < Base
28
34
  include ConfigurableEnforcedStyle
@@ -4,7 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  # Helps determine the offending location if there is not an empty line
7
- # following the node. Allows comments to follow directly after.
7
+ # following the node. Allows comments to follow directly after
8
+ # in the following cases.
9
+ # - `rubocop:enable` directive
10
+ # - followed by empty line(s)
8
11
  module EmptyLineSeparation
9
12
  include FinalEndLocation
10
13
  include RangeHelp
@@ -21,13 +24,19 @@ module RuboCop
21
24
  end
22
25
 
23
26
  def missing_separating_line(node)
24
- line = final_end_location(node).line
27
+ line = final_end_line = final_end_location(node).line
25
28
 
26
- line += 1 while comment_line?(processed_source[line])
29
+ while comment_line?(processed_source[line])
30
+ line += 1
31
+ comment = processed_source.comment_at_line(line)
32
+ if DirectiveComment.new(comment).enabled?
33
+ enable_directive_line = line
34
+ end
35
+ end
27
36
 
28
37
  return if processed_source[line].blank?
29
38
 
30
- yield offending_loc(line)
39
+ yield offending_loc(enable_directive_line || final_end_line)
31
40
  end
32
41
 
33
42
  def offending_loc(last_line)
@@ -31,6 +31,26 @@ module RuboCop
31
31
  # end
32
32
  # end
33
33
  #
34
+ # @example `aggregate_failures: true` (default)
35
+ #
36
+ # # good - the cop ignores when RSpec aggregates failures
37
+ # describe UserCreator do
38
+ # it 'builds a user', :aggregate_failures do
39
+ # expect(user.name).to eq("John")
40
+ # expect(user.age).to eq(22)
41
+ # end
42
+ # end
43
+ #
44
+ # @example `aggregate_failures: false`
45
+ #
46
+ # # Detected as an offense
47
+ # describe UserCreator do
48
+ # it 'builds a user', aggregate_failures: false do
49
+ # expect(user.name).to eq("John")
50
+ # expect(user.age).to eq(22)
51
+ # end
52
+ # end
53
+ #
34
54
  # @example configuration
35
55
  #
36
56
  # # .rubocop.yml
@@ -5,7 +5,8 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for consistent method usage for negating expectations.
7
7
  #
8
- # @example
8
+ # @example `EnforcedStyle: not_to` (default)
9
+ #
9
10
  # # bad
10
11
  # it '...' do
11
12
  # expect(false).to_not be_true
@@ -15,6 +16,18 @@ module RuboCop
15
16
  # it '...' do
16
17
  # expect(false).not_to be_true
17
18
  # end
19
+ #
20
+ # @example `EnforcedStyle: to_not`
21
+ #
22
+ # # bad
23
+ # it '...' do
24
+ # expect(false).not_to be_true
25
+ # end
26
+ #
27
+ # # good
28
+ # it '...' do
29
+ # expect(false).to_not be_true
30
+ # end
18
31
  class NotToNot < Base
19
32
  extend AutoCorrector
20
33
  include ConfigurableEnforcedStyle
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module Rails
7
+ # Checks that tests use `have_http_status` instead of equality matchers.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # expect(response.status).to be(200)
12
+ #
13
+ # # good
14
+ # expect(response).to have_http_status(200)
15
+ #
16
+ class HaveHttpStatus < Base
17
+ extend AutoCorrector
18
+
19
+ MSG =
20
+ 'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
21
+ 'over `expect(response.status).%<to>s %<match>s`.'
22
+
23
+ # @!method match_status(node)
24
+ def_node_matcher :match_status, <<-PATTERN
25
+ (send
26
+ (send nil? :expect
27
+ $(send (send nil? :response) :status)
28
+ )
29
+ $#Runners.all
30
+ $(send nil? {:be :eq :eql :equal} (int $_))
31
+ )
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ match_status(node) do |response_status, to, match, status|
36
+ message = format(MSG, to: to, match: match.source, status: status)
37
+ add_offense(node, message: message) do |corrector|
38
+ corrector.replace(response_status.source_range, 'response')
39
+ corrector.replace(match.loc.selector, 'have_http_status')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -11,6 +11,17 @@ module RuboCop
11
11
  #
12
12
  # This cop can be configured using the `EnforcedStyle` option
13
13
  #
14
+ # @example `EnforcedStyle: and_return` (default)
15
+ # # bad
16
+ # allow(Foo).to receive(:bar) { "baz" }
17
+ # expect(Foo).to receive(:bar) { "baz" }
18
+ #
19
+ # # good
20
+ # allow(Foo).to receive(:bar).and_return("baz")
21
+ # expect(Foo).to receive(:bar).and_return("baz")
22
+ # # also good as the returned value is dynamic
23
+ # allow(Foo).to receive(:bar) { bar.baz }
24
+ #
14
25
  # @example `EnforcedStyle: block`
15
26
  # # bad
16
27
  # allow(Foo).to receive(:bar).and_return("baz")
@@ -22,17 +33,6 @@ module RuboCop
22
33
  # # also good as the returned value is dynamic
23
34
  # allow(Foo).to receive(:bar).and_return(bar.baz)
24
35
  #
25
- # @example `EnforcedStyle: and_return`
26
- # # bad
27
- # allow(Foo).to receive(:bar) { "baz" }
28
- # expect(Foo).to receive(:bar) { "baz" }
29
- #
30
- # # good
31
- # allow(Foo).to receive(:bar).and_return("baz")
32
- # expect(Foo).to receive(:bar).and_return("baz")
33
- # # also good as the returned value is dynamic
34
- # allow(Foo).to receive(:bar) { bar.baz }
35
- #
36
36
  class ReturnFromStub < Base
37
37
  extend AutoCorrector
38
38
  include ConfigurableEnforcedStyle
@@ -35,7 +35,7 @@ module RuboCop
35
35
  occurrences.each do |occurrence|
36
36
  lines_except_current = lines - [occurrence.first_line]
37
37
  message = format(MSG, hook_name: occurrences.first.method_name,
38
- lines: lines_msg(lines_except_current))
38
+ lines: lines_msg(lines_except_current))
39
39
  add_offense(occurrence, message: message)
40
40
  end
41
41
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  def autocorrect(corrector, node, range)
46
46
  corrector.replace(
47
- range_with_surrounding_space(range: range, side: :left),
47
+ range_with_surrounding_space(range, side: :left),
48
48
  generate_replacement(node.body)
49
49
  )
50
50
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'rspec/capybara/current_path_expectation'
4
4
  require_relative 'rspec/capybara/feature_methods'
5
+ require_relative 'rspec/capybara/specific_matcher'
5
6
  require_relative 'rspec/capybara/visibility_matcher'
6
7
 
7
8
  require_relative 'rspec/factory_bot/attribute_defined_statically'
@@ -10,6 +11,7 @@ require_relative 'rspec/factory_bot/factory_class_name'
10
11
  require_relative 'rspec/factory_bot/syntax_methods'
11
12
 
12
13
  require_relative 'rspec/rails/avoid_setup_hook'
14
+ require_relative 'rspec/rails/have_http_status'
13
15
  begin
14
16
  require_relative 'rspec/rails/http_status'
15
17
  rescue LoadError
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # RuboCop RSpec specific extensions of RuboCop::AST::Node
6
6
  module Node
7
- # In various cops we want to regard const as literal althought it's not
7
+ # In various cops we want to regard const as literal although it's not
8
8
  # strictly literal.
9
9
  def recursive_literal_or_const?
10
10
  case type
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '2.11.1'
7
+ STRING = '2.12.0'
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.1
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Backus
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-05-18 00:00:00.000000000 Z
13
+ date: 2022-07-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: '1.19'
21
+ version: '1.31'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: '1.19'
28
+ version: '1.31'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rack
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -141,6 +141,7 @@ files:
141
141
  - lib/rubocop/cop/rspec/before_after_all.rb
142
142
  - lib/rubocop/cop/rspec/capybara/current_path_expectation.rb
143
143
  - lib/rubocop/cop/rspec/capybara/feature_methods.rb
144
+ - lib/rubocop/cop/rspec/capybara/specific_matcher.rb
144
145
  - lib/rubocop/cop/rspec/capybara/visibility_matcher.rb
145
146
  - lib/rubocop/cop/rspec/change_by_zero.rb
146
147
  - lib/rubocop/cop/rspec/context_method.rb
@@ -207,6 +208,7 @@ files:
207
208
  - lib/rubocop/cop/rspec/pending.rb
208
209
  - lib/rubocop/cop/rspec/predicate_matcher.rb
209
210
  - lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb
211
+ - lib/rubocop/cop/rspec/rails/have_http_status.rb
210
212
  - lib/rubocop/cop/rspec/rails/http_status.rb
211
213
  - lib/rubocop/cop/rspec/receive_counts.rb
212
214
  - lib/rubocop/cop/rspec/receive_never.rb