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.
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