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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db6a354c4b2a3ba18944c5a6fc3ce076eaec380b3f84b67d3a825ab81a2f18a2
4
- data.tar.gz: c15d5bb4a019e0ff6d419058e136959aa54af22a409e302c4cbd012f06960ba6
3
+ metadata.gz: 63bb2b52f05acc607af023c9e7afa5375d5815cf026b73fda3dc10c8648806d2
4
+ data.tar.gz: c0bf3717a359f99ec9dd9519f5897c95dcc3e35f592baaad3dc037f1ae25c7d8
5
5
  SHA512:
6
- metadata.gz: cd09459ea1199299104e462a6592ffe6ecba612f6560e4f5003c13c302cf47a9b21b5da186cb7237378c2187f83c63894fb0a89780ce9c22d328b4cfb9795c81
7
- data.tar.gz: 2f24de47526a5719c96772d5c4e557fc37d2cc39472c372e0c3c6d4913a47cee1568bb06547de1d67684d1afd60b8875c7073e162b8e637e5718b4b4f8972839
6
+ metadata.gz: 4af0c95c0baf2a8dc469767c5455105276ea42f1c66ffadd09760e40eeecd0d362afcc7886b77931f0eafcc3a0785f0126ab4a3924ac4a07b1a8d94344c26d7a
7
+ data.tar.gz: 3712863da50c7d61207b76a92002432469e7021a87dcc1a4af1a8779581fc6a7b8beb9fc2e2017b8a45adace69f3e7a5dc6f9e9e2d47ca067c84df576cb8919d
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 1.22.0 (2018-01-10)
6
+
7
+ * Updates `describe_class` to account for RSpecs `:system` wrapper of rails system tests. ([@EliseFitz15][])
8
+ * Add `RSpec/ExpectChange` cop to enforce consistent usage of the change matcher. ([@Darhazer][])
9
+ * Add autocorrect support to `RSpec/LetBeforeExamples`. ([@Darhazer][])
10
+ * Fix `RSpec/InstanceVariable` flagging instance variables inside dynamically defined class. ([@Darhazer][])
11
+ * Add autocorrect support for `RSpec/ReturnFromStub` cop. ([@bquorning][])
12
+ * Add `RSpec/ExampleWithoutDescription` cop. ([@Darhazer][])
13
+
5
14
  ## 1.21.0 (2017-12-13)
6
15
 
7
16
  * Compatibility with RuboCop v0.52.0. ([@bquorning][])
@@ -280,3 +289,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
280
289
  [@walf443]: https://github.com/walf443
281
290
  [@pirj]: https://github.com/pirj
282
291
  [@telmofcosta]: https://github.com/telmofcosta
292
+ [@EliseFitz15]: https://github.com/EliseFitz15
data/Rakefile CHANGED
@@ -45,3 +45,22 @@ task confirm_config: :build_config do
45
45
  end
46
46
 
47
47
  task default: %i[build_config coverage internal_investigation confirm_config]
48
+
49
+ desc 'Generate a new cop template'
50
+ task :new_cop, [:cop] do |_task, args|
51
+ require 'rubocop'
52
+
53
+ cop_name = args.fetch(:cop) do
54
+ warn 'usage: bundle exec rake new_cop[Department/Name]'
55
+ exit!
56
+ end
57
+
58
+ generator = RuboCop::Cop::Generator.new(cop_name)
59
+
60
+ generator.write_source
61
+ generator.write_spec
62
+ generator.inject_require(root_file_path: 'lib/rubocop/cop/rspec_cops.rb')
63
+ generator.inject_config(config_file_path: 'config/default.yml')
64
+
65
+ puts generator.todo
66
+ end
@@ -104,6 +104,16 @@ RSpec/ExampleLength:
104
104
  Max: 5
105
105
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleLength
106
106
 
107
+ RSpec/ExampleWithoutDescription:
108
+ Description: Checks for examples without a description.
109
+ Enabled: true
110
+ EnforcedStyle: always_allow
111
+ SupportedStyles:
112
+ - always_allow
113
+ - single_line_only
114
+ - disallow
115
+ StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWithoutDescription
116
+
107
117
  RSpec/ExampleWording:
108
118
  Description: Checks for common mistakes in example descriptions.
109
119
  Enabled: true
@@ -122,6 +132,15 @@ RSpec/ExpectActual:
122
132
  - spec/routing/**/*
123
133
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectActual
124
134
 
135
+ RSpec/ExpectChange:
136
+ Description: Checks for consistent style of change matcher.
137
+ Enabled: true
138
+ EnforcedStyle: method_call
139
+ SupportedStyles:
140
+ - method_call
141
+ - block
142
+ StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectChange
143
+
125
144
  RSpec/ExpectInHook:
126
145
  Enabled: true
