rubocop-vicenzo 0.1.0 → 0.2.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/.rubocop.yml +5 -0
- data/CHANGELOG.md +17 -0
- data/config/default.yml +24 -9
- data/lib/rubocop/cop/vicenzo/rails/enum_inclusion_of_validation.rb +4 -3
- data/lib/rubocop/cop/vicenzo/rspec/inconsistent_sibling_structure.rb +117 -0
- data/lib/rubocop/cop/vicenzo/rspec/leaky_definition.rb +104 -0
- data/lib/rubocop/cop/vicenzo/rspec/nested_context_improper_start.rb +25 -12
- data/lib/rubocop/cop/vicenzo/rspec/nested_let_redefinition.rb +3 -2
- data/lib/rubocop/cop/vicenzo/rspec/nested_subject_redefinition.rb +1 -1
- data/lib/rubocop/cop/vicenzo_cops.rb +2 -1
- data/lib/rubocop/vicenzo/version.rb +1 -1
- metadata +7 -5
- data/lib/rubocop/cop/vicenzo/rspec/mixed_example_groups.rb +0 -65
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b430ab160562cd140cd498e015dbf4e2c03a6bd99189ae24adc00881a3bbab48
|
|
4
|
+
data.tar.gz: 3757afc640413d2ed6548319003ecf62be4cc84658673d430e5ff8236936f2d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b2cbdc348b8faa2dd97e722e78e5c8956b67da26df8541314990331a645b156c758540d6602960e8f96340a6294bf0a35a833b4f2d5a1a237349bbb91f58ba6
|
|
7
|
+
data.tar.gz: 88b77a6119a722404ad6d263f61ed53423e4991ae216bd8ea2295712fa72c5536b902948bea8c61e2f32e832444e356c2a0b16ae2f4a8727993acd063db40130
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2025-11-27
|
|
4
|
+
|
|
5
|
+
- Remove RuboCop::Cop::Vicenzo::RSpec::MixedExampleGroups in favor of InconsistentSiblingStructure #10;
|
|
6
|
+
|
|
7
|
+
- Add RoboCop::Cop::Vicenzo::RSpec::LeakyDefinition #9;
|
|
8
|
+
- Add RoboCop::Cop::Vicenzo::RSpec::InconsistentSiblingStructure #10;
|
|
9
|
+
|
|
10
|
+
- Fix NestedContextImproperStart to deal with all nested contexts #10;
|
|
11
|
+
- Fix NestedLetRedefinition to not point sibling lets as nested #10;
|
|
12
|
+
- Fix NestedSubjectRedefinition to not point sibling lets as nested #10;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## [0.1.1] - 2025-08-12
|
|
16
|
+
|
|
17
|
+
- Add Rightly enable all cops #7;
|
|
18
|
+
- Fix RuboCop::Cop::Vicenzo::Rails::EnumInclusionOfValidation working with array format and no options #7;
|
|
19
|
+
|
|
3
20
|
## [0.1.0] - 2025-04-02
|
|
4
21
|
|
|
5
22
|
- Initial release;
|
data/config/default.yml
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
Vicenzo/
|
|
2
|
-
Description: 'Check if
|
|
3
|
-
Enabled:
|
|
1
|
+
Vicenzo/Rails/EnumInclusionOfValidation:
|
|
2
|
+
Description: 'Check if the enum has the inclusion of validation defined.'
|
|
3
|
+
Enabled: true
|
|
4
|
+
Severity: convention
|
|
4
5
|
VersionAdded: '0.1.0'
|
|
5
6
|
|
|
7
|
+
Vicenzo/RSpec/InconsistentSiblingStructure:
|
|
8
|
+
Description: 'Enforces strict structural consistency (e.g. prevents mixing describe with context or examples with groups).'
|
|
9
|
+
Enabled: true
|
|
10
|
+
Severity: warning
|
|
11
|
+
VersionAdded: '0.2.0'
|
|
12
|
+
|
|
6
13
|
Vicenzo/RSpec/NestedContextImproperStart:
|
|
7
14
|
Description: 'Check if the nested context does not start as a root one.'
|
|
8
|
-
Enabled:
|
|
15
|
+
Enabled: true
|
|
16
|
+
Severity: convention
|
|
9
17
|
VersionAdded: '0.1.0'
|
|
10
18
|
|
|
11
19
|
Vicenzo/RSpec/NestedLetRedefinition:
|
|
@@ -16,10 +24,17 @@ Vicenzo/RSpec/NestedLetRedefinition:
|
|
|
16
24
|
|
|
17
25
|
Vicenzo/RSpec/NestedSubjectRedefinition:
|
|
18
26
|
Description: 'Check if a subject is redefined in a nested example group.'
|
|
19
|
-
Enabled:
|
|
27
|
+
Enabled: true
|
|
28
|
+
Severity: warning
|
|
20
29
|
VersionAdded: '0.1.0'
|
|
21
30
|
|
|
22
|
-
Vicenzo/
|
|
23
|
-
Description: '
|
|
24
|
-
Enabled:
|
|
25
|
-
|
|
31
|
+
Vicenzo/RSpec/LeakyDefinition:
|
|
32
|
+
Description: 'Do not define methods, classes, or modules directly in spec files (unless inside spec/support).'
|
|
33
|
+
Enabled: true
|
|
34
|
+
Severity: warning
|
|
35
|
+
VersionAdded: '0.2.0'
|
|
36
|
+
Include:
|
|
37
|
+
- '**/spec/**/*_spec.rb'
|
|
38
|
+
Exclude:
|
|
39
|
+
- '**/spec/support/**/*'
|
|
40
|
+
- '**/spec/factories/**/*'
|
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
54
|
def find_validate_option(enum_node)
|
|
55
|
-
enum_node
|
|
55
|
+
options_node_for(enum_node)&.each_pair&.find { |pair| pair.key.value == :validate }
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def valid_validate_option?(validate_kwarg)
|
|
@@ -62,14 +62,15 @@ module RuboCop
|
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def options_node_for(enum_node)
|
|
66
66
|
enum_node.last_argument if enum_node.last_argument.hash_type?
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def register_offence_for(enum_node, validate_kwarg)
|
|
70
70
|
if validate_kwarg.nil?
|
|
71
71
|
add_offense(enum_node, message: MSG_MISSING_VALIDATE) do |corrector|
|
|
72
|
-
|
|
72
|
+
last_node = options_node_for(enum_node) || enum_node.last_argument
|
|
73
|
+
corrector.insert_after(last_node, ', validate: { allow_nil: true }')
|
|
73
74
|
end
|
|
74
75
|
elsif !valid_validate_option?(validate_kwarg)
|
|
75
76
|
add_offense(validate_kwarg, message: MSG_INVALID_VALIDATE) do |corrector|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vicenzo
|
|
6
|
+
module RSpec
|
|
7
|
+
# Enforces strict structural consistency in RSpec files.
|
|
8
|
+
#
|
|
9
|
+
# It forbids mixing:
|
|
10
|
+
# 1. Examples (`it`) with Groups (`describe` or `context`)
|
|
11
|
+
# 2. Different types of Groups (`describe` with `context`)
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# # bad (Mixing Describe and Context)
|
|
15
|
+
# RSpec.describe User do
|
|
16
|
+
# describe '#admin?' do ... end
|
|
17
|
+
# context 'when user is logged' do ... end
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# # bad (Mixing Example and Context)
|
|
21
|
+
# RSpec.describe User do
|
|
22
|
+
# it { is_expected.to be_valid }
|
|
23
|
+
# context 'when invalid' do ... end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# # good
|
|
27
|
+
# RSpec.describe User do
|
|
28
|
+
# describe '#admin?' do ... end
|
|
29
|
+
# describe '#client?' do ... end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
class InconsistentSiblingStructure < RuboCop::Cop::RSpec::Base
|
|
33
|
+
MSG = 'Do not mix %<type_a>s with %<type_b>s at the same level.'
|
|
34
|
+
|
|
35
|
+
EXAMPLES = %i[it specify example scenario focus].freeze
|
|
36
|
+
DESCRIBES = %i[describe feature experiment].freeze
|
|
37
|
+
CONTEXTS = %i[context].freeze
|
|
38
|
+
|
|
39
|
+
# @!method example_definition?(node)
|
|
40
|
+
def_node_matcher :example_definition?, <<~PATTERN
|
|
41
|
+
(block (send nil? {#{EXAMPLES.map(&:inspect).join(' ')}} ...) ...)
|
|
42
|
+
PATTERN
|
|
43
|
+
|
|
44
|
+
# @!method describe_definition?(node)
|
|
45
|
+
def_node_matcher :describe_definition?, <<~PATTERN
|
|
46
|
+
(block (send nil? {#{DESCRIBES.map(&:inspect).join(' ')}} ...) ...)
|
|
47
|
+
PATTERN
|
|
48
|
+
|
|
49
|
+
# @!method context_definition?(node)
|
|
50
|
+
def_node_matcher :context_definition?, <<~PATTERN
|
|
51
|
+
(block (send nil? {#{CONTEXTS.map(&:inspect).join(' ')}} ...) ...)
|
|
52
|
+
PATTERN
|
|
53
|
+
|
|
54
|
+
def on_block(node)
|
|
55
|
+
return unless example_group?(node)
|
|
56
|
+
return unless node.body
|
|
57
|
+
|
|
58
|
+
# Normaliza e classifica em passos separados
|
|
59
|
+
children = child_nodes_for(node)
|
|
60
|
+
found_nodes = classify_children(children)
|
|
61
|
+
|
|
62
|
+
validate_consistency(found_nodes)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
alias on_numblock on_block
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def child_nodes_for(node)
|
|
70
|
+
node.body.begin_type? ? node.body.each_child_node : [node.body]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def classify_children(nodes)
|
|
74
|
+
classified = { example: [], describe: [], context: [] }
|
|
75
|
+
|
|
76
|
+
nodes.each do |child|
|
|
77
|
+
next unless child.block_type?
|
|
78
|
+
|
|
79
|
+
type = node_type(child)
|
|
80
|
+
classified[type] << child if type
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
classified
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def node_type(node)
|
|
87
|
+
if example_definition?(node)
|
|
88
|
+
:example
|
|
89
|
+
elsif describe_definition?(node)
|
|
90
|
+
:describe
|
|
91
|
+
elsif context_definition?(node)
|
|
92
|
+
:context
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def validate_consistency(nodes)
|
|
97
|
+
present_types = nodes.keys.select { |type| nodes[type].any? }
|
|
98
|
+
|
|
99
|
+
return if present_types.size <= 1
|
|
100
|
+
|
|
101
|
+
check_pair(nodes, :example, :describe)
|
|
102
|
+
check_pair(nodes, :example, :context)
|
|
103
|
+
check_pair(nodes, :describe, :context)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def check_pair(nodes, type_a, type_b)
|
|
107
|
+
return unless nodes[type_a].any? && nodes[type_b].any?
|
|
108
|
+
|
|
109
|
+
nodes[type_b].each do |node|
|
|
110
|
+
add_offense(node, message: format(MSG, type_a: type_a, type_b: type_b))
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubocop'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
module Vicenzo
|
|
8
|
+
module RSpec
|
|
9
|
+
# Checks for methods, classes, or modules defined directly within
|
|
10
|
+
# RSpec blocks or at the top level of a spec file.
|
|
11
|
+
#
|
|
12
|
+
# Such definitions pollute the global namespace or the test class scope,
|
|
13
|
+
# leading to state leaking and intermittent test failures.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# describe User do
|
|
18
|
+
# def setup_user
|
|
19
|
+
# # ...
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # bad
|
|
24
|
+
# def global_helper
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# # bad
|
|
28
|
+
# def stub_service
|
|
29
|
+
# allow(Service).to receive(:call)
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# # good
|
|
33
|
+
# describe User do
|
|
34
|
+
# let(:user) { create(:user) }
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# # good (inside anonymous class)
|
|
38
|
+
# let(:model) do
|
|
39
|
+
# Class.new do
|
|
40
|
+
# def safe_method; end
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# # good
|
|
45
|
+
# before do
|
|
46
|
+
# allow(Service).to receive(:call)
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
class LeakyDefinition < RuboCop::Cop::RSpec::Base
|
|
50
|
+
MSG = 'Do not define methods, classes, or modules directly in the global scope or within spec blocks. ' \
|
|
51
|
+
'This pollutes the namespace. ' \
|
|
52
|
+
'Move this logic to `spec/support`, use `let`, before, ' \
|
|
53
|
+
'or encapsulate it within a safe structure (e.g., `Class.new`).'
|
|
54
|
+
|
|
55
|
+
# Matcher to identify dynamic class/module definitions commonly used in specs
|
|
56
|
+
# Looks for blocks applied to Class.new, Module.new, or Struct.new
|
|
57
|
+
# considers controller {} a valid mock
|
|
58
|
+
# @!method dynamic_definition?(node)
|
|
59
|
+
def_node_matcher :dynamic_definition?, <<~PATTERN
|
|
60
|
+
(block
|
|
61
|
+
{
|
|
62
|
+
(send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
|
|
63
|
+
(send nil? :controller ...)
|
|
64
|
+
}
|
|
65
|
+
...
|
|
66
|
+
)
|
|
67
|
+
PATTERN
|
|
68
|
+
|
|
69
|
+
def on_def(node)
|
|
70
|
+
check_node(node)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def on_class(node)
|
|
74
|
+
check_node(node)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def on_module(node)
|
|
78
|
+
check_node(node)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def check_node(node)
|
|
84
|
+
# If inside a safe scope (static or dynamic class/module), it's allowed.
|
|
85
|
+
return if inside_safe_scope?(node)
|
|
86
|
+
|
|
87
|
+
add_offense(node)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def inside_safe_scope?(node)
|
|
91
|
+
# Traverse ancestors to find a "protective shield"
|
|
92
|
+
node.each_ancestor.any? do |ancestor|
|
|
93
|
+
# 1. Is it a traditional definition? (class Foo; end)
|
|
94
|
+
next true if ancestor.type?(:class, :module)
|
|
95
|
+
|
|
96
|
+
# 2. Is it a dynamic definition? (Class.new do; end)
|
|
97
|
+
next true if dynamic_definition?(ancestor)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -26,32 +26,45 @@ module RuboCop
|
|
|
26
26
|
|
|
27
27
|
FORBIDDEN_PREFIXES = %w[when with without].freeze
|
|
28
28
|
|
|
29
|
+
# @!method context_block?(node)
|
|
30
|
+
def_node_matcher :context_block?, <<~PATTERN
|
|
31
|
+
(block (send nil? :context ...) ...)
|
|
32
|
+
PATTERN
|
|
33
|
+
|
|
34
|
+
# @!method example_group_block?(node)
|
|
35
|
+
def_node_matcher :example_group_block?, <<~PATTERN
|
|
36
|
+
(block (send nil? {:describe :context :feature :example_group} ...) ...)
|
|
37
|
+
PATTERN
|
|
38
|
+
|
|
29
39
|
def on_block(node)
|
|
30
|
-
return unless context_block?(node)
|
|
40
|
+
return unless context_block?(node)
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
return unless context_description
|
|
42
|
+
parent = find_closest_example_group(node)
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
return unless FORBIDDEN_PREFIXES.include?(first_word)
|
|
44
|
+
return unless parent && context_block?(parent)
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
check_description(node)
|
|
39
47
|
end
|
|
40
48
|
|
|
41
49
|
alias on_numblock on_block
|
|
42
50
|
|
|
43
51
|
private
|
|
44
52
|
|
|
45
|
-
def
|
|
46
|
-
|
|
53
|
+
def find_closest_example_group(node)
|
|
54
|
+
node.each_ancestor(:block).find { |ancestor| example_group_block?(ancestor) }
|
|
47
55
|
end
|
|
48
56
|
|
|
49
|
-
def
|
|
50
|
-
|
|
57
|
+
def check_description(node)
|
|
58
|
+
description_node = node.send_node.first_argument
|
|
51
59
|
|
|
52
|
-
return
|
|
60
|
+
return unless description_node&.str_type?
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
text = description_node.value.to_s.strip
|
|
63
|
+
first_word = text.split.first&.downcase
|
|
64
|
+
|
|
65
|
+
return unless FORBIDDEN_PREFIXES.include?(first_word)
|
|
66
|
+
|
|
67
|
+
add_offense(node.send_node)
|
|
55
68
|
end
|
|
56
69
|
end
|
|
57
70
|
end
|
|
@@ -90,11 +90,12 @@ module RuboCop
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def check_let(let_node, let_definitions)
|
|
93
|
-
name = (let_name(let_node) || let_it_be_name(let_node))
|
|
93
|
+
name = (let_name(let_node) || let_it_be_name(let_node))&.to_s&.to_sym
|
|
94
94
|
|
|
95
95
|
if let_definitions.key?(name)
|
|
96
96
|
add_offense(let_node, message: redefined_let_message(name, let_definitions))
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
let_definitions[name] += [line_location(let_node)]
|
|
98
99
|
else
|
|
99
100
|
let_definitions[name] = [line_location(let_node)]
|
|
100
101
|
end
|
|
@@ -96,7 +96,7 @@ module RuboCop
|
|
|
96
96
|
|
|
97
97
|
if subject_definitions.key?(name)
|
|
98
98
|
add_offense(subject_node, message: redefined_subject_message(name, subject_definitions))
|
|
99
|
-
subject_definitions[name]
|
|
99
|
+
subject_definitions[name] += [line_location(subject_node)]
|
|
100
100
|
else
|
|
101
101
|
subject_definitions[name] = [line_location(subject_node)]
|
|
102
102
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'vicenzo/rspec/
|
|
3
|
+
require_relative 'vicenzo/rspec/inconsistent_sibling_structure'
|
|
4
4
|
require_relative 'vicenzo/rspec/nested_context_improper_start'
|
|
5
5
|
require_relative 'vicenzo/rspec/nested_let_redefinition'
|
|
6
6
|
require_relative 'vicenzo/rspec/nested_subject_redefinition'
|
|
7
|
+
require_relative 'vicenzo/rspec/leaky_definition'
|
|
7
8
|
require_relative 'vicenzo/rails/enum_inclusion_of_validation'
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-vicenzo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bruno Vicenzo
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: lint_roller
|
|
@@ -51,7 +51,8 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: 3.5.0
|
|
54
|
-
description:
|
|
54
|
+
description: A growing set of custom RuboCop cops capturing the best practices I've
|
|
55
|
+
been learning.
|
|
55
56
|
email:
|
|
56
57
|
- bruno@alumni.usp.br
|
|
57
58
|
executables: []
|
|
@@ -68,7 +69,8 @@ files:
|
|
|
68
69
|
- config/default.yml
|
|
69
70
|
- lib/rubocop-vicenzo.rb
|
|
70
71
|
- lib/rubocop/cop/vicenzo/rails/enum_inclusion_of_validation.rb
|
|
71
|
-
- lib/rubocop/cop/vicenzo/rspec/
|
|
72
|
+
- lib/rubocop/cop/vicenzo/rspec/inconsistent_sibling_structure.rb
|
|
73
|
+
- lib/rubocop/cop/vicenzo/rspec/leaky_definition.rb
|
|
72
74
|
- lib/rubocop/cop/vicenzo/rspec/nested_context_improper_start.rb
|
|
73
75
|
- lib/rubocop/cop/vicenzo/rspec/nested_let_redefinition.rb
|
|
74
76
|
- lib/rubocop/cop/vicenzo/rspec/nested_subject_redefinition.rb
|
|
@@ -101,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
101
103
|
- !ruby/object:Gem::Version
|
|
102
104
|
version: '0'
|
|
103
105
|
requirements: []
|
|
104
|
-
rubygems_version: 3.
|
|
106
|
+
rubygems_version: 3.7.2
|
|
105
107
|
specification_version: 4
|
|
106
108
|
summary: Cops of Bruno Vicenzo
|
|
107
109
|
test_files: []
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Vicenzo
|
|
6
|
-
module RSpec
|
|
7
|
-
# Ensures that examples (`it`, `specify`, `example`)
|
|
8
|
-
# are not mixed with groups (`describe`, `context`) at the same level.
|
|
9
|
-
#
|
|
10
|
-
# @example
|
|
11
|
-
# # bad
|
|
12
|
-
# RSpec.describe User do
|
|
13
|
-
# it { is_expected.to validate_presence_of(:name) }
|
|
14
|
-
# describe '#admin?' do
|
|
15
|
-
# it { expect(true).to eq(true) }
|
|
16
|
-
# end
|
|
17
|
-
# end
|
|
18
|
-
#
|
|
19
|
-
# # bad
|
|
20
|
-
# RSpec.describe User do
|
|
21
|
-
# describe '#admin?' do
|
|
22
|
-
# it { expect(true).to eq(true) }
|
|
23
|
-
# context 'when email starts with' do
|
|
24
|
-
# end
|
|
25
|
-
# end
|
|
26
|
-
# end
|
|
27
|
-
#
|
|
28
|
-
# # good
|
|
29
|
-
# RSpec.describe User do
|
|
30
|
-
# describe '#admin?' do
|
|
31
|
-
# context 'when email starts with' do
|
|
32
|
-
# it { expect(true).to eq(true) }
|
|
33
|
-
# end
|
|
34
|
-
# end
|
|
35
|
-
# end
|
|
36
|
-
class MixedExampleGroups < RuboCop::Cop::RSpec::Base
|
|
37
|
-
MSG = 'Do not mix examples (`it`, `specify`, `example`) with groups (`describe`, `context`) ' \
|
|
38
|
-
'at the same level.'
|
|
39
|
-
|
|
40
|
-
def on_block(node)
|
|
41
|
-
return unless example_or_group?(node)
|
|
42
|
-
|
|
43
|
-
parent = node.parent
|
|
44
|
-
return unless parent
|
|
45
|
-
|
|
46
|
-
children = parent.children.select { |child| example_or_group?(child) }
|
|
47
|
-
example_nodes, group_nodes = children.partition { |n| example?(n) }
|
|
48
|
-
|
|
49
|
-
return if example_nodes.empty? || group_nodes.empty?
|
|
50
|
-
|
|
51
|
-
add_offense(node)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
alias on_numblock on_block
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
def example_or_group?(node)
|
|
59
|
-
example?(node) || example_group?(node)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|