rubocop-rspec 0.18.1 → 1.0.rc1
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 +24 -0
- data/Gemfile.lock +10 -8
- data/README.md +19 -0
- data/config/default.yml +27 -5
- data/lib/rubocop-rspec.rb +9 -4
- data/lib/rubocop/cop/rspec_describe_class.rb +28 -0
- data/lib/rubocop/cop/rspec_describe_method.rb +35 -0
- data/lib/rubocop/cop/rspec_described_class.rb +56 -0
- data/lib/rubocop/cop/rspec_example_wording.rb +33 -0
- data/lib/rubocop/cop/rspec_file_name.rb +59 -0
- data/lib/rubocop/cop/rspec_instance_variable.rb +39 -0
- data/lib/rubocop/cop/rspec_multiple_describes.rb +35 -0
- data/lib/rubocop/rspec/top_level_describe.rb +59 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/pkg/rubocop-rspec-0.18.1.gem +0 -0
- data/pkg/rubocop-rspec-1.0.rc1.gem +0 -0
- data/rubocop-rspec.gemspec +3 -3
- data/spec/rubocop/cop/rspec_describe_class_spec.rb +36 -0
- data/spec/rubocop/cop/rspec_describe_method_spec.rb +22 -0
- data/spec/rubocop/cop/rspec_described_class_spec.rb +113 -0
- data/spec/rubocop/cop/rspec_example_wording_spec.rb +23 -0
- data/spec/rubocop/cop/rspec_file_name_spec.rb +122 -0
- data/spec/rubocop/cop/rspec_instance_variable_spec.rb +32 -0
- data/spec/rubocop/cop/rspec_multiple_describes_spec.rb +32 -0
- metadata +38 -9
- data/lib/rubocop/cop/rspec/unit_spec_naming.rb +0 -144
- data/spec/rubocop/cop/rspec/unit_spec_naming_spec.rb +0 -137
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rubocop
|
4
|
+
module RSpec
|
5
|
+
# Helper methods for top level describe cops
|
6
|
+
module TopLevelDescribe
|
7
|
+
def on_send(node)
|
8
|
+
return unless respond_to?(:on_top_level_describe)
|
9
|
+
return unless top_level_describe?(node)
|
10
|
+
|
11
|
+
_receiver, _method_name, *args = *node
|
12
|
+
# Ignore non-string args (RSpec metadata)
|
13
|
+
args = [args.first] + args[1..-1].select { |a| a.type == :str }
|
14
|
+
|
15
|
+
on_top_level_describe(node, args)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def top_level_describe?(node)
|
21
|
+
_receiver, method_name, *_args = *node
|
22
|
+
return false unless method_name == :describe
|
23
|
+
|
24
|
+
top_level_nodes.include?(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def top_level_nodes
|
28
|
+
nodes = describe_statement_children(root_node)
|
29
|
+
# If we have no top level describe statements, we need to check any
|
30
|
+
# blocks on the top level (e.g. after a require).
|
31
|
+
if nodes.size == 0
|
32
|
+
nodes = node_children(root_node).map do |child|
|
33
|
+
describe_statement_children(child) if child.type == :block
|
34
|
+
end.flatten.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
nodes
|
38
|
+
end
|
39
|
+
|
40
|
+
def root_node
|
41
|
+
processed_source.ast
|
42
|
+
end
|
43
|
+
|
44
|
+
def single_top_level_describe?
|
45
|
+
top_level_nodes.count == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def describe_statement_children(node)
|
49
|
+
node_children(node).select do |element|
|
50
|
+
element.type == :send && element.children[1] == :describe
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def node_children(node)
|
55
|
+
node.children.select { |e| e.is_a? Parser::AST::Node }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
Binary file
|
Binary file
|
data/rubocop-rspec.gemspec
CHANGED
@@ -11,8 +11,8 @@ Gem::Specification.new do |spec|
|
|
11
11
|
A plugin for the RuboCop code style enforcing & linting tool.
|
12
12
|
end_description
|
13
13
|
spec.homepage = 'http://github.com/nevir/rubocop-rspec'
|
14
|
-
spec.authors = ['Ian MacLeod']
|
15
|
-
spec.email = ['ian@nevir.net']
|
14
|
+
spec.authors = ['Ian MacLeod', 'Nils Gemeinhardt']
|
15
|
+
spec.email = ['ian@nevir.net', 'git@nilsgemeinhardt.de']
|
16
16
|
spec.licenses = ['MIT']
|
17
17
|
|
18
18
|
spec.version = Rubocop::RSpec::Version::STRING
|
@@ -24,5 +24,5 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.test_files = spec.files.grep(/^spec\//)
|
25
25
|
spec.extra_rdoc_files = ['MIT-LICENSE.md', 'README.md']
|
26
26
|
|
27
|
-
spec.add_runtime_dependency('rubocop', '~> 0.
|
27
|
+
spec.add_runtime_dependency('rubocop', '~> 0.19', '>= 0.19')
|
28
28
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecDescribeClass do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'checks first-line describe statements' do
|
9
|
+
inspect_source(cop, ['describe "bad describe" do; end'])
|
10
|
+
expect(cop.offenses.size).to eq(1)
|
11
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
12
|
+
expect(cop.messages).to eq(['The first argument to describe should be ' \
|
13
|
+
'the class or module being tested.'])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'checks describe statements after a require' do
|
17
|
+
inspect_source(cop, ["require 'spec_helper'",
|
18
|
+
'describe "bad describe" do; end'])
|
19
|
+
expect(cop.offenses.size).to eq(1)
|
20
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
21
|
+
expect(cop.messages).to eq(['The first argument to describe should be ' \
|
22
|
+
'the class or module being tested.'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'ignores nested describe statements' do
|
26
|
+
inspect_source(cop, ['describe Some::Class do',
|
27
|
+
' describe "bad describe" do; end',
|
28
|
+
'end'])
|
29
|
+
expect(cop.offenses).to be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't blow up on single-line describes" do
|
33
|
+
inspect_source(cop, ['describe Some::Class'])
|
34
|
+
expect(cop.offenses).to be_empty
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecDescribeMethod do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'enforces non-method names' do
|
9
|
+
inspect_source(cop, ["describe Some::Class, 'nope' do; end"])
|
10
|
+
expect(cop.offenses.size).to eq(1)
|
11
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
12
|
+
expect(cop.messages)
|
13
|
+
.to eq(['The second argument to describe should be the method being ' \
|
14
|
+
"tested. '#instance' or '.class'"])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'skips methods starting with a . or #' do
|
18
|
+
inspect_source(cop, ["describe Some::Class, '.asdf' do; end",
|
19
|
+
"describe Some::Class, '#fdsa' do; end"])
|
20
|
+
expect(cop.offenses).to be_empty
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecDescribedClass do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'checks for the use of the described class' do
|
9
|
+
inspect_source(cop, ['describe MyClass do',
|
10
|
+
' subject { MyClass.do_something }',
|
11
|
+
' before { MyClass.do_something }',
|
12
|
+
'end'])
|
13
|
+
expect(cop.offenses.size).to eq(2)
|
14
|
+
expect(cop.offenses.map(&:line).sort).to eq([2, 3])
|
15
|
+
expect(cop.messages)
|
16
|
+
.to eq(['Use `described_class` instead of `MyClass`'] * 2)
|
17
|
+
expect(cop.highlights).to eq(['MyClass'] * 2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'ignores described class as string' do
|
21
|
+
inspect_source(cop, ['describe MyClass do',
|
22
|
+
' subject { "MyClass" }',
|
23
|
+
'end'])
|
24
|
+
expect(cop.offenses).to be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'ignores describes that do not referece to a class' do
|
28
|
+
inspect_source(cop, ['describe "MyClass" do',
|
29
|
+
' subject { "MyClass" }',
|
30
|
+
'end'])
|
31
|
+
expect(cop.offenses).to be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'ignores if the scope is changing' do
|
35
|
+
inspect_source(cop, ['describe MyClass do',
|
36
|
+
' include MyClass',
|
37
|
+
'end'])
|
38
|
+
expect(cop.offenses.size).to eq(1)
|
39
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
40
|
+
expect(cop.messages)
|
41
|
+
.to eq(['Use `described_class` instead of `MyClass`'])
|
42
|
+
expect(cop.highlights).to eq(['MyClass'])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'ignores class if the scope is changing' do
|
46
|
+
inspect_source(cop, ['describe MyClass do',
|
47
|
+
' def method',
|
48
|
+
' include MyClass',
|
49
|
+
' end',
|
50
|
+
' class OtherClass',
|
51
|
+
' include MyClass',
|
52
|
+
' end',
|
53
|
+
' module MyModle',
|
54
|
+
' include MyClass',
|
55
|
+
' end',
|
56
|
+
'end'])
|
57
|
+
expect(cop.offenses).to be_empty
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'only takes class from top level describes' do
|
61
|
+
inspect_source(cop, ['describe MyClass do',
|
62
|
+
' describe MyClass::Foo do',
|
63
|
+
' subject { MyClass::Foo }',
|
64
|
+
' let(:foo) { MyClass }',
|
65
|
+
' end',
|
66
|
+
'end'])
|
67
|
+
expect(cop.offenses.size).to eq(1)
|
68
|
+
expect(cop.offenses.map(&:line).sort).to eq([4])
|
69
|
+
expect(cop.messages)
|
70
|
+
.to eq(['Use `described_class` instead of `MyClass`'])
|
71
|
+
expect(cop.highlights).to eq(['MyClass'])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'ignores subclasses' do
|
75
|
+
inspect_source(cop, ['describe MyClass do',
|
76
|
+
' subject { MyClass::SubClass }',
|
77
|
+
'end'])
|
78
|
+
expect(cop.offenses).to be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'ignores if namespace is not matching' do
|
82
|
+
inspect_source(cop, ['describe MyNamespace::MyClass do',
|
83
|
+
' subject { ::MyClass }',
|
84
|
+
' let(:foo) { MyClass }',
|
85
|
+
'end'])
|
86
|
+
expect(cop.offenses).to be_empty
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'checks for the use of described class with namespace' do
|
90
|
+
inspect_source(cop, ['describe MyNamespace::MyClass do',
|
91
|
+
' subject { MyNamespace::MyClass }',
|
92
|
+
'end'])
|
93
|
+
expect(cop.offenses.size).to eq(1)
|
94
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
95
|
+
expect(cop.messages)
|
96
|
+
.to eq(['Use `described_class` instead of `MyNamespace::MyClass`'])
|
97
|
+
expect(cop.highlights).to eq(['MyNamespace::MyClass'])
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'checks for the use of described class with module' do
|
101
|
+
pending 'TODO'
|
102
|
+
inspect_source(cop, ['module MyNamespace',
|
103
|
+
' describe MyClass do',
|
104
|
+
' subject { MyNamespace::MyClass }',
|
105
|
+
' end',
|
106
|
+
'end'])
|
107
|
+
expect(cop.offenses.size).to eq(1)
|
108
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
109
|
+
expect(cop.messages)
|
110
|
+
.to eq(['Use `described_class` instead of `MyNamespace::MyClass`'])
|
111
|
+
expect(cop.highlights).to eq(['MyNamespace::MyClass'])
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecExampleWording do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'finds description with `should` at the beginning' do
|
9
|
+
inspect_source(cop, ["it 'should do something' do", 'end'])
|
10
|
+
expect(cop.offenses.size).to eq(1)
|
11
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
12
|
+
expect(cop.messages)
|
13
|
+
.to eq(['Do not use should when describing your tests.'])
|
14
|
+
expect(cop.highlights).to eq(['it'])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'skips descriptions without `should` at the beginning' do
|
18
|
+
inspect_source(cop, ["it 'finds no should ' \\",
|
19
|
+
" 'here' do",
|
20
|
+
'end'])
|
21
|
+
expect(cop.offenses).to be_empty
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecFileName do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'checks the path' do
|
9
|
+
inspect_source(cop,
|
10
|
+
["describe MyClass, 'foo' do; end"],
|
11
|
+
'wrong_path_foo_spec.rb')
|
12
|
+
expect(cop.offenses.size).to eq(1)
|
13
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
14
|
+
expect(cop.messages)
|
15
|
+
.to eq(['Spec path should end with `my_class_foo*_spec.rb`'])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'checks the path' do
|
19
|
+
inspect_source(cop,
|
20
|
+
["describe MyClass, '#foo' do; end"],
|
21
|
+
'wrong_class_foo_spec.rb')
|
22
|
+
expect(cop.offenses.size).to eq(1)
|
23
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
24
|
+
expect(cop.messages)
|
25
|
+
.to eq(['Spec path should end with `my_class_foo*_spec.rb`'])
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'checks class spec paths' do
|
29
|
+
inspect_source(cop,
|
30
|
+
['describe MyClass do; end'],
|
31
|
+
'wrong_class_spec.rb')
|
32
|
+
expect(cop.offenses.size).to eq(1)
|
33
|
+
expect(cop.offenses.map(&:line).sort).to eq([1])
|
34
|
+
expect(cop.messages)
|
35
|
+
.to eq(['Spec path should end with `my_class*_spec.rb`'])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'skips specs that do not describe a class / method' do
|
39
|
+
inspect_source(cop,
|
40
|
+
["describe 'Test something' do; end"],
|
41
|
+
'some/class_spec.rb')
|
42
|
+
expect(cop.offenses).to be_empty
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'skips specs that do have multiple top level describes' do
|
46
|
+
inspect_source(cop,
|
47
|
+
["describe MyClass, 'do_this' do; end",
|
48
|
+
"describe MyClass, 'do_that' do; end"],
|
49
|
+
'some/class_spec.rb')
|
50
|
+
expect(cop.offenses).to be_empty
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'checks class specs' do
|
54
|
+
inspect_source(cop,
|
55
|
+
['describe Some::Class do; end'],
|
56
|
+
'some/class_spec.rb')
|
57
|
+
expect(cop.offenses).to be_empty
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'handles CamelCaps class names' do
|
61
|
+
inspect_source(cop,
|
62
|
+
['describe MyClass do; end'],
|
63
|
+
'my_class_spec.rb')
|
64
|
+
expect(cop.offenses).to be_empty
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'handles ACRONYMClassNames' do
|
68
|
+
inspect_source(cop,
|
69
|
+
['describe ABCOne::Two do; end'],
|
70
|
+
'abc_one/two_spec.rb')
|
71
|
+
expect(cop.offenses).to be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'handles ALLCAPS class names' do
|
75
|
+
inspect_source(cop,
|
76
|
+
['describe ALLCAPS do; end'],
|
77
|
+
'allcaps_spec.rb')
|
78
|
+
expect(cop.offenses).to be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'checks instance methods' do
|
82
|
+
inspect_source(cop,
|
83
|
+
["describe Some::Class, '#inst' do; end"],
|
84
|
+
'some/class_inst_spec.rb')
|
85
|
+
expect(cop.offenses).to be_empty
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'checks methods' do
|
89
|
+
inspect_source(cop,
|
90
|
+
["describe Some::Class, 'inst' do; end"],
|
91
|
+
'some/class_inst_spec.rb')
|
92
|
+
expect(cop.offenses).to be_empty
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'checks class methods' do
|
96
|
+
inspect_source(cop,
|
97
|
+
["describe Some::Class, '.inst' do; end"],
|
98
|
+
'some/class_inst_spec.rb')
|
99
|
+
expect(cop.offenses).to be_empty
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'ignores non-alphanumeric characters' do
|
103
|
+
inspect_source(cop,
|
104
|
+
["describe Some::Class, '#pred?' do; end"],
|
105
|
+
'some/class_pred_spec.rb')
|
106
|
+
expect(cop.offenses).to be_empty
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'allows flexibility with predicates' do
|
110
|
+
inspect_source(cop,
|
111
|
+
["describe Some::Class, '#thing?' do; end"],
|
112
|
+
'some/class_thing_predicate_spec.rb')
|
113
|
+
expect(cop.offenses).to be_empty
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'allows flexibility with operators' do
|
117
|
+
inspect_source(cop,
|
118
|
+
["describe MyClass, '#<=>' do; end"],
|
119
|
+
'my_class_spaceship_operator_spec.rb')
|
120
|
+
expect(cop.offenses).to be_empty
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rubocop::Cop::RSpecInstanceVariable do
|
6
|
+
subject(:cop) { described_class.new }
|
7
|
+
|
8
|
+
it 'finds an instance variable inside a describe' do
|
9
|
+
inspect_source(cop, ['describe MyClass do',
|
10
|
+
' before { @foo = [] }',
|
11
|
+
' it { expect(@foo).to be_emtpy }',
|
12
|
+
'end'])
|
13
|
+
expect(cop.offenses.size).to eq(1)
|
14
|
+
expect(cop.offenses.map(&:line).sort).to eq([3])
|
15
|
+
expect(cop.messages).to eq(['Use `let` instead of an instance variable'])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'finds an instance variable inside a shared example' do
|
19
|
+
inspect_source(cop, ["shared_examples 'shared example' do",
|
20
|
+
' it { expect(@foo).to be_emtpy }',
|
21
|
+
'end'])
|
22
|
+
expect(cop.offenses.size).to eq(1)
|
23
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
24
|
+
expect(cop.messages).to eq(['Use `let` instead of an instance variable'])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'ignores an instance variable without describe' do
|
28
|
+
inspect_source(cop, ['@foo = []',
|
29
|
+
'@foo.empty?'])
|
30
|
+
expect(cop.offenses).to be_empty
|
31
|
+
end
|
32
|
+
end
|