127
146
  Description: Do not use `expect` in hooks such as `before`.
@@ -3,81 +3,26 @@ require 'yaml'
3
3
 
4
4
  require 'rubocop'
5
5
 
6
- require 'rubocop/rspec'
7
- require 'rubocop/rspec/version'
8
- require 'rubocop/rspec/inject'
9
- require 'rubocop/rspec/top_level_describe'
10
- require 'rubocop/rspec/wording'
11
- require 'rubocop/rspec/util'
12
- require 'rubocop/rspec/language'
13
- require 'rubocop/rspec/language/node_pattern'
14
- require 'rubocop/rspec/concept'
15
- require 'rubocop/rspec/example_group'
16
- require 'rubocop/rspec/example'
17
- require 'rubocop/rspec/hook'
18
- require 'rubocop/cop/rspec/cop'
19
- require 'rubocop/rspec/align_let_brace'
20
- require 'rubocop/rspec/capybara'
21
- require 'rubocop/rspec/factory_bot'
6
+ require_relative 'rubocop/rspec'
7
+ require_relative 'rubocop/rspec/version'
8
+ require_relative 'rubocop/rspec/inject'
9
+ require_relative 'rubocop/rspec/top_level_describe'
10
+ require_relative 'rubocop/rspec/wording'
11
+ require_relative 'rubocop/rspec/util'
12
+ require_relative 'rubocop/rspec/language'
13
+ require_relative 'rubocop/rspec/language/node_pattern'
14
+ require_relative 'rubocop/rspec/concept'
15
+ require_relative 'rubocop/rspec/example_group'
16
+ require_relative 'rubocop/rspec/example'
17
+ require_relative 'rubocop/rspec/hook'
18
+ require_relative 'rubocop/cop/rspec/cop'
19
+ require_relative 'rubocop/rspec/align_let_brace'
20
+ require_relative 'rubocop/rspec/capybara'
21
+ require_relative 'rubocop/rspec/factory_bot'
22
22
 
23
23
  RuboCop::RSpec::Inject.defaults!
24
24
 
25
- # cops
26
- require 'rubocop/cop/rspec/align_left_let_brace'
27
- require 'rubocop/cop/rspec/align_right_let_brace'
28
- require 'rubocop/cop/rspec/any_instance'
29
- require 'rubocop/cop/rspec/around_block'
30
- require 'rubocop/cop/rspec/be_eql'
31
- require 'rubocop/cop/rspec/before_after_all'
32
- require 'rubocop/cop/rspec/capybara/current_path_expectation'
33
- require 'rubocop/cop/rspec/capybara/feature_methods'
34
- require 'rubocop/cop/rspec/context_wording'
35
- require 'rubocop/cop/rspec/describe_class'
36
- require 'rubocop/cop/rspec/describe_method'
37
- require 'rubocop/cop/rspec/describe_symbol'
38
- require 'rubocop/cop/rspec/described_class'
39
- require 'rubocop/cop/rspec/empty_example_group'
40
- require 'rubocop/cop/rspec/empty_line_after_final_let'
41
- require 'rubocop/cop/rspec/empty_line_after_subject'
42
- require 'rubocop/cop/rspec/example_length'
43
- require 'rubocop/cop/rspec/example_wording'
44
- require 'rubocop/cop/rspec/expect_actual'
45
- require 'rubocop/cop/rspec/expect_in_hook'
46
- require 'rubocop/cop/rspec/expect_output'
47
- require 'rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically'
48
- require 'rubocop/cop/rspec/file_path'
49
- require 'rubocop/cop/rspec/focus'
50
- require 'rubocop/cop/rspec/hook_argument'
51
- require 'rubocop/cop/rspec/implicit_expect'
52
- require 'rubocop/cop/rspec/instance_spy'
53
- require 'rubocop/cop/rspec/instance_variable'
54
- require 'rubocop/cop/rspec/invalid_predicate_matcher'
55
- require 'rubocop/cop/rspec/it_behaves_like'
56
- require 'rubocop/cop/rspec/iterated_expectation'
57
- require 'rubocop/cop/rspec/leading_subject'
58
- require 'rubocop/cop/rspec/let_before_examples'
59
- require 'rubocop/cop/rspec/let_setup'
60
- require 'rubocop/cop/rspec/message_chain'
61
- require 'rubocop/cop/rspec/message_expectation'
62
- require 'rubocop/cop/rspec/message_spies'
63
- require 'rubocop/cop/rspec/multiple_describes'
64
- require 'rubocop/cop/rspec/multiple_expectations'
65
- require 'rubocop/cop/rspec/multiple_subjects'
66
- require 'rubocop/cop/rspec/named_subject'
67
- require 'rubocop/cop/rspec/nested_groups'
68
- require 'rubocop/cop/rspec/not_to_not'
69
- require 'rubocop/cop/rspec/overwriting_setup'
70
- require 'rubocop/cop/rspec/repeated_description'
71
- require 'rubocop/cop/rspec/repeated_example'
72
- require 'rubocop/cop/rspec/return_from_stub'
73
- require 'rubocop/cop/rspec/scattered_let'
74
- require 'rubocop/cop/rspec/scattered_setup'
75
- require 'rubocop/cop/rspec/shared_context'
76
- require 'rubocop/cop/rspec/single_argument_message_chain'
77
- require 'rubocop/cop/rspec/subject_stub'
78
- require 'rubocop/cop/rspec/predicate_matcher'
79
- require 'rubocop/cop/rspec/verified_doubles'
80
- require 'rubocop/cop/rspec/void_expect'
25
+ require_relative 'rubocop/cop/rspec_cops'
81
26
 
