rubocop-rspec 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +23 -0
  4. data/Rakefile +19 -1
  5. data/config/default.yml +78 -15
  6. data/lib/rubocop-rspec.rb +19 -1
  7. data/lib/rubocop/cop/rspec/any_instance.rb +5 -1
  8. data/lib/rubocop/cop/rspec/be_eql.rb +58 -0
  9. data/lib/rubocop/cop/rspec/describe_class.rb +2 -3
  10. data/lib/rubocop/cop/rspec/describe_method.rb +4 -3
  11. data/lib/rubocop/cop/rspec/described_class.rb +4 -35
  12. data/lib/rubocop/cop/rspec/empty_example_group.rb +100 -0
  13. data/lib/rubocop/cop/rspec/example_length.rb +5 -2
  14. data/lib/rubocop/cop/rspec/example_wording.rb +5 -2
  15. data/lib/rubocop/cop/rspec/expect_actual.rb +79 -0
  16. data/lib/rubocop/cop/rspec/file_path.rb +3 -1
  17. data/lib/rubocop/cop/rspec/focus.rb +12 -28
  18. data/lib/rubocop/cop/rspec/hook_argument.rb +122 -0
  19. data/lib/rubocop/cop/rspec/instance_variable.rb +53 -12
  20. data/lib/rubocop/cop/rspec/leading_subject.rb +58 -0
  21. data/lib/rubocop/cop/rspec/let_setup.rb +58 -0
  22. data/lib/rubocop/cop/rspec/message_chain.rb +33 -0
  23. data/lib/rubocop/cop/rspec/message_expectation.rb +58 -0
  24. data/lib/rubocop/cop/rspec/multiple_describes.rb +10 -7
  25. data/lib/rubocop/cop/rspec/multiple_expectations.rb +89 -0
  26. data/lib/rubocop/cop/rspec/named_subject.rb +10 -1
  27. data/lib/rubocop/cop/rspec/nested_groups.rb +125 -0
  28. data/lib/rubocop/cop/rspec/not_to_not.rb +3 -3
  29. data/lib/rubocop/cop/rspec/subject_stub.rb +136 -0
  30. data/lib/rubocop/cop/rspec/verified_doubles.rb +4 -1
  31. data/lib/rubocop/rspec.rb +10 -0
  32. data/lib/rubocop/rspec/config_formatter.rb +33 -0
  33. data/lib/rubocop/rspec/description_extractor.rb +35 -0
  34. data/lib/rubocop/rspec/inject.rb +2 -6
  35. data/lib/rubocop/rspec/language.rb +73 -0
  36. data/lib/rubocop/rspec/language/node_pattern.rb +16 -0
  37. data/lib/rubocop/rspec/spec_only.rb +61 -0
  38. data/lib/rubocop/rspec/top_level_describe.rb +6 -0
  39. data/lib/rubocop/rspec/version.rb +1 -1
  40. data/rubocop-rspec.gemspec +1 -0
  41. data/spec/project/changelog_spec.rb +81 -0
  42. data/spec/project/default_config_spec.rb +52 -0
  43. data/spec/project/project_requires_spec.rb +8 -0
  44. data/spec/rubocop/cop/rspec/be_eql_spec.rb +59 -0
  45. data/spec/rubocop/cop/rspec/described_class_spec.rb +2 -2
  46. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +79 -0
  47. data/spec/rubocop/cop/rspec/example_length_spec.rb +50 -30
  48. data/spec/rubocop/cop/rspec/example_wording_spec.rb +21 -3
  49. data/spec/rubocop/cop/rspec/expect_actual_spec.rb +136 -0
  50. data/spec/rubocop/cop/rspec/file_path_spec.rb +48 -71
  51. data/spec/rubocop/cop/rspec/focus_spec.rb +1 -1
  52. data/spec/rubocop/cop/rspec/hook_argument_spec.rb +189 -0
  53. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +37 -0
  54. data/spec/rubocop/cop/rspec/leading_subject_spec.rb +54 -0
  55. data/spec/rubocop/cop/rspec/let_setup_spec.rb +66 -0
  56. data/spec/rubocop/cop/rspec/message_chain_spec.rb +21 -0
  57. data/spec/rubocop/cop/rspec/message_expectation_spec.rb +63 -0
  58. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +84 -0
  59. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +55 -0
  60. data/spec/rubocop/cop/rspec/not_to_not_spec.rb +12 -2
  61. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +183 -0
  62. data/spec/rubocop/rspec/config_formatter_spec.rb +48 -0
  63. data/spec/rubocop/rspec/description_extractor_spec.rb +35 -0
  64. data/spec/rubocop/rspec/language/selector_set_spec.rb +29 -0
  65. data/spec/rubocop/rspec/spec_only_spec.rb +97 -0
  66. data/spec/shared/rspec_only_cop_behavior.rb +68 -0
  67. data/spec/spec_helper.rb +13 -1
  68. metadata +72 -5
  69. data/spec/project_spec.rb +0 -115
@@ -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 = '1.6.0'.freeze
7
+ STRING = '1.7.0'.freeze
8
8
  end
9
9
  end
10
10
  end
@@ -38,4 +38,5 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'anima'
39
39
  spec.add_development_dependency 'concord'
40
40
  spec.add_development_dependency 'adamantium'
41
+ spec.add_development_dependency 'yard'
41
42
  end
@@ -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,8 @@
1
+ describe 'Project requires' do
2
+ it 'alphabetizes cop requires' do
3
+ source = SpecHelper::ROOT.join('lib', 'rubocop-rspec.rb').read
4
+ requires = source.split("\n").grep(%r{rubocop/cop})
5
+
6
+ expect(requires.join("\n")).to eql(requires.sort.join("\n"))
7
+ end
8
+ 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 referece to a class' do
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 "doesn't allow a long example" do
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.size).to eq(1)
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
- it 'ignores comments' do
64
- inspect_source(
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
- expect(cop.offenses).to be_empty
94
+ end
95
+
96
+ include_examples 'large example violation'
76
97
  end
77
98
 
78
99
  context 'with CountComments enabled' do
79
- before { cop_config['CountComments'] = true }
100
+ let(:cop_config) do
101
+ { 'Max' => 3, 'CountComments' => true }
102
+ end
80
103
 
81
- it 'counts comments' do
82
- inspect_source(
83
- cop, [
84
- 'it do',
85
- ' line 1',
86
- ' line 2',
87
- ' # comment',
88
- ' line 3',
89
- 'end'
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