rubocop-rspec 1.9.1 → 1.10.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile +2 -1
- data/config/default.yml +13 -1
- data/lib/rubocop-rspec.rb +5 -0
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
- data/lib/rubocop/cop/rspec/cop.rb +1 -2
- data/lib/rubocop/cop/rspec/described_class.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +52 -0
- data/lib/rubocop/cop/rspec/implicit_expect.rb +3 -2
- data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/message_spies.rb +2 -2
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -4
- data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/nested_groups.rb +16 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +41 -0
- data/lib/rubocop/cop/rspec/scattered_setup.rb +49 -0
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +17 -5
- data/lib/rubocop/rspec.rb +1 -1
- data/lib/rubocop/rspec/concept.rb +33 -0
- data/lib/rubocop/rspec/example.rb +1 -25
- data/lib/rubocop/rspec/example_group.rb +28 -12
- data/lib/rubocop/rspec/hook.rb +49 -0
- data/lib/rubocop/rspec/language/node_pattern.rb +1 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/spec/rubocop/cop/rspec/cop_spec.rb +4 -1
- data/spec/rubocop/cop/rspec/expect_output_spec.rb +62 -0
- data/spec/rubocop/cop/rspec/message_spies_spec.rb +74 -2
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/nested_groups_spec.rb +14 -2
- data/spec/rubocop/cop/rspec/repeated_example_spec.rb +65 -0
- data/spec/rubocop/cop/rspec/scattered_setup_spec.rb +96 -0
- data/spec/rubocop/cop/rspec/single_argument_message_chain_spec.rb +27 -3
- data/spec/rubocop/rspec/description_extractor_spec.rb +1 -1
- data/spec/rubocop/rspec/example_group_spec.rb +1 -1
- data/spec/rubocop/rspec/example_spec.rb +2 -2
- data/spec/rubocop/rspec/hook_spec.rb +53 -0
- 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
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
@@ -20,7 +20,9 @@ RSpec.describe RuboCop::Cop::RSpec::Cop do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
let(:fake_cop) do
|
23
|
-
|
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(
|
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(
|
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
|
-
^^^^^^^^^^^^^^^^^^^^^^
|
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
|
-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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
|
37
|
-
let(:cop_config) { { '
|
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
|