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.
@@ -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
@@ -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 = '0.18.1'
7
+ STRING = '1.0.rc1'
8
8
  end
9
9
  end
10
10
  end
Binary file
Binary file
@@ -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.18')
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