82
27
  # We have to register our autocorrect incompatibilies in RuboCop's cops as well
83
28
  # so we do not hit infinite loops
@@ -19,12 +19,12 @@ module RuboCop
19
19
  # @example
20
20
  # # bad
21
21
  # context 'the display name not present' do
22
- # ...
22
+ # # ...
23
23
  # end
24
24
  #
25
25
  # # good
26
26
  # context 'when the display name is not present' do
27
- # ...
27
+ # # ...
28
28
  # end
29
29
  class ContextWording < Cop
30
30
  MSG = 'Start context description with %<prefixes>s.'.freeze
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def_node_matcher :rails_metadata?, <<-PATTERN
37
37
  (pair
38
38
  (sym :type)
39
- (sym {:request :feature :routing :view}))
39
+ (sym {:request :feature :system :routing :view}))
40
40
  PATTERN
41
41
 
42
42
  def_node_matcher :shared_group?, <<-PATTERN
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # ...
17
17
  # end
18
18
  #
19
- # See https://github.com/rspec/rspec-core/issues/1610
19
+ # @see https://github.com/rspec/rspec-core/issues/1610
20
20
  class DescribeSymbol < Cop
21
21
  MSG = 'Avoid describing symbols.'.freeze
22
22
 
@@ -81,10 +81,9 @@ module RuboCop
81
81
  def find_usage(node, &block)
82
82
  yield(node) if offensive?(node)
83
83
 
84
- return unless node.is_a?(Parser::AST::Node)
85
84
  return if scope_change?(node) || node.const_type?
86
85
 
87
- node.children.each do |child|
86
+ node.each_child_node do |child|
88
87
  find_usage(child, &block)
89
88
  end
90
89
  end
@@ -116,8 +115,7 @@ module RuboCop
116
115
  if style == :described_class
117
116
  node.eql?(@described_class)
118
117
  else
119
- _receiver, method_name, *_args = *node
120
- method_name == :described_class
118
+ node.method_name == :described_class
121
119
  end
122
120
  end
123
121
  end
@@ -7,19 +7,15 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # let(:foo) { bar }
11
- # let(:something) { other }
12
- # it do
13
- # ...
14
- # end
10
+ # let(:foo) { bar }
11
+ # let(:something) { other }
12
+ # it { does_something }
15
13
  #
16
14
  # # good
17
- # let(:foo) { bar }
18
- # let(:something) { other }
15
+ # let(:foo) { bar }
16
+ # let(:something) { other }
19
17
  #
20
- # it do
21
- # ...
22
- # end
18
+ # it { does_something }
23
19
  class EmptyLineAfterFinalLet < Cop
24
20
  MSG = 'Add an empty line after the last `let` block.'.freeze
25
21
 
@@ -7,13 +7,13 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # subject(:obj) { described_class }
11
- # let(:foo) { bar }
10
+ # subject(:obj) { described_class }
11
+ # let(:foo) { bar }
12
12
  #
13
13
  # # good
14
- # subject(:obj) { described_class }
14
+ # subject(:obj) { described_class }
15
15
  #
16
- # let(:foo) { bar }
16
+ # let(:foo) { bar }
17
17
  class EmptyLineAfterSubject < Cop
18
18
  MSG = 'Add empty line after `subject`.'.freeze
