rubocop-rspec 1.9.1 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +2 -1
  4. data/config/default.yml +13 -1
  5. data/lib/rubocop-rspec.rb +5 -0
  6. data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
  7. data/lib/rubocop/cop/rspec/cop.rb +1 -2
  8. data/lib/rubocop/cop/rspec/described_class.rb +1 -1
  9. data/lib/rubocop/cop/rspec/expect_output.rb +52 -0
  10. data/lib/rubocop/cop/rspec/implicit_expect.rb +3 -2
  11. data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
  12. data/lib/rubocop/cop/rspec/message_spies.rb +2 -2
  13. data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -4
  14. data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
  15. data/lib/rubocop/cop/rspec/nested_groups.rb +16 -1
  16. data/lib/rubocop/cop/rspec/repeated_example.rb +41 -0
  17. data/lib/rubocop/cop/rspec/scattered_setup.rb +49 -0
  18. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +17 -5
  19. data/lib/rubocop/rspec.rb +1 -1
  20. data/lib/rubocop/rspec/concept.rb +33 -0
  21. data/lib/rubocop/rspec/example.rb +1 -25
  22. data/lib/rubocop/rspec/example_group.rb +28 -12
  23. data/lib/rubocop/rspec/hook.rb +49 -0
  24. data/lib/rubocop/rspec/language/node_pattern.rb +1 -0
  25. data/lib/rubocop/rspec/version.rb +1 -1
  26. data/spec/rubocop/cop/rspec/cop_spec.rb +4 -1
  27. data/spec/rubocop/cop/rspec/expect_output_spec.rb +62 -0
  28. data/spec/rubocop/cop/rspec/message_spies_spec.rb +74 -2
  29. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +2 -2
  30. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +14 -2
  31. data/spec/rubocop/cop/rspec/repeated_example_spec.rb +65 -0
  32. data/spec/rubocop/cop/rspec/scattered_setup_spec.rb +96 -0
  33. data/spec/rubocop/cop/rspec/single_argument_message_chain_spec.rb +27 -3
  34. data/spec/rubocop/rspec/description_extractor_spec.rb +1 -1
  35. data/spec/rubocop/rspec/example_group_spec.rb +1 -1
  36. data/spec/rubocop/rspec/example_spec.rb +2 -2
  37. data/spec/rubocop/rspec/hook_spec.rb +53 -0
  38. metadata +15 -2
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec DSL methods
6
+ class Concept
7
+ include Language, Language::NodePattern
8
+ extend NodePattern::Macros
9
+
10
+ def initialize(node)
11
+ @node = node
12
+ end
13
+
14
+ def eql?(other)
15
+ node.eql?(other.node)
16
+ end
17
+
18
+ alias == eql?
19
+
20
+ def hash
21
+ [self.class, node].hash
22
+ end
23
+
24
+ def to_node
25
+ node
26
+ end
27
+
28
+ protected
29
+
30
+ attr_reader :node
31
+ end
32
+ end
33
+ end
@@ -3,17 +3,11 @@
3
3
  module RuboCop
4
4
  module RSpec
5
5
  # Wrapper for RSpec examples
6
- class Example
7
- extend RuboCop::NodePattern::Macros
8
-
6
+ class Example < Concept
9
7
  def_node_matcher :extract_doc_string, '(send _ _ $str ...)'
10
8
  def_node_matcher :extract_metadata, '(send _ _ _ $...)'
11
9
  def_node_matcher :extract_implementation, '(block send args $_)'
12
10
 
13
- def initialize(node)
14
- @node = node
15
- end
16
-
17
11
  def doc_string
18
12
  extract_doc_string(definition)
19
13
  end
@@ -26,20 +20,6 @@ module RuboCop
26
20
  extract_implementation(node)
27
21
  end
28
22
 
29
- def eql?(other)
30
- node.eql?(other.node)
31
- end
32
-
33
- alias == eql?
34
-
35
- def hash
36
- [self.class, node].hash
37
- end
38
-
39
- def to_node
40
- node
41
- end
42
-
43
23
  def definition
44
24
  if node.send_type?
45
25
  node
@@ -47,10 +27,6 @@ module RuboCop
47
27
  node.children.first
48
28
  end
49
29
  end
50
-
51
- protected
52
-
53
- attr_reader :node
54
30
  end
