eslint-plugin-traceability 1.6.4 → 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.
- package/README.md +38 -1
- package/lib/src/index.d.ts +28 -25
- package/lib/src/index.js +49 -31
- package/lib/src/maintenance/cli.d.ts +12 -0
- package/lib/src/maintenance/cli.js +279 -0
- package/lib/src/maintenance/detect.js +30 -15
- package/lib/src/maintenance/update.js +42 -34
- package/lib/src/maintenance/utils.js +30 -30
- package/lib/src/rules/helpers/require-story-io.js +51 -15
- package/lib/src/rules/helpers/require-story-visitors.js +5 -16
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
- package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
- package/lib/src/rules/require-story-annotation.js +9 -14
- package/lib/src/rules/valid-annotation-format.js +168 -180
- package/lib/src/rules/valid-req-reference.js +139 -29
- package/lib/src/rules/valid-story-reference.d.ts +7 -0
- package/lib/src/rules/valid-story-reference.js +38 -80
- package/lib/src/utils/annotation-checker.js +2 -145
- package/lib/src/utils/branch-annotation-helpers.js +12 -3
- package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
- package/lib/src/utils/reqAnnotationDetection.js +152 -0
- package/lib/tests/maintenance/cli.test.d.ts +1 -0
- package/lib/tests/maintenance/cli.test.js +172 -0
- package/lib/tests/maintenance/detect-isolated.test.js +68 -1
- package/lib/tests/maintenance/report.test.js +2 -2
- package/lib/tests/rules/require-branch-annotation.test.js +3 -2
- package/lib/tests/rules/require-req-annotation.test.js +57 -68
- package/lib/tests/rules/require-story-annotation.test.js +13 -28
- package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
- package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
- package/lib/tests/rules/valid-annotation-format.test.js +328 -51
- package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
- package/lib/tests/utils/annotation-checker.test.js +24 -17
- package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
- package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
- package/lib/tests/utils/ts-language-options.d.ts +22 -0
- package/lib/tests/utils/ts-language-options.js +27 -0
- package/package.json +12 -3
|
@@ -12,12 +12,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
* @req REQ-ERROR-MESSAGES-CONSISTENT - Verify invalid annotation errors use consistent wording and structure
|
|
13
13
|
* @req REQ-ERROR-MESSAGES-ACTIONABLE - Verify invalid annotation errors provide actionable guidance and examples
|
|
14
14
|
* @req REQ-ERROR-MESSAGES-IDENTIFIERS - Verify invalid annotation errors echo the offending identifier/path in the message
|
|
15
|
+
* Tests for: docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
16
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
17
|
+
* @req REQ-CONFIGURABLE-PATTERNS-STORY - Rule supports configurable story path regex patterns
|
|
18
|
+
* @req REQ-CONFIGURABLE-PATTERNS-REQ - Rule supports configurable requirement ID regex patterns
|
|
19
|
+
* @req REQ-CONFIGURABLE-PATTERNS-EXAMPLES - Rule supports configurable example strings in error messages
|
|
20
|
+
* @req REQ-CONFIGURABLE-PATTERNS-FALLBACK - Invalid regex patterns fall back to default behavior without crashing
|
|
15
21
|
*/
|
|
16
22
|
const eslint_1 = require("eslint");
|
|
17
23
|
const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
|
|
18
24
|
const ruleTester = new eslint_1.RuleTester({
|
|
19
25
|
languageOptions: { parserOptions: { ecmaVersion: 2020 } },
|
|
20
26
|
});
|
|
27
|
+
const makeInvalid = ({ name, code, output, messageId, details, options, }) => ({
|
|
28
|
+
name,
|
|
29
|
+
code,
|
|
30
|
+
...(output ? { output } : {}),
|
|
31
|
+
...(options ? { options } : {}),
|
|
32
|
+
errors: [
|
|
33
|
+
{
|
|
34
|
+
messageId,
|
|
35
|
+
data: {
|
|
36
|
+
details,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Test-only convenience for Story 005.0 error messaging consistency.
|
|
43
|
+
* Preconfigures the invalidStoryFormat messageId so tests only specify
|
|
44
|
+
* name, code, and details (plus optional output/options).
|
|
45
|
+
*/
|
|
46
|
+
const makeInvalidStory = ({ name, code, details, output, options, }) => makeInvalid({
|
|
47
|
+
name,
|
|
48
|
+
code,
|
|
49
|
+
output,
|
|
50
|
+
options,
|
|
51
|
+
messageId: "invalidStoryFormat",
|
|
52
|
+
details,
|
|
53
|
+
});
|
|
21
54
|
describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)", () => {
|
|
22
55
|
ruleTester.run("valid-annotation-format", valid_annotation_format_1.default, {
|
|
23
56
|
valid: [
|
|
@@ -57,148 +90,392 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
|
|
|
57
90
|
* @req REQ-FLEXIBLE-PARSING
|
|
58
91
|
*/`,
|
|
59
92
|
},
|
|
60
|
-
],
|
|
61
|
-
invalid: [
|
|
62
93
|
{
|
|
63
|
-
name: "[REQ-
|
|
64
|
-
code: `// @story`,
|
|
65
|
-
|
|
94
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-STORY] custom storyPathPattern accepts alternate extension",
|
|
95
|
+
code: `// @story stories/feature-010.1-CUSTOM.story.mdx`,
|
|
96
|
+
options: [
|
|
66
97
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
98
|
+
story: {
|
|
99
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
100
|
+
example: "stories/example-010.1-CUSTOM.story.mdx",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-REQ] custom requirementIdPattern accepts PROJECT-123 style IDs",
|
|
107
|
+
code: `// @req PROJECT-123`,
|
|
108
|
+
options: [
|
|
109
|
+
{
|
|
110
|
+
req: {
|
|
111
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
112
|
+
example: "PROJECT-123",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-BOTH] custom patterns accept alternative story and req shapes",
|
|
119
|
+
code: `/**
|
|
120
|
+
* @story stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx
|
|
121
|
+
* @req STORY-10
|
|
122
|
+
*/`,
|
|
123
|
+
options: [
|
|
124
|
+
{
|
|
125
|
+
story: {
|
|
126
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
127
|
+
example: "stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx",
|
|
70
128
|
},
|
|
129
|
+
req: {
|
|
130
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
131
|
+
example: "STORY-10",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-STORY-FLAT] flat storyPathPattern accepts alternate extension when nested config not provided",
|
|
138
|
+
code: `// @story stories/feature-010.1-CUSTOM.story.mdx`,
|
|
139
|
+
options: [
|
|
140
|
+
{
|
|
141
|
+
storyPathPattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
142
|
+
storyPathExample: "stories/example-010.1-CUSTOM.story.mdx",
|
|
71
143
|
},
|
|
72
144
|
],
|
|
73
145
|
},
|
|
74
146
|
{
|
|
147
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-REQ-FLAT] flat requirementIdPattern accepts PROJECT-123 style IDs when nested config not provided",
|
|
148
|
+
code: `// @req PROJECT-123`,
|
|
149
|
+
options: [
|
|
150
|
+
{
|
|
151
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
152
|
+
requirementIdExample: "PROJECT-123",
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-BOTH-FLAT] flat patterns accept alternative story and req shapes when nested config not provided",
|
|
158
|
+
code: `/**
|
|
159
|
+
* @story stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx
|
|
160
|
+
* @req STORY-10
|
|
161
|
+
*/`,
|
|
162
|
+
options: [
|
|
163
|
+
{
|
|
164
|
+
storyPathPattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
165
|
+
storyPathExample: "stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx",
|
|
166
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
167
|
+
requirementIdExample: "STORY-10",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
invalid: [
|
|
173
|
+
makeInvalidStory({
|
|
174
|
+
name: "[REQ-PATH-FORMAT] missing story path (single line)",
|
|
175
|
+
code: `// @story`,
|
|
176
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
177
|
+
}),
|
|
178
|
+
makeInvalidStory({
|
|
75
179
|
name: "[REQ-PATH-FORMAT] invalid story file extension",
|
|
76
180
|
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
77
181
|
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
182
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
183
|
+
}),
|
|
184
|
+
makeInvalidStory({
|
|
185
|
+
name: "[REQ-PATH-FORMAT] missing extension in story path",
|
|
186
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION`,
|
|
187
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
188
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
189
|
+
}),
|
|
190
|
+
makeInvalidStory({
|
|
191
|
+
name: "[REQ-PATH-FORMAT] story path must not use path traversal",
|
|
192
|
+
code: `// @story ../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
193
|
+
details: 'Invalid story path "../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
194
|
+
}),
|
|
195
|
+
makeInvalid({
|
|
196
|
+
name: "[REQ-REQ-FORMAT] missing req id (single line)",
|
|
197
|
+
code: `// @req`,
|
|
198
|
+
messageId: "invalidReqFormat",
|
|
199
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
200
|
+
}),
|
|
201
|
+
makeInvalid({
|
|
202
|
+
name: "[REQ-REQ-FORMAT] invalid req id format (single line)",
|
|
203
|
+
code: `// @req invalid-format`,
|
|
204
|
+
messageId: "invalidReqFormat",
|
|
205
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
206
|
+
}),
|
|
207
|
+
makeInvalid({
|
|
208
|
+
name: "[REQ-REQ-FORMAT] missing req identifier with trailing space",
|
|
209
|
+
code: `// @req `,
|
|
210
|
+
messageId: "invalidReqFormat",
|
|
211
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
212
|
+
}),
|
|
213
|
+
makeInvalidStory({
|
|
214
|
+
name: "[REQ-MULTILINE-SUPPORT] missing story path with multi-line block comment",
|
|
215
|
+
code: `/**
|
|
216
|
+
* @story
|
|
217
|
+
*/`,
|
|
218
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
219
|
+
}),
|
|
220
|
+
makeInvalidStory({
|
|
221
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line story path after collapsing whitespace",
|
|
222
|
+
code: `/**
|
|
223
|
+
* @story docs/stories/005.0-
|
|
224
|
+
* DEV-ANNOTATION-VALIDATION.story
|
|
225
|
+
*/`,
|
|
226
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
227
|
+
}),
|
|
228
|
+
makeInvalid({
|
|
229
|
+
name: "[REQ-MULTILINE-SUPPORT] missing req id with multi-line block comment",
|
|
230
|
+
code: `/**
|
|
231
|
+
* @req
|
|
232
|
+
*/`,
|
|
233
|
+
messageId: "invalidReqFormat",
|
|
234
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
235
|
+
}),
|
|
236
|
+
makeInvalid({
|
|
237
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line req id after collapsing whitespace",
|
|
238
|
+
code: `/**
|
|
239
|
+
* @req invalid-
|
|
240
|
+
* format
|
|
241
|
+
*/`,
|
|
242
|
+
messageId: "invalidReqFormat",
|
|
243
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
244
|
+
}),
|
|
245
|
+
{
|
|
246
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-EXAMPLES] custom story example appears in error message",
|
|
247
|
+
code: `// @story invalid/path.txt`,
|
|
248
|
+
options: [
|
|
249
|
+
{
|
|
250
|
+
story: {
|
|
251
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
252
|
+
example: "stories/example-010.1-CUSTOM.story.mdx",
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
],
|
|
78
256
|
errors: [
|
|
79
257
|
{
|
|
80
258
|
messageId: "invalidStoryFormat",
|
|
81
259
|
data: {
|
|
82
|
-
details: 'Invalid story path "
|
|
260
|
+
details: 'Invalid story path "invalid/path.txt" for @story annotation. Expected a path like "stories/example-010.1-CUSTOM.story.mdx".',
|
|
83
261
|
},
|
|
84
262
|
},
|
|
85
263
|
],
|
|
86
264
|
},
|
|
87
265
|
{
|
|
88
|
-
name: "[REQ-
|
|
89
|
-
code: `// @
|
|
90
|
-
|
|
266
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-EXAMPLES] custom requirement example appears in error message",
|
|
267
|
+
code: `// @req not-matching`,
|
|
268
|
+
options: [
|
|
269
|
+
{
|
|
270
|
+
req: {
|
|
271
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
272
|
+
example: "PROJECT-123",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
],
|
|
91
276
|
errors: [
|
|
92
277
|
{
|
|
93
|
-
messageId: "
|
|
278
|
+
messageId: "invalidReqFormat",
|
|
94
279
|
data: {
|
|
95
|
-
details: 'Invalid
|
|
280
|
+
details: 'Invalid requirement ID "not-matching" for @req annotation. Expected an identifier like "PROJECT-123" (uppercase letters, numbers, and dashes only).',
|
|
96
281
|
},
|
|
97
282
|
},
|
|
98
283
|
],
|
|
99
284
|
},
|
|
100
285
|
{
|
|
101
|
-
name: "[REQ-
|
|
102
|
-
code: `// @story
|
|
286
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK] invalid storyPathPattern falls back to default behavior",
|
|
287
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
288
|
+
options: [
|
|
289
|
+
{
|
|
290
|
+
// invalid regex should be caught by the rule and ignored
|
|
291
|
+
story: {
|
|
292
|
+
pattern: "[unclosed",
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
103
297
|
errors: [
|
|
298
|
+
{
|
|
299
|
+
// Configuration error should also be reported
|
|
300
|
+
messageId: "invalidRuleConfiguration",
|
|
301
|
+
data: {
|
|
302
|
+
details: 'Invalid regular expression for option "story.pattern": "[unclosed"',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
104
305
|
{
|
|
105
306
|
messageId: "invalidStoryFormat",
|
|
106
307
|
data: {
|
|
107
|
-
|
|
308
|
+
// Because we fall back, we still use the default example text
|
|
309
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
108
310
|
},
|
|
109
311
|
},
|
|
110
312
|
],
|
|
111
313
|
},
|
|
112
314
|
{
|
|
113
|
-
name: "[REQ-
|
|
114
|
-
code: `// @req`,
|
|
315
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK] invalid requirementIdPattern falls back to default behavior",
|
|
316
|
+
code: `// @req invalid-format`,
|
|
317
|
+
options: [
|
|
318
|
+
{
|
|
319
|
+
// invalid regex should be caught by the rule and ignored
|
|
320
|
+
req: {
|
|
321
|
+
pattern: "(unclosed",
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
],
|
|
115
325
|
errors: [
|
|
326
|
+
{
|
|
327
|
+
// Configuration error should also be reported
|
|
328
|
+
messageId: "invalidRuleConfiguration",
|
|
329
|
+
data: {
|
|
330
|
+
details: 'Invalid regular expression for option "req.pattern": "(unclosed"',
|
|
331
|
+
},
|
|
332
|
+
},
|
|
116
333
|
{
|
|
117
334
|
messageId: "invalidReqFormat",
|
|
118
335
|
data: {
|
|
119
|
-
|
|
336
|
+
// Because we fall back, we still use the default example text
|
|
337
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
120
338
|
},
|
|
121
339
|
},
|
|
122
340
|
],
|
|
123
341
|
},
|
|
124
342
|
{
|
|
125
|
-
name: "[REQ-
|
|
126
|
-
code: `// @
|
|
343
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK-FLAT] invalid flat storyPathPattern falls back to default and reports config error",
|
|
344
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
345
|
+
options: [
|
|
346
|
+
{
|
|
347
|
+
// invalid regex should be caught by the rule and ignored
|
|
348
|
+
storyPathPattern: "[unclosed",
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
127
352
|
errors: [
|
|
128
353
|
{
|
|
129
|
-
|
|
354
|
+
// Configuration error should also be reported
|
|
355
|
+
messageId: "invalidRuleConfiguration",
|
|
130
356
|
data: {
|
|
131
|
-
details: 'Invalid
|
|
357
|
+
details: 'Invalid regular expression for option "storyPathPattern": "[unclosed"',
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
// Because we fall back, we still use the default example text
|
|
362
|
+
messageId: "invalidStoryFormat",
|
|
363
|
+
data: {
|
|
364
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
132
365
|
},
|
|
133
366
|
},
|
|
134
367
|
],
|
|
135
368
|
},
|
|
136
369
|
{
|
|
137
|
-
name: "[REQ-
|
|
138
|
-
code: `// @req `,
|
|
370
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK-FLAT] invalid flat requirementIdPattern falls back to default and reports config error",
|
|
371
|
+
code: `// @req invalid-format`,
|
|
372
|
+
options: [
|
|
373
|
+
{
|
|
374
|
+
// invalid regex should be caught by the rule and ignored
|
|
375
|
+
requirementIdPattern: "(unclosed",
|
|
376
|
+
},
|
|
377
|
+
],
|
|
139
378
|
errors: [
|
|
140
379
|
{
|
|
380
|
+
// Configuration error should also be reported
|
|
381
|
+
messageId: "invalidRuleConfiguration",
|
|
382
|
+
data: {
|
|
383
|
+
details: 'Invalid regular expression for option "requirementIdPattern": "(unclosed"',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
// Because we fall back, we still use the default example text
|
|
141
388
|
messageId: "invalidReqFormat",
|
|
142
389
|
data: {
|
|
143
|
-
details: '
|
|
390
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
144
391
|
},
|
|
145
392
|
},
|
|
146
393
|
],
|
|
147
394
|
},
|
|
148
395
|
{
|
|
149
|
-
name: "[REQ-
|
|
150
|
-
code:
|
|
151
|
-
|
|
152
|
-
|
|
396
|
+
name: "[REQ-PATTERN-CONFIG] nested story.pattern takes precedence over flat storyPathPattern and its example",
|
|
397
|
+
code: `// @story not-matching.mdx`,
|
|
398
|
+
options: [
|
|
399
|
+
{
|
|
400
|
+
story: {
|
|
401
|
+
pattern: "^stories\\/nested-only\\.story\\.mdx$",
|
|
402
|
+
example: "stories/nested-only.story.mdx",
|
|
403
|
+
},
|
|
404
|
+
storyPathPattern: "^docs\\/stories\\/should-not-apply\\.story\\.mdx$",
|
|
405
|
+
storyPathExample: "docs/stories/should-not-apply.story.mdx",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
153
408
|
errors: [
|
|
154
409
|
{
|
|
155
410
|
messageId: "invalidStoryFormat",
|
|
156
411
|
data: {
|
|
157
|
-
details: '
|
|
412
|
+
details: 'Invalid story path "not-matching.mdx" for @story annotation. Expected a path like "stories/nested-only.story.mdx".',
|
|
158
413
|
},
|
|
159
414
|
},
|
|
160
415
|
],
|
|
161
416
|
},
|
|
162
417
|
{
|
|
163
|
-
name: "[REQ-
|
|
164
|
-
code:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
418
|
+
name: "[REQ-PATTERN-CONFIG] nested req.pattern takes precedence over flat requirementIdPattern and its example",
|
|
419
|
+
code: `// @req DOES-NOT-MATCH`,
|
|
420
|
+
options: [
|
|
421
|
+
{
|
|
422
|
+
req: {
|
|
423
|
+
pattern: "^REQ-[0-9]{4}$",
|
|
424
|
+
example: "REQ-0001",
|
|
425
|
+
},
|
|
426
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
427
|
+
requirementIdExample: "PROJECT-123",
|
|
428
|
+
},
|
|
429
|
+
],
|
|
168
430
|
errors: [
|
|
169
431
|
{
|
|
170
|
-
messageId: "
|
|
432
|
+
messageId: "invalidReqFormat",
|
|
171
433
|
data: {
|
|
172
|
-
details: 'Invalid
|
|
434
|
+
details: 'Invalid requirement ID "DOES-NOT-MATCH" for @req annotation. Expected an identifier like "REQ-0001" (uppercase letters, numbers, and dashes only).',
|
|
173
435
|
},
|
|
174
436
|
},
|
|
175
437
|
],
|
|
176
438
|
},
|
|
177
439
|
{
|
|
178
|
-
name: "[REQ-
|
|
179
|
-
code:
|
|
180
|
-
|
|
181
|
-
|
|
440
|
+
name: "[REQ-EXAMPLE-MESSAGES] nested story example text overrides flat storyPathExample in error messages",
|
|
441
|
+
code: `// @story invalid/path.txt`,
|
|
442
|
+
options: [
|
|
443
|
+
{
|
|
444
|
+
story: {
|
|
445
|
+
pattern: "^stories\\/special\\/.+\\.story\\.mdx$",
|
|
446
|
+
example: "stories/special/example.story.mdx",
|
|
447
|
+
},
|
|
448
|
+
storyPathPattern: "^stories\\/ignored\\/.+\\.story\\.mdx$",
|
|
449
|
+
storyPathExample: "stories/ignored/example.story.mdx",
|
|
450
|
+
},
|
|
451
|
+
],
|
|
182
452
|
errors: [
|
|
183
453
|
{
|
|
184
|
-
messageId: "
|
|
454
|
+
messageId: "invalidStoryFormat",
|
|
185
455
|
data: {
|
|
186
|
-
details: '
|
|
456
|
+
details: 'Invalid story path "invalid/path.txt" for @story annotation. Expected a path like "stories/special/example.story.mdx".',
|
|
187
457
|
},
|
|
188
458
|
},
|
|
189
459
|
],
|
|
190
460
|
},
|
|
191
461
|
{
|
|
192
|
-
name: "[REQ-
|
|
193
|
-
code:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
462
|
+
name: "[REQ-EXAMPLE-MESSAGES] nested req example text overrides flat requirementIdExample in error messages",
|
|
463
|
+
code: `// @req bad-id`,
|
|
464
|
+
options: [
|
|
465
|
+
{
|
|
466
|
+
req: {
|
|
467
|
+
pattern: "^REQ-[A-Z]+-[0-9]{3}$",
|
|
468
|
+
example: "REQ-FOO-001",
|
|
469
|
+
},
|
|
470
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
471
|
+
requirementIdExample: "PROJECT-123",
|
|
472
|
+
},
|
|
473
|
+
],
|
|
197
474
|
errors: [
|
|
198
475
|
{
|
|
199
476
|
messageId: "invalidReqFormat",
|
|
200
477
|
data: {
|
|
201
|
-
details: 'Invalid requirement ID "
|
|
478
|
+
details: 'Invalid requirement ID "bad-id" for @req annotation. Expected an identifier like "REQ-FOO-001" (uppercase letters, numbers, and dashes only).',
|
|
202
479
|
},
|
|
203
480
|
},
|
|
204
481
|
],
|
|
@@ -1 +1,24 @@
|
|
|
1
|
+
type AnnotationCheckerTestConfig = {
|
|
2
|
+
rule: any;
|
|
3
|
+
valid: Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
code: string;
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}>;
|
|
8
|
+
invalid: Array<{
|
|
9
|
+
name: string;
|
|
10
|
+
code: string;
|
|
11
|
+
errors: any[];
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Shared helper for running tests that exercise the annotation-checker logic
|
|
17
|
+
* for TypeScript constructs.
|
|
18
|
+
*
|
|
19
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
20
|
+
* @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
|
|
21
|
+
* @req REQ-TEST-UTILS-TS-LANG - Shared TS RuleTester language options helper
|
|
22
|
+
*/
|
|
23
|
+
export declare function runAnnotationCheckerTests(ruleName: string, config: AnnotationCheckerTestConfig): void;
|
|
1
24
|
export {};
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runAnnotationCheckerTests = runAnnotationCheckerTests;
|
|
3
4
|
/**
|
|
4
5
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
5
6
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
6
7
|
* @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
|
|
8
|
+
* @req REQ-TEST-UTILS-TS-LANG - Shared TS RuleTester language options helper
|
|
7
9
|
*/
|
|
8
10
|
const eslint_1 = require("eslint");
|
|
9
11
|
const annotation_checker_1 = require("../../src/utils/annotation-checker");
|
|
12
|
+
const ts_language_options_1 = require("./ts-language-options");
|
|
10
13
|
const ruleTester = new eslint_1.RuleTester();
|
|
14
|
+
const withTsAnnotationCheckerOptions = (test) => ({
|
|
15
|
+
...test,
|
|
16
|
+
languageOptions: ts_language_options_1.tsRuleTesterLanguageOptions,
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Shared helper for running tests that exercise the annotation-checker logic
|
|
20
|
+
* for TypeScript constructs.
|
|
21
|
+
*
|
|
22
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
23
|
+
* @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
|
|
24
|
+
* @req REQ-TEST-UTILS-TS-LANG - Shared TS RuleTester language options helper
|
|
25
|
+
*/
|
|
26
|
+
function runAnnotationCheckerTests(ruleName, config) {
|
|
27
|
+
const { rule, valid, invalid } = config;
|
|
28
|
+
ruleTester.run(ruleName, rule, {
|
|
29
|
+
valid: valid.map(withTsAnnotationCheckerOptions),
|
|
30
|
+
invalid: invalid.map(withTsAnnotationCheckerOptions),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
11
33
|
const rule = {
|
|
12
34
|
meta: {
|
|
13
35
|
type: "problem",
|
|
@@ -35,23 +57,16 @@ const rule = {
|
|
|
35
57
|
},
|
|
36
58
|
};
|
|
37
59
|
describe("annotation-checker helper", () => {
|
|
38
|
-
|
|
60
|
+
runAnnotationCheckerTests("annotation-checker", {
|
|
61
|
+
rule,
|
|
39
62
|
valid: [
|
|
40
63
|
{
|
|
41
64
|
name: "[REQ-TYPESCRIPT-SUPPORT] valid TSDeclareFunction with @req",
|
|
42
65
|
code: `/** @req REQ-TEST */\ndeclare function foo(): void;`,
|
|
43
|
-
languageOptions: {
|
|
44
|
-
parser: require("@typescript-eslint/parser"),
|
|
45
|
-
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
46
|
-
},
|
|
47
66
|
},
|
|
48
67
|
{
|
|
49
68
|
name: "[REQ-TYPESCRIPT-SUPPORT] valid TSMethodSignature with @req",
|
|
50
69
|
code: `interface I { /** @req REQ-TEST */ method(): void; }`,
|
|
51
|
-
languageOptions: {
|
|
52
|
-
parser: require("@typescript-eslint/parser"),
|
|
53
|
-
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
54
|
-
},
|
|
55
70
|
},
|
|
56
71
|
],
|
|
57
72
|
invalid: [
|
|
@@ -60,20 +75,12 @@ describe("annotation-checker helper", () => {
|
|
|
60
75
|
code: `declare function foo(): void;`,
|
|
61
76
|
output: `/** @req <REQ-ID> */\ndeclare function foo(): void;`,
|
|
62
77
|
errors: [{ messageId: "missingReq" }],
|
|
63
|
-
languageOptions: {
|
|
64
|
-
parser: require("@typescript-eslint/parser"),
|
|
65
|
-
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
66
|
-
},
|
|
67
78
|
},
|
|
68
79
|
{
|
|
69
80
|
name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
|
|
70
81
|
code: `interface I { method(): void; }`,
|
|
71
82
|
output: `interface I { /** @req <REQ-ID> */\nmethod(): void; }`,
|
|
72
83
|
errors: [{ messageId: "missingReq" }],
|
|
73
|
-
languageOptions: {
|
|
74
|
-
parser: require("@typescript-eslint/parser"),
|
|
75
|
-
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
76
|
-
},
|
|
77
84
|
},
|
|
78
85
|
],
|
|
79
86
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test helpers for require-story-core branch coverage.
|
|
3
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
+
* @req REQ-AUTOFIX - Provide reusable helpers to exercise autofix branches
|
|
5
|
+
*/
|
|
6
|
+
interface ExerciseOptions {
|
|
7
|
+
annotationText?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function exerciseCreateAddStoryFixBranches(createAddStoryFix: any, options?: ExerciseOptions): void;
|
|
10
|
+
export {};
|