19
19
 
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for examples without a description.
7
+ #
8
+ # RSpec allows for auto-generated example descriptions when there is no
9
+ # description provided or the description is an empty one.
10
+ #
11
+ # This cop removes empty descriptions.
12
+ # It also defines whether auto-generated description is allowed, based
13
+ # on the configured style.
14
+ #
15
+ # This cop can be configured using the `EnforcedStyle` option
16
+ #
17
+ # @example `EnforcedStyle: always_allow`
18
+ # # bad
19
+ # it('') { is_expected.to be_good }
20
+ # it '' do
21
+ # result = service.call
22
+ # expect(result).to be(true)
23
+ # end
24
+ #
25
+ # # good
26
+ # it { is_expected.to be_good }
27
+ # it do
28
+ # result = service.call
29
+ # expect(result).to be(true)
30
+ # end
31
+ #
32
+ # @example `EnforcedStyle: single_line_only`
33
+ # # bad
34
+ # it('') { is_expected.to be_good }
35
+ # it do
36
+ # result = service.call
37
+ # expect(result).to be(true)
38
+ # end
39
+ #
40
+ # # good
41
+ # it { is_expected.to be_good }
42
+ #
43
+ # @example `EnforcedStyle: disallow`
44
+ # # bad
45
+ # it { is_expected.to be_good }
46
+ # it do
47
+ # result = service.call
48
+ # expect(result).to be(true)
49
+ # end
50
+ class ExampleWithoutDescription < Cop
51
+ include ConfigurableEnforcedStyle
52
+
53
+ MSG_DEFAULT_ARGUMENT = 'Omit the argument when you want to ' \
54
+ 'have auto-generated description.'.freeze
55
+ MSG_ADD_DESCRIPTION = 'Add a description.'.freeze
56
+
57
+ def_node_matcher :example?, Examples::ALL.send_pattern
58
+ def_node_matcher :example_description, '(send nil? _ $(str $_))'
59
+
60
+ def on_send(node)
61
+ return unless example?(node)
62
+
63
+ check_example_without_description(node)
64
+
65
+ example_description(node) do |message_node, message|
66
+ return unless message.to_s.empty?
67
+
68
+ add_offense(message_node, message: MSG_DEFAULT_ARGUMENT)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def check_example_without_description(node)
75
+ _send, _method, arg = *node
76
+ return unless arg.nil?
77
+ return unless disallow_empty_description?(node)
78
+
79
+ add_offense(node, message: MSG_ADD_DESCRIPTION)
80
+ end
81
+
82
+ def disallow_empty_description?(node)
83
+ style == :disallow ||
84
+ (style == :single_line_only && node.parent.multiline?)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for consistent style of change matcher.
7
+ #
8
+ # Enforces either passing object and attribute as arguments to the matcher
9
+ # or passing a block that reads the attribute value.
10
+ #
11
+ # This cop can be configured using the `EnforcedStyle` option.
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`
21
+ # # bad
22
+ # expect(run).to change { Foo.bar }
23
+ # expect(run).to change { foo.baz }
24
+ #
25
+ # # good
26
+ # expect(run).to change(Foo, :bar)
27
+ # expect(run).to change(foo, :baz)
28
+ # # also good when there are arguments or chained method calls
29
+ # expect(run).to change { Foo.bar(:count) }
30
+ # expect(run).to change { user.reload.name }
31
+ #
32
+ class ExpectChange < Cop
33
+ include ConfigurableEnforcedStyle
34
+
35
+ MSG_BLOCK = 'Prefer `change(%<obj>s, :%<attr>s)`.'.freeze
36
+ MSG_CALL = 'Prefer `change { %<obj>s.%<attr>s }`.'.freeze
37
+
38
+ def_node_matcher :expect_change_with_arguments, <<-PATTERN
39
+ (send nil? :change ({const send} nil? $_) (sym $_))
40
+ PATTERN
41
+
42
+ def_node_matcher :expect_change_with_block, <<-PATTERN
43
+ (block
44
+ (send nil? :change)
45
+ (args)
46
+ (send ({const send} nil? $_) $_)
47
+ )
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ return unless style == :block
52
+
53
+ expect_change_with_arguments(node) do |receiver, message|
54
+ add_offense(
55
+ node,
56
+ message: format(MSG_CALL, obj: receiver, attr: message)
57
+ )
58
+ end
59
+ end
60
+
61
+ def on_block(node)
62
+ return unless style == :method_call
63
+
64
+ expect_change_with_block(node) do |receiver, message|
65
+ add_offense(
66
+ node,
67
+ message: format(MSG_BLOCK, obj: receiver, attr: message)
68
+ )
69
+ end
70
+ end
71
+
72
+ def autocorrect(node)
73
+ if style == :block
74
+ autocorrect_method_call_to_block(node)
75
+ else
76
+ autocorrect_block_to_method_call(node)
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def autocorrect_method_call_to_block(node)
83
+ lambda do |corrector|
84
+ expect_change_with_arguments(node) do |receiver, message|
85
+ replacement = "change { #{receiver}.#{message} }"
86
+ corrector.replace(node.loc.expression, replacement)
87
+ end
88
+ end
89
+ end
90
+
91
+ def autocorrect_block_to_method_call(node)
92
+ lambda do |corrector|
93
+ expect_change_with_block(node) do |receiver, message|
94
+ replacement = "change(#{receiver}, :#{message})"
95
+ corrector.replace(node.loc.expression, replacement)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end