rubocop-rspec 0.18.1 → 1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|