55
31
  end
56
32
  end
@@ -3,12 +3,7 @@
3
3
  module RuboCop
4
4
  module RSpec
5
5
  # Wrapper for RSpec example groups
6
- class ExampleGroup
7
- include Language
8
- extend NodePattern::Macros
9
-
10
- def_node_matcher :example?, Examples::ALL.block_pattern
11
-
6
+ class ExampleGroup < Concept
12
7
  # @!method scope_change?(node)
13
8
  #
14
9
  # Detect if the node is an example group or shared example
@@ -18,21 +13,42 @@ module RuboCop
18
13
  def_node_matcher :scope_change?,
19
14
  (ExampleGroups::ALL + SharedGroups::ALL).block_pattern
20
15
 
21
- def initialize(node)
22
- @node = node
23
- end
16
+ # @!method hook(node)
17
+ #
18
+ # Detect if node is `before`, `after`, `around`
19
+ def_node_matcher :hook, <<-PATTERN
20
+ (block {$(send nil #{Hooks::ALL.node_pattern_union} ...)} ...)
21
+ PATTERN
24
22
 
25
23
  def examples
26
24
  examples_in_scope(node).map(&Example.public_method(:new))
27
25
  end
28
26
 
27
+ def hooks
28
+ hooks_in_scope(node).map(&Hook.public_method(:new))
29
+ end
30
+
29
31
  private
30
32
 
31
- attr_reader :node
33
+ def hooks_in_scope(node)
34
+ node.each_child_node.flat_map do |child|
35
+ find_hooks(child)
36
+ end
37
+ end
38
+
39
+ def find_hooks(node)
40
+ return [] if scope_change?(node) || example?(node)
41
+
42
+ if hook(node)
43
+ [node]
44
+ else
45
+ hooks_in_scope(node)
46
+ end
47
+ end
32
48
 
33
- def examples_in_scope(node)
49
+ def examples_in_scope(node, &blk)
34
50
  node.each_child_node.flat_map do |child|
35
- find_examples(child)
51
+ find_examples(child, &blk)
36
52
  end
37
53
  end
38
54
 
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec hook
6
+ class Hook < Concept
7
+ STANDARDIZED_SCOPES = %i(each context suite).freeze
8
+ private_constant(:STANDARDIZED_SCOPES)
9
+
10
+ def name
11
+ node.method_name
12
+ end
13
+
14
+ def knowable_scope?
15
+ return true unless scope_argument
16
+
17
+ scope_argument.sym_type?
18
+ end
19
+
20
+ def valid_scope?
21
+ STANDARDIZED_SCOPES.include?(scope)
22
+ end
23
+
24
+ def example?
25
+ scope.equal?(:each)
26
+ end
27
+
28
+ def scope
29
+ case scope_name
30
+ when nil, :each, :example then :each
31
+ when :context, :all then :context
32
+ when :suite then :suite
33
+ else
34
+ scope_name
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def scope_name
41
+ scope_argument.to_a.first
42
+ end
43
+
44
+ def scope_argument
45
+ node.method_args.first
46
+ end
47
+ end
48
+ end
49
+ end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  extend RuboCop::NodePattern::Macros
9
9
 
10
10
  def_node_matcher :example_group?, ExampleGroups::ALL.block_pattern
11
+ def_node_matcher :example?, Examples::ALL.block_pattern
11
12
  end
12
13
  end
13
14
  end
@@ -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 = '1.9.1'.freeze
7
+ STRING = '1.10.0'.freeze
8
8
  end
9
9
  end
10
10
  end
@@ -20,7 +20,9 @@ RSpec.describe RuboCop::Cop::RSpec::Cop do
20
20
  end
21
21
 
22
22
  let(:fake_cop) do
23
- Class.new(described_class) do
23
+ stub_const('RuboCop::RSpec', Module.new)
24
+ # rubocop:disable ClassAndModuleChildren
25
+ class RuboCop::RSpec::FakeCop < described_class
24
26
  def self.name
25
27
  'RuboCop::RSpec::FakeCop'
26
28
  end
@@ -29,6 +31,7 @@ RSpec.describe RuboCop::Cop::RSpec::Cop do
29
31
  add_offense(node, :expression, 'I flag everything')
30
32
  end
31
33
  end
34
+ RuboCop::RSpec::FakeCop
32
35
  end
33
36
 
34
37
  let(:rspec_patterns) { ['_spec.rb$', '(?:^|/)spec/'] }
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RuboCop::Cop::RSpec::ExpectOutput do
6
+ subject(:cop) { described_class.new }
7
+
8
+ it 'registers an offense for overwriting $stdout within an example' do
9
+ expect_violation(<<-RUBY)
10
+ specify do
11
+ $stdout = StringIO.new
12
+ ^^^^^^^ Use `expect { ... }.to output(...).to_stdout` instead of mutating $stdout
13
+ end
14
+ RUBY
15
+ end
16
+
17
+ it 'registers an offense for overwriting $stderr ' \
18
+ 'within an example scoped hook' do
19
+ expect_violation(<<-RUBY)
20
+ before(:each) do
21
+ $stderr = StringIO.new
22
+ ^^^^^^^ Use `expect { ... }.to output(...).to_stderr` instead of mutating $stderr
23
+ end
24
+ RUBY
25
+ end
26
+
27
+ it 'does not register an offense for interacting with $stdout' do
28
+ expect_no_violations(<<-RUBY)
29
+ specify do
30
+ $stdout.puts("hi")
31
+ end
32
+ RUBY
33
+ end
34
+
35
+ it 'does not flag assignments to other global variables' do
36
+ expect_no_violations(<<-RUBY)
37
+ specify do
38
+ $blah = StringIO.new
39
+ end
40
+ RUBY
41
+ end
42
+
43
+ it 'does not flag assignments to $stdout outside of example scope' do
44
+ expect_no_violations(<<-RUBY)
45
+ before(:suite) do
46
+ $stderr = StringIO.new
47
+ end
48
+ RUBY
49
+ end
50
+
51
+ it 'does not flag assignments to $stdout in example_group scope' do
52
+ expect_no_violations(<<-RUBY)
53
+ describe Foo do
54
+ $stderr = StringIO.new
55
+ end
56
+ RUBY
57
+ end
58
+
59
+ it 'does not flag assigns to $stdout when in the root scope' do
60
+ expect_no_violations('$stderr = StringIO.new')
61
+ end
62
+ end
@@ -8,13 +8,49 @@ describe RuboCop::Cop::RSpec::MessageSpies, :config do
8
8
  { 'EnforcedStyle' => 'have_received' }
9
9
  end
10
10
 
11
- it 'flags expect(...).to receive' do
11
+ it 'flags expect(send).to receive' do
12
12
  expect_violation(<<-RUBY)
13
13
  expect(foo).to receive(:bar)
14
14
  ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `foo` as a spy using `allow` or `instance_spy`.
15
15
  RUBY
16
16
  end
17
17
 
18
+ it 'flags expect(lvar).to receive' do
19
+ expect_violation(<<-RUBY)
20
+ foo = baz
21
+ expect(foo).to receive(:bar)
22
+ ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `foo` as a spy using `allow` or `instance_spy`.
23
+ RUBY
24
+ end
25
+
26
+ it 'flags expect(ivar).to receive' do
27
+ expect_violation(<<-RUBY)
28
+ expect(@foo).to receive(:bar)
29
+ ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `@foo` as a spy using `allow` or `instance_spy`.
30
+ RUBY
31
+ end
32
+
33
+ it 'flags expect(const).to receive' do
34
+ expect_violation(<<-RUBY)
35
+ expect(Foo).to receive(:bar)
36
+ ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `Foo` as a spy using `allow` or `instance_spy`.
37
+ RUBY
38
+ end
39
+
40
+ it 'flags expect(...).not_to receive' do
41
+ expect_violation(<<-RUBY)
42
+ expect(foo).not_to receive(:bar)
43
+ ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `foo` as a spy using `allow` or `instance_spy`.
44
+ RUBY
45
+ end
46
+
47
+ it 'flags expect(...).to_not receive' do
48
+ expect_violation(<<-RUBY)
49
+ expect(foo).to_not receive(:bar)
50
+ ^^^^^^^ Prefer `have_received` for setting message expectations. Setup `foo` as a spy using `allow` or `instance_spy`.
51
+ RUBY
52
+ end
53
+
18
54
  it 'flags expect(...).to receive with' do
19
55
  expect_violation(<<-RUBY)
20
56
  expect(foo).to receive(:bar).with(:baz)
@@ -52,13 +88,49 @@ describe RuboCop::Cop::RSpec::MessageSpies, :config do
52
88
  { 'EnforcedStyle' => 'receive' }
53
89
  end
54
90
 
55
- it 'flags expect(...).to have_received' do
91
+ it 'flags expect(send).to have_received' do
56
92
  expect_violation(<<-RUBY)
57
93
  expect(foo).to have_received(:bar)
58
94
  ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
59
95
  RUBY
60
96
  end
61
97
 
98
+ it 'flags expect(lvar).to have_received' do
99
+ expect_violation(<<-RUBY)
100
+ foo = baz
101
+ expect(foo).to have_received(:bar)
102
+ ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
103
+ RUBY
104
+ end
105
+
106
+ it 'flags expect(ivar).to have_received' do
107
+ expect_violation(<<-RUBY)
108
+ expect(@foo).to have_received(:bar)
109
+ ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
110
+ RUBY
111
+ end
112
+
113
+ it 'flags expect(const).to have_received' do
114
+ expect_violation(<<-RUBY)
115
+ expect(Foo).to have_received(:bar)
116
+ ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
117
+ RUBY
118
+ end
119
+
120
+ it 'flags expect(...).not_to have_received' do
121
+ expect_violation(<<-RUBY)
122
+ expect(foo).not_to have_received(:bar)
123
+ ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
124
+ RUBY
125
+ end
126
+
127
+ it 'flags expect(...).to_not have_received' do
128
+ expect_violation(<<-RUBY)
129
+ expect(foo).to_not have_received(:bar)
130
+ ^^^^^^^^^^^^^ Prefer `receive` for setting message expectations.
131
+ RUBY
132
+ end
133
+
62
134
  it 'flags expect(...).to have_received with' do
63
135
  expect_violation(<<-RUBY)
64
136
  expect(foo).to have_received(:bar).with(:baz)
@@ -10,7 +10,7 @@ describe RuboCop::Cop::RSpec::MultipleExpectations, :config do
10
10
  expect_violation(<<-RUBY)
11
11
  describe Foo do
12
12
  it 'uses expect twice' do
13
- ^^^^^^^^^^^^^^^^^^^^^^ Too many expectations.
13
+ ^^^^^^^^^^^^^^^^^^^^^^ Example has too many expectations [2/1]
14
14
  expect(foo).to eq(bar)
15
15
  expect(baz).to eq(bar)
16
16
  end
@@ -53,7 +53,7 @@ describe RuboCop::Cop::RSpec::MultipleExpectations, :config do
53
53
  expect_violation(<<-RUBY)
54
54
  describe Foo do
55
55
  it 'uses expect three times' do
56
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Too many expectations.
56
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Example has too many expectations [3/2]
57
57
  expect(foo).to eq(bar)
58
58
  expect(baz).to eq(bar)
59
59
  expect(qux).to eq(bar)
@@ -33,8 +33,8 @@ describe RuboCop::Cop::RSpec::NestedGroups, :config do
33
33
  RUBY
34
34
  end
35
35
 
36
- context 'when MaxNesting is configured as 2' do
37
- let(:cop_config) { { 'MaxNesting' => '2' } }
36
+ context 'when Max is configured as 2' do
37
+ let(:cop_config) { { 'Max' => '2' } }
38
38
 
39
39
  it 'flags two levels of nesting' do
40
40
  expect_violation(<<-RUBY)
@@ -51,4 +51,16 @@ describe RuboCop::Cop::RSpec::NestedGroups, :config do
51
51
  RUBY
52
52
  end
53
53
  end
54
+
55
+ context 'when configured with MaxNesting' do
56
+ let(:cop_config) { { 'MaxNesting' => '1' } }
57
+
58
+ it 'emits a deprecation warning' do
59
+ expect { inspect_source(cop, 'describe(Foo) { }', 'foo_spec.rb') }
60
+ .to output(
61
+ 'Configuration key `MaxNesting` for RSpec/NestedGroups is ' \
62
+ "deprecated in favor of `Max`. Please use that instead.\n"
63
+ ).to_stderr
64
+ end
65
+ end
54
66
  end