forked from rubocop/rubocop-rspec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnested_groups.rb
162 lines (148 loc) · 4.36 KB
/
nested_groups.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks for nested example groups.
#
# This cop is configurable using the `Max` option
# and supports `--auto-gen-config`.
#
# @example
# # bad
# context 'when using some feature' do
# let(:some) { :various }
# let(:feature) { :setup }
#
# context 'when user is signed in' do # flagged by rubocop
# let(:user) do
# UserCreate.call(user_attributes)
# end
#
# let(:user_attributes) do
# {
# name: 'John',
# age: 22,
# role: role
# }
# end
#
# context 'when user is an admin' do # flagged by rubocop
# let(:role) { 'admin' }
#
# it 'blah blah'
# it 'yada yada'
# end
# end
# end
#
# # good
# context 'using some feature as an admin' do
# let(:some) { :various }
# let(:feature) { :setup }
#
# let(:user) do
# UserCreate.call(
# name: 'John',
# age: 22,
# role: 'admin'
# )
# end
#
# it 'blah blah'
# it 'yada yada'
# end
#
# @example `Max: 3` (default)
# # bad
# describe Foo do
# context 'foo' do
# context 'bar' do
# context 'baz' do # flagged by rubocop
# end
# end
# end
# end
#
# @example `Max: 2`
# # bad
# describe Foo do
# context 'foo' do
# context 'bar' do # flagged by rubocop
# context 'baz' do # flagged by rubocop
# end
# end
# end
# end
#
# @example `AllowedGroups: [] (default)`
# describe Foo do # <-- nested groups 1
# context 'foo' do # <-- nested groups 2
# context 'bar' do # <-- nested groups 3
# end
# end
# end
#
# @example `AllowedGroups: [path]`
# describe Foo do # <-- nested groups 1
# path '/foo' do # <-- nested groups 1 (not counted)
# context 'bar' do # <-- nested groups 2
# end
# end
# end
#
class NestedGroups < Base
include ConfigurableMax
include TopLevelGroup
MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
DEPRECATED_MAX_KEY = 'MaxNesting'
DEPRECATION_WARNING =
"Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \
'deprecated in favor of `Max`. Please use that instead.'
def on_top_level_group(node)
find_nested_example_groups(node) do |example_group, nesting|
self.max = nesting
add_offense(
example_group.send_node,
message: message(nesting)
)
end
end
private
def find_nested_example_groups(node, nesting: 1, &block)
example_group = example_group?(node)
yield node, nesting if example_group && nesting > max_nesting
next_nesting = if count_up_nesting?(node, example_group)
nesting + 1
else
nesting
end
node.each_child_node(:block, :begin) do |child|
find_nested_example_groups(child, nesting: next_nesting, &block)
end
end
def count_up_nesting?(node, example_group)
example_group &&
(node.block_type? &&
!allowed_groups.include?(node.method_name))
end
def message(nesting)
format(MSG, total: nesting, max: max_nesting)
end
def max_nesting
@max_nesting ||= Integer(max_nesting_config)
end
def max_nesting_config
if cop_config.key?(DEPRECATED_MAX_KEY)
warn DEPRECATION_WARNING
cop_config.fetch(DEPRECATED_MAX_KEY)
else
cop_config.fetch('Max', 3)
end
end
def allowed_groups
@allowed_groups ||= cop_config.fetch('AllowedGroups', [])
end
end
end
end
end