rubocop-rspec 1.6.0 → 1.7.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 +22 -0
- data/README.md +23 -0
- data/Rakefile +19 -1
- data/config/default.yml +78 -15
- data/lib/rubocop-rspec.rb +19 -1
- data/lib/rubocop/cop/rspec/any_instance.rb +5 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +58 -0
- data/lib/rubocop/cop/rspec/describe_class.rb +2 -3
- data/lib/rubocop/cop/rspec/describe_method.rb +4 -3
- data/lib/rubocop/cop/rspec/described_class.rb +4 -35
- data/lib/rubocop/cop/rspec/empty_example_group.rb +100 -0
- data/lib/rubocop/cop/rspec/example_length.rb +5 -2
- data/lib/rubocop/cop/rspec/example_wording.rb +5 -2
- data/lib/rubocop/cop/rspec/expect_actual.rb +79 -0
- data/lib/rubocop/cop/rspec/file_path.rb +3 -1
- data/lib/rubocop/cop/rspec/focus.rb +12 -28
- data/lib/rubocop/cop/rspec/hook_argument.rb +122 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +53 -12
- data/lib/rubocop/cop/rspec/leading_subject.rb +58 -0
- data/lib/rubocop/cop/rspec/let_setup.rb +58 -0
- data/lib/rubocop/cop/rspec/message_chain.rb +33 -0
- data/lib/rubocop/cop/rspec/message_expectation.rb +58 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +10 -7
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +89 -0
- data/lib/rubocop/cop/rspec/named_subject.rb +10 -1
- data/lib/rubocop/cop/rspec/nested_groups.rb +125 -0
- data/lib/rubocop/cop/rspec/not_to_not.rb +3 -3
- data/lib/rubocop/cop/rspec/subject_stub.rb +136 -0
- data/lib/rubocop/cop/rspec/verified_doubles.rb +4 -1
- data/lib/rubocop/rspec.rb +10 -0
- data/lib/rubocop/rspec/config_formatter.rb +33 -0
- data/lib/rubocop/rspec/description_extractor.rb +35 -0
- data/lib/rubocop/rspec/inject.rb +2 -6
- data/lib/rubocop/rspec/language.rb +73 -0
- data/lib/rubocop/rspec/language/node_pattern.rb +16 -0
- data/lib/rubocop/rspec/spec_only.rb +61 -0
- data/lib/rubocop/rspec/top_level_describe.rb +6 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/rubocop-rspec.gemspec +1 -0
- data/spec/project/changelog_spec.rb +81 -0
- data/spec/project/default_config_spec.rb +52 -0
- data/spec/project/project_requires_spec.rb +8 -0
- data/spec/rubocop/cop/rspec/be_eql_spec.rb +59 -0
- data/spec/rubocop/cop/rspec/described_class_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +79 -0
- data/spec/rubocop/cop/rspec/example_length_spec.rb +50 -30
- data/spec/rubocop/cop/rspec/example_wording_spec.rb +21 -3
- data/spec/rubocop/cop/rspec/expect_actual_spec.rb +136 -0
- data/spec/rubocop/cop/rspec/file_path_spec.rb +48 -71
- data/spec/rubocop/cop/rspec/focus_spec.rb +1 -1
- data/spec/rubocop/cop/rspec/hook_argument_spec.rb +189 -0
- data/spec/rubocop/cop/rspec/instance_variable_spec.rb +37 -0
- data/spec/rubocop/cop/rspec/leading_subject_spec.rb +54 -0
- data/spec/rubocop/cop/rspec/let_setup_spec.rb +66 -0
- data/spec/rubocop/cop/rspec/message_chain_spec.rb +21 -0
- data/spec/rubocop/cop/rspec/message_expectation_spec.rb +63 -0
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +84 -0
- data/spec/rubocop/cop/rspec/nested_groups_spec.rb +55 -0
- data/spec/rubocop/cop/rspec/not_to_not_spec.rb +12 -2
- data/spec/rubocop/cop/rspec/subject_stub_spec.rb +183 -0
- data/spec/rubocop/rspec/config_formatter_spec.rb +48 -0
- data/spec/rubocop/rspec/description_extractor_spec.rb +35 -0
- data/spec/rubocop/rspec/language/selector_set_spec.rb +29 -0
- data/spec/rubocop/rspec/spec_only_spec.rb +97 -0
- data/spec/shared/rspec_only_cop_behavior.rb +68 -0
- data/spec/spec_helper.rb +13 -1
- metadata +72 -5
- data/spec/project_spec.rb +0 -115
data/rubocop-rspec.gemspec
CHANGED
@@ -0,0 +1,81 @@
|
|
1
|
+
describe 'CHANGELOG.md' do
|
2
|
+
subject(:changelog) { SpecHelper::ROOT.join('CHANGELOG.md').read }
|
3
|
+
|
4
|
+
it 'has link definitions for all implicit links' do
|
5
|
+
implicit_link_names = changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq
|
6
|
+
implicit_link_names.each do |name|
|
7
|
+
expect(changelog).to include("[#{name}]: http")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'entry' do
|
12
|
+
subject(:entries) { lines.grep(/^\*/).map(&:chomp) }
|
13
|
+
let(:lines) { changelog.each_line }
|
14
|
+
|
15
|
+
it 'has a whitespace between the * and the body' do
|
16
|
+
entries.each do |entry|
|
17
|
+
expect(entry).to match(/^\* \S/)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'has a link to the contributors at the end' do
|
22
|
+
entries.each do |entry|
|
23
|
+
expect(entry).to match(/\(\[@\S+\]\[\](?:, \[@\S+\]\[\])*\)$/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'link to related issue on github' do
|
28
|
+
let(:issues) do
|
29
|
+
entries.map do |entry|
|
30
|
+
entry.match(/\[(?<number>[#\d]+)\]\((?<url>[^\)]+)\)/)
|
31
|
+
end.compact
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has an issue number prefixed with #' do
|
35
|
+
issues.each do |issue|
|
36
|
+
expect(issue[:number]).to match(/^#\d+$/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'has a valid URL' do
|
41
|
+
issues.each do |issue|
|
42
|
+
number = issue[:number].gsub(/\D/, '')
|
43
|
+
pattern = %r{^https://github\.com/.+/.+/(?:issues|pull)/#{number}$} # rubocop:disable LineLength
|
44
|
+
expect(issue[:url]).to match(pattern)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'has a colon and a whitespace at the end' do
|
49
|
+
entries_including_issue_link = entries.select do |entry|
|
50
|
+
entry.match(/^\*\s*\[/)
|
51
|
+
end
|
52
|
+
|
53
|
+
entries_including_issue_link.each do |entry|
|
54
|
+
expect(entry).to include('): ')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'body' do
|
60
|
+
let(:bodies) do
|
61
|
+
entries.map do |entry|
|
62
|
+
entry
|
63
|
+
.sub(/^\*\s*(?:\[.+?\):\s*)?/, '')
|
64
|
+
.sub(/\s*\([^\)]+\)$/, '')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'does not start with a lower case' do
|
69
|
+
bodies.each do |body|
|
70
|
+
expect(body).not_to match(/^[a-z]/)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'ends with a punctuation' do
|
75
|
+
bodies.each do |body|
|
76
|
+
expect(body).to match(/[\.\!]$/)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
describe 'config/default.yml' do
|
2
|
+
subject(:default_config) do
|
3
|
+
RuboCop::ConfigLoader.load_file('config/default.yml')
|
4
|
+
end
|
5
|
+
|
6
|
+
let(:cop_names) do
|
7
|
+
glob = SpecHelper::ROOT.join('lib', 'rubocop', 'cop', 'rspec', '*.rb')
|
8
|
+
|
9
|
+
Pathname.glob(glob).map do |file|
|
10
|
+
file_name = file.basename('.rb').to_s
|
11
|
+
cop_name = file_name.gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase }
|
12
|
+
|
13
|
+
"RSpec/#{cop_name}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:config_keys) do
|
18
|
+
cop_names + %w(AllCops)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cop_configuration(config_key)
|
22
|
+
cop_names.map do |cop_name|
|
23
|
+
cop_config = default_config.fetch(cop_name)
|
24
|
+
|
25
|
+
cop_config.fetch(config_key) do
|
26
|
+
raise "Expected #{cop_name} to have #{config_key} configuration key"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has configuration for all cops' do
|
32
|
+
expect(default_config.keys.sort).to eq(config_keys.sort)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'has descriptions for all cops' do
|
36
|
+
expect(cop_configuration('Description')).to all(be_a(String))
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not have newlines in cop descriptions' do
|
40
|
+
cop_configuration('Description').each do |value|
|
41
|
+
expect(value).not_to include("\n")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'ends every description with a period' do
|
46
|
+
expect(cop_configuration('Description')).to all(end_with('.'))
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'includes Enabled: true for every cop' do
|
50
|
+
expect(cop_configuration('Enabled')).to all(be(true))
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
describe RuboCop::Cop::RSpec::BeEql do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
it 'registers an offense for `eql` when argument is a boolean' do
|
5
|
+
expect_violation(<<-RUBY)
|
6
|
+
it { expect(foo).to eql(true) }
|
7
|
+
^^^ Prefer `be` over `eql`
|
8
|
+
it { expect(foo).to eql(false) }
|
9
|
+
^^^ Prefer `be` over `eql`
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'registers an offense for `eql` when argument is an integer' do
|
14
|
+
expect_violation(<<-RUBY)
|
15
|
+
it { expect(foo).to eql(0) }
|
16
|
+
^^^ Prefer `be` over `eql`
|
17
|
+
it { expect(foo).to eql(123) }
|
18
|
+
^^^ Prefer `be` over `eql`
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'registers an offense for `eql` when argument is a float' do
|
23
|
+
expect_violation(<<-RUBY)
|
24
|
+
it { expect(foo).to eql(1.0) }
|
25
|
+
^^^ Prefer `be` over `eql`
|
26
|
+
it { expect(foo).to eql(1.23) }
|
27
|
+
^^^ Prefer `be` over `eql`
|
28
|
+
RUBY
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'registers an offense for `eql` when argument is a symbol' do
|
32
|
+
expect_violation(<<-RUBY)
|
33
|
+
it { expect(foo).to eql(:foo) }
|
34
|
+
^^^ Prefer `be` over `eql`
|
35
|
+
RUBY
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not register an offense for `eql` when argument is a string' do
|
39
|
+
expect_no_violations(<<-RUBY)
|
40
|
+
it { expect(foo).to eql('foo') }
|
41
|
+
RUBY
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not register an offense for `eql` when expectation is negated' do
|
45
|
+
expect_no_violations(<<-RUBY)
|
46
|
+
it { expect(foo).to_not eql(1) }
|
47
|
+
RUBY
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'autocorrects offense to use `be`' do
|
51
|
+
corrected =
|
52
|
+
autocorrect_source(
|
53
|
+
cop,
|
54
|
+
['it { expect(foo).to eql(1) }'],
|
55
|
+
'spec/foo_spec.rb'
|
56
|
+
)
|
57
|
+
expect(corrected).to eq 'it { expect(foo).to be(1) }'
|
58
|
+
end
|
59
|
+
end
|
@@ -92,7 +92,7 @@ describe RuboCop::Cop::RSpec::DescribedClass, :config do
|
|
92
92
|
RUBY
|
93
93
|
end
|
94
94
|
|
95
|
-
it 'ignores describe that do not
|
95
|
+
it 'ignores describe that do not reference to a class' do
|
96
96
|
expect_no_violations(<<-RUBY)
|
97
97
|
describe "MyClass" do
|
98
98
|
subject { "MyClass" }
|
@@ -204,7 +204,7 @@ describe RuboCop::Cop::RSpec::DescribedClass, :config do
|
|
204
204
|
' subject { MyClass.do_something }',
|
205
205
|
' before { MyClass.do_something }',
|
206
206
|
'end'
|
207
|
-
]
|
207
|
+
], 'spec/foo_spec.rb'
|
208
208
|
)
|
209
209
|
expect(new_source).to eq(
|
210
210
|
[
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RuboCop::Cop::RSpec::EmptyExampleGroup, :config do
|
4
|
+
subject(:cop) { described_class.new(config) }
|
5
|
+
|
6
|
+
it 'flags an empty context' do
|
7
|
+
expect_violation(<<-RUBY)
|
8
|
+
describe Foo do
|
9
|
+
context 'when bar' do
|
10
|
+
^^^^^^^^^^^^^^^^^^ Empty example group detected.
|
11
|
+
|
12
|
+
let(:foo) { bar }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#thingy?' do
|
16
|
+
specify do
|
17
|
+
expect(whatever.thingy?).to be(true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it { should be_true }
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'flags an empty top level describe' do
|
27
|
+
expect_violation(<<-RUBY)
|
28
|
+
describe Foo do
|
29
|
+
^^^^^^^^^^^^ Empty example group detected.
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not flag include_examples' do
|
35
|
+
expect_no_violations(<<-RUBY)
|
36
|
+
describe Foo do
|
37
|
+
context "when something is true" do
|
38
|
+
include_examples "some expectations"
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when something else is true" do
|
42
|
+
include_context "some expectations"
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when a third thing is true" do
|
46
|
+
it_behaves_like "some thingy"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
RUBY
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does not recognize custom include methods by default' do
|
53
|
+
expect_violation(<<-RUBY)
|
54
|
+
describe Foo do
|
55
|
+
^^^^^^^^^^^^ Empty example group detected.
|
56
|
+
context "when I do something clever" do
|
57
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Empty example group detected.
|
58
|
+
it_has_special_behavior
|
59
|
+
end
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when a custom include method is specified' do
|
65
|
+
let(:cop_config) do
|
66
|
+
{ 'CustomIncludeMethods' => %w(it_has_special_behavior) }
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'does not flag an otherwise empty example group' do
|
70
|
+
expect_no_violations(<<-RUBY)
|
71
|
+
describe Foo do
|
72
|
+
context "when I do something clever" do
|
73
|
+
it_has_special_behavior
|
74
|
+
end
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
describe RuboCop::Cop::RSpec::ExampleLength, :config do
|
2
2
|
subject(:cop) { described_class.new(config) }
|
3
|
+
|
3
4
|
let(:cop_config) { { 'Max' => 3 } }
|
4
5
|
|
5
6
|
it 'ignores non-spec blocks' do
|
@@ -12,7 +13,8 @@ describe RuboCop::Cop::RSpec::ExampleLength, :config do
|
|
12
13
|
' line 3',
|
13
14
|
' line 4',
|
14
15
|
'end'
|
15
|
-
]
|
16
|
+
],
|
17
|
+
'foo_spec.rb'
|
16
18
|
)
|
17
19
|
|
18
20
|
expect(cop.offenses).to be_empty
|
@@ -24,7 +26,8 @@ describe RuboCop::Cop::RSpec::ExampleLength, :config do
|
|
24
26
|
[
|
25
27
|
'it do',
|
26
28
|
'end'
|
27
|
-
]
|
29
|
+
],
|
30
|
+
'foo_spec.rb'
|
28
31
|
)
|
29
32
|
expect(cop.offenses).to be_empty
|
30
33
|
end
|
@@ -38,60 +41,77 @@ describe RuboCop::Cop::RSpec::ExampleLength, :config do
|
|
38
41
|
' line 2',
|
39
42
|
' line 3',
|
40
43
|
'end'
|
41
|
-
]
|
44
|
+
],
|
45
|
+
'spec/foo_spec.rb'
|
42
46
|
)
|
43
47
|
expect(cop.offenses).to be_empty
|
44
48
|
end
|
45
49
|
|
46
|
-
it
|
50
|
+
it 'ignores comments' do
|
47
51
|
inspect_source(
|
48
52
|
cop,
|
49
53
|
[
|
50
54
|
'it do',
|
51
55
|
' line 1',
|
52
56
|
' line 2',
|
57
|
+
' # comment',
|
53
58
|
' line 3',
|
54
|
-
' line 4',
|
55
59
|
'end'
|
56
|
-
]
|
60
|
+
],
|
61
|
+
'spec/foo_spec.rb'
|
57
62
|
)
|
58
|
-
expect(cop.offenses
|
59
|
-
expect(cop.offenses.map(&:line).sort).to eq([1])
|
60
|
-
expect(cop.messages).to eq(['Example has too many lines. [4/3]'])
|
63
|
+
expect(cop.offenses).to be_empty
|
61
64
|
end
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
cop,
|
66
|
+
shared_examples 'large example violation' do
|
67
|
+
before do
|
68
|
+
inspect_source(cop, source, 'spec/foo_spec.rb')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'flags an offense' do
|
72
|
+
expect(cop.offenses.size).to eq(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'registers the offense on line 1' do
|
76
|
+
expect(cop.offenses.map(&:line)).to eq([1])
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'adds a message saying the example has too many lines' do
|
80
|
+
expect(cop.messages).to eq(['Example has too many lines. [4/3]'])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when inspecting large examples' do
|
85
|
+
let(:source) do
|
66
86
|
[
|
67
87
|
'it do',
|
68
88
|
' line 1',
|
69
89
|
' line 2',
|
70
|
-
' # comment',
|
71
90
|
' line 3',
|
91
|
+
' line 4',
|
72
92
|
'end'
|
73
93
|
]
|
74
|
-
|
75
|
-
|
94
|
+
end
|
95
|
+
|
96
|
+
include_examples 'large example violation'
|
76
97
|
end
|
77
98
|
|
78
99
|
context 'with CountComments enabled' do
|
79
|
-
|
100
|
+
let(:cop_config) do
|
101
|
+
{ 'Max' => 3, 'CountComments' => true }
|
102
|
+
end
|
80
103
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
]
|
91
|
-
)
|
92
|
-
expect(cop.offenses.size).to eq(1)
|
93
|
-
expect(cop.offenses.map(&:line).sort).to eq([1])
|
94
|
-
expect(cop.messages).to eq(['Example has too many lines. [4/3]'])
|
104
|
+
let(:source) do
|
105
|
+
[
|
106
|
+
'it do',
|
107
|
+
' line 1',
|
108
|
+
' line 2',
|
109
|
+
' # comment',
|
110
|
+
' line 3',
|
111
|
+
'end'
|
112
|
+
]
|
95
113
|
end
|
114
|
+
|
115
|
+
include_examples 'large example violation'
|
96
116
|
end
|
97
117
|
end
|