rubocop-aaa 0.0.1 → 0.0.2
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/README.md +6 -0
- data/lib/rubocop/cop/aaa/pattern.rb +87 -12
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e6004f573b4c0674b182fb676a0736cb40e9bbe92118c0c77e50ffb9c4f3c035
|
|
4
|
+
data.tar.gz: 0aa8a6cb45918ee3a50aa68bcaea9e97808c5166386c271a646b0749ea4654e9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8efaad9641a3826f7a343d06d0b3b489e4046e572831923015c80a868f146006e519ce82e30126515b6c0fb98c6b2bc0d0494803d2c2358e28f7d5cc491a0d72
|
|
7
|
+
data.tar.gz: f1ed6f5e2ba4596918841a93c4d081ef20109e18335da6bb00900e21132addedb03bf62fb950302da25e09ab1b3a2a3c82f1d6927e790fb97830b31e22159fb3
|
data/README.md
CHANGED
|
@@ -83,6 +83,12 @@ AAA/Pattern:
|
|
|
83
83
|
assert: [検証, 確認]
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
## Auto-correct
|
|
87
|
+
|
|
88
|
+
When **all three** section comments are missing, `rubocop -a` (or `rubocop --autocorrect`) inserts a `# arrange` / `# act` / `# assert` template at the top of the block. You then move each comment above the code that belongs to it.
|
|
89
|
+
|
|
90
|
+
Other cases — one or two sections missing, wrong order, empty section — are reported with explicit hints in the offense message but are not auto-corrected, because the correct insertion point depends on the test's intent.
|
|
91
|
+
|
|
86
92
|
## License
|
|
87
93
|
|
|
88
94
|
MIT
|
|
@@ -2,11 +2,16 @@ module RuboCop
|
|
|
2
2
|
module Cop
|
|
3
3
|
module AAA
|
|
4
4
|
class Pattern < Base
|
|
5
|
+
extend AutoCorrector
|
|
6
|
+
|
|
5
7
|
SECTIONS = %i[arrange act assert].freeze
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
MSG_MISSING_ALL = 'Missing arrange/act/assert section comments. Insert # %<arrange>s, # %<act>s, and # %<assert>s to mark each section (auto-correct scaffolds a template at the top of the block).'.freeze
|
|
10
|
+
MSG_MISSING_ARRANGE = 'Missing "arrange" comment. Insert # %<label>s before the code that sets up test state.'.freeze
|
|
11
|
+
MSG_MISSING_ACT = 'Missing "act" comment. Insert # %<label>s before the code that triggers the behavior under test.'.freeze
|
|
12
|
+
MSG_MISSING_ASSERT = 'Missing "assert" comment. Insert # %<label>s before the code that verifies the result.'.freeze
|
|
13
|
+
MSG_ORDER = 'AAA comments must appear in order: arrange -> act -> assert. Found "%<found>s" after "%<previous>s".'.freeze
|
|
14
|
+
MSG_EMPTY = 'Section "%<section>s" has no statements. Add code between # %<section>s and the next section, or remove the # %<section>s comment.'.freeze
|
|
10
15
|
|
|
11
16
|
DEFAULT_LABELS = {
|
|
12
17
|
'arrange' => %w[arrange],
|
|
@@ -23,19 +28,27 @@ module RuboCop
|
|
|
23
28
|
seen = {}
|
|
24
29
|
found.each { |section, comment| seen[section] ||= comment }
|
|
25
30
|
|
|
26
|
-
SECTIONS.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
missing = SECTIONS.reject { |s| seen[s] }
|
|
32
|
+
|
|
33
|
+
if missing.size == SECTIONS.size
|
|
34
|
+
report_all_missing(node)
|
|
35
|
+
return
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
unless missing.empty?
|
|
39
|
+
missing.each { |s| report_missing_one(node, s) }
|
|
40
|
+
return
|
|
31
41
|
end
|
|
32
42
|
|
|
33
43
|
ordered = SECTIONS.map { |s| seen[s] }
|
|
34
44
|
(1...ordered.size).each do |i|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
next unless ordered[i].loc.expression.begin_pos < ordered[i - 1].loc.expression.begin_pos
|
|
46
|
+
|
|
47
|
+
add_offense(
|
|
48
|
+
ordered[i],
|
|
49
|
+
message: format(MSG_ORDER, found: SECTIONS[i], previous: SECTIONS[i - 1])
|
|
50
|
+
)
|
|
51
|
+
return
|
|
39
52
|
end
|
|
40
53
|
|
|
41
54
|
return if allow_empty_section?
|
|
@@ -46,6 +59,64 @@ module RuboCop
|
|
|
46
59
|
|
|
47
60
|
private
|
|
48
61
|
|
|
62
|
+
def report_all_missing(node)
|
|
63
|
+
labels_for_template = {
|
|
64
|
+
arrange: preferred_label('arrange'),
|
|
65
|
+
act: preferred_label('act'),
|
|
66
|
+
assert: preferred_label('assert')
|
|
67
|
+
}
|
|
68
|
+
message = format(MSG_MISSING_ALL, labels_for_template)
|
|
69
|
+
|
|
70
|
+
add_offense(node, message: message) do |corrector|
|
|
71
|
+
insert_template(corrector, node, labels_for_template)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def report_missing_one(node, section)
|
|
76
|
+
message_template = {
|
|
77
|
+
arrange: MSG_MISSING_ARRANGE,
|
|
78
|
+
act: MSG_MISSING_ACT,
|
|
79
|
+
assert: MSG_MISSING_ASSERT
|
|
80
|
+
}[section]
|
|
81
|
+
# Anchor each offense to a distinct location so RuboCop does not dedupe
|
|
82
|
+
# them when multiple sections are missing.
|
|
83
|
+
location = {
|
|
84
|
+
arrange: node.send_node.loc.expression,
|
|
85
|
+
act: node.loc.begin,
|
|
86
|
+
assert: node.loc.end
|
|
87
|
+
}[section]
|
|
88
|
+
add_offense(location, message: format(message_template, label: preferred_label(section.to_s)))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def insert_template(corrector, node, labels_for_template)
|
|
92
|
+
source = processed_source.raw_source
|
|
93
|
+
block_start = node.loc.begin.end_pos # position right after "do" / "{"
|
|
94
|
+
indent = detect_indent(node, block_start)
|
|
95
|
+
|
|
96
|
+
template = +"\n#{indent}# #{labels_for_template[:arrange]}"
|
|
97
|
+
template << "\n#{indent}# #{labels_for_template[:act]}"
|
|
98
|
+
template << "\n#{indent}# #{labels_for_template[:assert]}"
|
|
99
|
+
|
|
100
|
+
corrector.insert_after(node.loc.begin, template)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def detect_indent(node, block_start)
|
|
104
|
+
source = processed_source.raw_source
|
|
105
|
+
# Prefer the indentation of the first line of body content.
|
|
106
|
+
tail = source[block_start..-1] || ''
|
|
107
|
+
tail.each_line do |line|
|
|
108
|
+
next if line.strip.empty?
|
|
109
|
+
|
|
110
|
+
stripped = line.sub(/^\n/, '')
|
|
111
|
+
match = stripped.match(/\A([ \t]*)\S/)
|
|
112
|
+
return match[1] if match
|
|
113
|
+
end
|
|
114
|
+
# Fallback: use the block's own indent + 2 spaces.
|
|
115
|
+
block_line = source[0...node.loc.expression.begin_pos].rpartition("\n").last
|
|
116
|
+
lead = block_line.match(/\A[ \t]*/)[0]
|
|
117
|
+
"#{lead} "
|
|
118
|
+
end
|
|
119
|
+
|
|
49
120
|
def collect_sections(node)
|
|
50
121
|
block_range = node.loc.expression
|
|
51
122
|
processed_source.comments.each_with_object([]) do |comment, acc|
|
|
@@ -98,6 +169,10 @@ module RuboCop
|
|
|
98
169
|
comment.text.sub(/\A#\s*/, '').strip
|
|
99
170
|
end
|
|
100
171
|
|
|
172
|
+
def preferred_label(section)
|
|
173
|
+
(labels[section] && labels[section].first) || section
|
|
174
|
+
end
|
|
175
|
+
|
|
101
176
|
def test_method?(name)
|
|
102
177
|
test_functions.include?(name.to_s)
|
|
103
178
|
end
|