rubocop-rspec 1.27.0 → 1.28.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 +11 -0
- data/README.md +1 -1
- data/config/default.yml +15 -10
- data/lib/rubocop/cop/rspec/describe_method.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +2 -4
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -3
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -3
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -3
- data/lib/rubocop/cop/rspec/example_without_description.rb +3 -4
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +8 -23
- data/lib/rubocop/cop/rspec/expect_output.rb +0 -2
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +146 -0
- data/lib/rubocop/cop/rspec/instance_spy.rb +0 -2
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/leading_subject.rb +1 -6
- data/lib/rubocop/cop/rspec/let_before_examples.rb +0 -2
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +4 -4
- data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_never.rb +43 -0
- data/lib/rubocop/cop/rspec/scattered_let.rb +0 -2
- data/lib/rubocop/cop/rspec/shared_context.rb +3 -3
- data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +4 -3
- data/lib/rubocop/rspec/align_let_brace.rb +1 -3
- data/lib/rubocop/rspec/blank_line_separation.rb +6 -0
- data/lib/rubocop/rspec/example_group.rb +0 -7
- data/lib/rubocop/rspec/language/node_pattern.rb +6 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/rubocop-rspec.gemspec +2 -2
- data/spec/project/project_requires_spec.rb +13 -3
- data/spec/rubocop/cop/rspec/before_after_all_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/empty_line_after_example_group_spec.rb +15 -0
- data/spec/rubocop/cop/rspec/factory_bot/attribute_defined_statically_spec.rb +156 -0
- data/spec/rubocop/cop/rspec/let_before_examples_spec.rb +0 -5
- data/spec/rubocop/cop/rspec/missing_example_group_argument_spec.rb +55 -0
- data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +0 -5
- data/spec/rubocop/cop/rspec/receive_never_spec.rb +45 -0
- data/spec/rubocop/cop/rspec/scattered_let_spec.rb +0 -5
- data/spec/shared/smoke_test_examples.rb +25 -0
- data/spec/smoke_tests/empty_spec.rb +0 -0
- data/spec/smoke_tests/factory_bot_spec.rb +11 -0
- data/spec/smoke_tests/no_tests_spec.rb +4 -0
- data/spec/smoke_tests/weird_rspec_spec.rb +233 -0
- data/spec/spec_helper.rb +4 -0
- metadata +24 -11
- data/lib/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically.rb +0 -93
- data/lib/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically.rb +0 -81
- data/spec/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically_spec.rb +0 -139
- data/spec/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically_spec.rb +0 -107
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that the first argument to an example group is not empty.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# describe do
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# RSpec.describe do
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# describe TestedClass do
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# describe "A feature example" do
|
21
|
+
# end
|
22
|
+
class MissingExampleGroupArgument < Cop
|
23
|
+
MSG = 'The first argument to `%<method>s` should not be empty.'.freeze
|
24
|
+
|
25
|
+
def on_block(node)
|
26
|
+
return unless example_group?(node)
|
27
|
+
return if node.send_node.arguments?
|
28
|
+
|
29
|
+
add_offense(node, location: :expression,
|
30
|
+
message: format(MSG, method: node.method_name))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -36,10 +36,6 @@ module RuboCop
|
|
36
36
|
class MultipleSubjects < Cop
|
37
37
|
MSG = 'Do not set more than one subject per example group'.freeze
|
38
38
|
|
39
|
-
def_node_matcher :named_subject?, <<-PATTERN
|
40
|
-
(block (send nil? :subject $sym) args ...)
|
41
|
-
PATTERN
|
42
|
-
|
43
39
|
def on_block(node)
|
44
40
|
return unless example_group?(node)
|
45
41
|
|
@@ -62,6 +58,10 @@ module RuboCop
|
|
62
58
|
|
63
59
|
private
|
64
60
|
|
61
|
+
def named_subject?(node)
|
62
|
+
node.send_node.arguments?
|
63
|
+
end
|
64
|
+
|
65
65
|
def rename_autocorrect(node)
|
66
66
|
lambda do |corrector|
|
67
67
|
corrector.replace(node.send_node.loc.selector, 'let')
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module RSpec
|
4
|
+
# Prefer `not_to receive(...)` over `receive(...).never`.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# # bad
|
9
|
+
# expect(foo).to receive(:bar).never
|
10
|
+
#
|
11
|
+
# # good
|
12
|
+
# expect(foo).not_to receive(:bar)
|
13
|
+
#
|
14
|
+
class ReceiveNever < Cop
|
15
|
+
include RangeHelp
|
16
|
+
|
17
|
+
MSG = 'Use `not_to receive` instead of `never`.'.freeze
|
18
|
+
|
19
|
+
def_node_search :method_on_stub?, '(send nil? :receive ...)'
|
20
|
+
|
21
|
+
def on_send(node)
|
22
|
+
return unless node.method_name == :never && method_on_stub?(node)
|
23
|
+
|
24
|
+
add_offense(
|
25
|
+
node,
|
26
|
+
location: :selector
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def autocorrect(node)
|
31
|
+
lambda do |corrector|
|
32
|
+
corrector.replace(node.parent.loc.selector, 'not_to')
|
33
|
+
range = range_between(
|
34
|
+
node.loc.dot.begin_pos,
|
35
|
+
node.loc.selector.end_pos
|
36
|
+
)
|
37
|
+
corrector.remove(range)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -68,11 +68,11 @@ module RuboCop
|
|
68
68
|
|
69
69
|
def on_block(node)
|
70
70
|
context_with_only_examples(node) do
|
71
|
-
add_shared_item_offense(node, MSG_EXAMPLES)
|
71
|
+
add_shared_item_offense(node.send_node, MSG_EXAMPLES)
|
72
72
|
end
|
73
73
|
|
74
74
|
examples_with_only_context(node) do
|
75
|
-
add_shared_item_offense(node, MSG_CONTEXT)
|
75
|
+
add_shared_item_offense(node.send_node, MSG_CONTEXT)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -100,7 +100,7 @@ module RuboCop
|
|
100
100
|
|
101
101
|
def add_shared_item_offense(node, message)
|
102
102
|
add_offense(
|
103
|
-
node
|
103
|
+
node,
|
104
104
|
location: :expression,
|
105
105
|
message: message
|
106
106
|
)
|
@@ -1,9 +1,8 @@
|
|
1
1
|
require_relative 'rspec/capybara/current_path_expectation'
|
2
2
|
require_relative 'rspec/capybara/feature_methods'
|
3
3
|
|
4
|
+
require_relative 'rspec/factory_bot/attribute_defined_statically'
|
4
5
|
require_relative 'rspec/factory_bot/create_list'
|
5
|
-
require_relative 'rspec/factory_bot/dynamic_attribute_defined_statically'
|
6
|
-
require_relative 'rspec/factory_bot/static_attribute_defined_dynamically'
|
7
6
|
|
8
7
|
begin
|
9
8
|
require_relative 'rspec/rails/http_status'
|
@@ -20,9 +19,9 @@ require_relative 'rspec/be_eql'
|
|
20
19
|
require_relative 'rspec/before_after_all'
|
21
20
|
require_relative 'rspec/context_wording'
|
22
21
|
require_relative 'rspec/describe_class'
|
23
|
-
require_relative 'rspec/described_class'
|
24
22
|
require_relative 'rspec/describe_method'
|
25
23
|
require_relative 'rspec/describe_symbol'
|
24
|
+
require_relative 'rspec/described_class'
|
26
25
|
require_relative 'rspec/empty_example_group'
|
27
26
|
require_relative 'rspec/empty_line_after_example_group'
|
28
27
|
require_relative 'rspec/empty_line_after_final_let'
|
@@ -50,6 +49,7 @@ require_relative 'rspec/let_setup'
|
|
50
49
|
require_relative 'rspec/message_chain'
|
51
50
|
require_relative 'rspec/message_expectation'
|
52
51
|
require_relative 'rspec/message_spies'
|
52
|
+
require_relative 'rspec/missing_example_group_argument'
|
53
53
|
require_relative 'rspec/multiple_describes'
|
54
54
|
require_relative 'rspec/multiple_expectations'
|
55
55
|
require_relative 'rspec/multiple_subjects'
|
@@ -60,6 +60,7 @@ require_relative 'rspec/overwriting_setup'
|
|
60
60
|
require_relative 'rspec/pending'
|
61
61
|
require_relative 'rspec/predicate_matcher'
|
62
62
|
require_relative 'rspec/receive_counts'
|
63
|
+
require_relative 'rspec/receive_never'
|
63
64
|
require_relative 'rspec/repeated_description'
|
64
65
|
require_relative 'rspec/repeated_example'
|
65
66
|
require_relative 'rspec/return_from_stub'
|
@@ -4,9 +4,7 @@ module RuboCop
|
|
4
4
|
module RSpec
|
5
5
|
# Shared behavior for aligning braces for single line lets
|
6
6
|
class AlignLetBrace
|
7
|
-
|
8
|
-
|
9
|
-
def_node_matcher :let?, Language::Helpers::ALL.block_pattern
|
7
|
+
include RuboCop::RSpec::Language::NodePattern
|
10
8
|
|
11
9
|
def initialize(root, token)
|
12
10
|
@root = root
|
@@ -25,6 +25,12 @@ module RuboCop
|
|
25
25
|
source_range(processed_source.buffer, last_line, start, content_length)
|
26
26
|
end
|
27
27
|
|
28
|
+
def last_child?(node)
|
29
|
+
return true unless node.parent && node.parent.begin_type?
|
30
|
+
|
31
|
+
node.equal?(node.parent.children.last)
|
32
|
+
end
|
33
|
+
|
28
34
|
def autocorrect(node)
|
29
35
|
lambda do |corrector|
|
30
36
|
missing_separating_line(node) do |location|
|
@@ -14,13 +14,6 @@ module RuboCop
|
|
14
14
|
ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
|
15
15
|
).block_pattern
|
16
16
|
|
17
|
-
# @!method hook?(node)
|
18
|
-
#
|
19
|
-
# Detect if node is `before`, `after`, `around`
|
20
|
-
def_node_matcher :hook?, Hooks::ALL.block_pattern
|
21
|
-
|
22
|
-
def_node_matcher :subject?, Subject::ALL.block_pattern
|
23
|
-
|
24
17
|
def subjects
|
25
18
|
subjects_in_scope(node)
|
26
19
|
end
|
@@ -14,6 +14,12 @@ module RuboCop
|
|
14
14
|
PATTERN
|
15
15
|
|
16
16
|
def_node_matcher :example?, Examples::ALL.block_pattern
|
17
|
+
|
18
|
+
def_node_matcher :hook?, Hooks::ALL.block_pattern
|
19
|
+
|
20
|
+
def_node_matcher :let?, Helpers::ALL.block_pattern
|
21
|
+
|
22
|
+
def_node_matcher :subject?, Subject::ALL.block_pattern
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
data/rubocop-rspec.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.version = RuboCop::RSpec::Version::STRING
|
21
21
|
spec.platform = Gem::Platform::RUBY
|
22
|
-
spec.required_ruby_version = '>= 2.
|
22
|
+
spec.required_ruby_version = '>= 2.2.0'
|
23
23
|
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
spec.files = Dir[
|
@@ -37,7 +37,7 @@ Gem::Specification.new do |spec|
|
|
37
37
|
'documentation_uri' => 'https://rubocop-rspec.readthedocs.io/'
|
38
38
|
}
|
39
39
|
|
40
|
-
spec.add_runtime_dependency 'rubocop', '>= 0.
|
40
|
+
spec.add_runtime_dependency 'rubocop', '>= 0.58.0'
|
41
41
|
|
42
42
|
spec.add_development_dependency 'rack'
|
43
43
|
spec.add_development_dependency 'rake'
|
@@ -1,8 +1,18 @@
|
|
1
1
|
RSpec.describe 'Project requires' do
|
2
2
|
it 'alphabetizes cop requires' do
|
3
|
-
source = SpecHelper::ROOT.join('lib', 'rubocop
|
4
|
-
|
3
|
+
source = SpecHelper::ROOT.join('lib', 'rubocop', 'cop', 'rspec_cops.rb')
|
4
|
+
captures = source.read.scan(%r{^(require_relative 'rspec/(.*?/)?(.*?)')$})
|
5
5
|
|
6
|
-
|
6
|
+
require_statements = captures.map(&:first)
|
7
|
+
sorted_require_statements =
|
8
|
+
captures.sort_by do |_require_statement, cop_category, name|
|
9
|
+
[cop_category || 'rspec', name]
|
10
|
+
end.map(&:first)
|
11
|
+
|
12
|
+
aggregate_failures do
|
13
|
+
# Sanity check that we actually discovered require statements.
|
14
|
+
expect(captures).not_to be_empty
|
15
|
+
expect(require_statements).to eql(sorted_require_statements)
|
16
|
+
end
|
7
17
|
end
|
8
18
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
RSpec.describe RuboCop::Cop::RSpec::BeforeAfterAll
|
2
|
-
subject(:cop) { described_class.new
|
1
|
+
RSpec.describe RuboCop::Cop::RSpec::BeforeAfterAll do
|
2
|
+
subject(:cop) { described_class.new }
|
3
3
|
|
4
4
|
def message(hook)
|
5
5
|
"Beware of using `#{hook}` as it may cause state to leak between tests. "\
|
@@ -62,6 +62,21 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterExampleGroup do
|
|
62
62
|
RUBY
|
63
63
|
end
|
64
64
|
|
65
|
+
it 'handles describes in an if block' do
|
66
|
+
expect_offense(<<-RUBY)
|
67
|
+
if RUBY_VERSION < 2.3
|
68
|
+
describe 'skips checks under old ruby' do
|
69
|
+
end
|
70
|
+
else
|
71
|
+
describe 'first check' do
|
72
|
+
end
|
73
|
+
^^^ Add an empty line after `describe`.
|
74
|
+
describe 'second check' do
|
75
|
+
end
|
76
|
+
end
|
77
|
+
RUBY
|
78
|
+
end
|
79
|
+
|
65
80
|
bad_example = <<-RUBY
|
66
81
|
RSpec.describe Foo do
|
67
82
|
describe '#bar' do
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe RuboCop::Cop::RSpec::FactoryBot::AttributeDefinedStatically do # rubocop:disable Metrics/LineLength
|
4
|
+
subject(:cop) { described_class.new }
|
5
|
+
|
6
|
+
it 'registers an offense for offending code' do
|
7
|
+
expect_offense(<<-RUBY)
|
8
|
+
FactoryBot.define do
|
9
|
+
factory :post do
|
10
|
+
title "Something"
|
11
|
+
^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
12
|
+
published_at 1.day.from_now
|
13
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
14
|
+
status [:draft, :published].sample
|
15
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
16
|
+
created_at 1.day.ago
|
17
|
+
^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
18
|
+
update_times [Time.current]
|
19
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
20
|
+
meta_tags(foo: Time.current)
|
21
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
22
|
+
end
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'registers an offense in a trait' do
|
28
|
+
expect_offense(<<-RUBY)
|
29
|
+
FactoryBot.define do
|
30
|
+
factory :post do
|
31
|
+
trait :published do
|
32
|
+
title "Something"
|
33
|
+
^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
34
|
+
published_at 1.day.from_now
|
35
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'registers an offense in a transient block' do
|
43
|
+
expect_offense(<<-RUBY)
|
44
|
+
FactoryBot.define do
|
45
|
+
factory :post do
|
46
|
+
transient do
|
47
|
+
title "Something"
|
48
|
+
^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
49
|
+
published_at 1.day.from_now
|
50
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use a block to declare attribute values.
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
RUBY
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'accepts valid factory definitions' do
|
58
|
+
expect_no_offenses(<<-RUBY)
|
59
|
+
FactoryBot.define do
|
60
|
+
factory :post do
|
61
|
+
trait :published do
|
62
|
+
published_at { 1.day.from_now }
|
63
|
+
end
|
64
|
+
created_at { 1.day.ago }
|
65
|
+
status { :draft }
|
66
|
+
comments_count { 0 }
|
67
|
+
title { "Static" }
|
68
|
+
description { FFaker::Lorem.paragraph(10) }
|
69
|
+
recent_statuses { [] }
|
70
|
+
tags { { like_count: 2 } }
|
71
|
+
|
72
|
+
before(:create, &:initialize_something)
|
73
|
+
after(:create, &:rebuild_cache)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'does not add offense if out of factory bot block' do
|
80
|
+
expect_no_offenses(<<-RUBY)
|
81
|
+
status [:draft, :published].sample
|
82
|
+
published_at 1.day.from_now
|
83
|
+
created_at 1.day.ago
|
84
|
+
update_times [Time.current]
|
85
|
+
meta_tags(foo: Time.current)
|
86
|
+
RUBY
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'accepts valid association definitions' do
|
90
|
+
expect_no_offenses(<<-RUBY)
|
91
|
+
FactoryBot.define do
|
92
|
+
factory :post do
|
93
|
+
author age: 42, factory: :user
|
94
|
+
end
|
95
|
+
end
|
96
|
+
RUBY
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'accepts valid sequence definition' do
|
100
|
+
expect_no_offenses(<<-RUBY)
|
101
|
+
FactoryBot.define do
|
102
|
+
factory :post do
|
103
|
+
sequence :negative_numbers, &:-@
|
104
|
+
end
|
105
|
+
end
|
106
|
+
RUBY
|
107
|
+
end
|
108
|
+
|
109
|
+
bad = <<-RUBY
|
110
|
+
FactoryBot.define do
|
111
|
+
factory :post do
|
112
|
+
title "Something"
|
113
|
+
comments_count 0
|
114
|
+
tag Tag::MAGIC
|
115
|
+
recent_statuses []
|
116
|
+
status([:draft, :published].sample)
|
117
|
+
published_at 1.day.from_now
|
118
|
+
created_at(1.day.ago)
|
119
|
+
updated_at Time.current
|
120
|
+
update_times [Time.current]
|
121
|
+
meta_tags(foo: Time.current)
|
122
|
+
other_tags({ foo: Time.current })
|
123
|
+
options color: :blue
|
124
|
+
|
125
|
+
trait :old do
|
126
|
+
published_at 1.week.ago
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
RUBY
|
131
|
+
|
132
|
+
corrected = <<-RUBY
|
133
|
+
FactoryBot.define do
|
134
|
+
factory :post do
|
135
|
+
title { "Something" }
|
136
|
+
comments_count { 0 }
|
137
|
+
tag { Tag::MAGIC }
|
138
|
+
recent_statuses { [] }
|
139
|
+
status { [:draft, :published].sample }
|
140
|
+
published_at { 1.day.from_now }
|
141
|
+
created_at { 1.day.ago }
|
142
|
+
updated_at { Time.current }
|
143
|
+
update_times { [Time.current] }
|
144
|
+
meta_tags { { foo: Time.current } }
|
145
|
+
other_tags { { foo: Time.current } }
|
146
|
+
options { { color: :blue } }
|
147
|
+
|
148
|
+
trait :old do
|
149
|
+
published_at { 1.week.ago }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
RUBY
|
154
|
+
|
155
|
+
include_examples 'autocorrect', bad, corrected
|
156
|
+
end
|