eslint-plugin-traceability 1.6.5 → 1.7.1
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 +39 -1
- package/lib/src/index.d.ts +30 -27
- package/lib/src/index.js +51 -31
- package/lib/src/maintenance/cli.d.ts +12 -0
- package/lib/src/maintenance/cli.js +279 -0
- package/lib/src/maintenance/detect.js +27 -12
- 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/valid-annotation-format-internal.d.ts +30 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
- 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-implements-utils.d.ts +75 -0
- package/lib/src/rules/helpers/valid-implements-utils.js +149 -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/prefer-implements-annotation.d.ts +39 -0
- package/lib/src/rules/prefer-implements-annotation.js +276 -0
- package/lib/src/rules/valid-annotation-format.js +255 -208
- package/lib/src/rules/valid-req-reference.js +210 -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/plugin-default-export-and-configs.test.js +3 -0
- package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
- package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
- 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 +395 -40
- package/lib/tests/rules/valid-req-reference.test.js +34 -0
- 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,50 @@ 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
|
|
21
|
+
* Tests for: docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
22
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
23
|
+
* @req REQ-IMPLEMENTS-PARSE - Rule parses @implements annotations with story and requirement references
|
|
24
|
+
* @req REQ-FORMAT-VALIDATION - Rule validates story and requirement formats inside @implements annotations
|
|
25
|
+
* @req REQ-MIXED-SUPPORT - Rule supports mixed @story/@req/@implements usage in the same comment
|
|
15
26
|
*/
|
|
16
27
|
const eslint_1 = require("eslint");
|
|
17
28
|
const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
|
|
18
29
|
const ruleTester = new eslint_1.RuleTester({
|
|
19
30
|
languageOptions: { parserOptions: { ecmaVersion: 2020 } },
|
|
20
31
|
});
|
|
32
|
+
const makeInvalid = ({ name, code, output, messageId, details, options, }) => ({
|
|
33
|
+
name,
|
|
34
|
+
code,
|
|
35
|
+
...(output ? { output } : {}),
|
|
36
|
+
...(options ? { options } : {}),
|
|
37
|
+
errors: [
|
|
38
|
+
{
|
|
39
|
+
messageId,
|
|
40
|
+
data: {
|
|
41
|
+
details,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Test-only convenience for Story 005.0 error messaging consistency.
|
|
48
|
+
* Preconfigures the invalidStoryFormat messageId so tests only specify
|
|
49
|
+
* name, code, and details (plus optional output/options).
|
|
50
|
+
*/
|
|
51
|
+
const makeInvalidStory = ({ name, code, details, output, options, }) => makeInvalid({
|
|
52
|
+
name,
|
|
53
|
+
code,
|
|
54
|
+
output,
|
|
55
|
+
options,
|
|
56
|
+
messageId: "invalidStoryFormat",
|
|
57
|
+
details,
|
|
58
|
+
});
|
|
21
59
|
describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)", () => {
|
|
22
60
|
ruleTester.run("valid-annotation-format", valid_annotation_format_1.default, {
|
|
23
61
|
valid: [
|
|
@@ -55,150 +93,467 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
|
|
|
55
93
|
code: `/**
|
|
56
94
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
57
95
|
* @req REQ-FLEXIBLE-PARSING
|
|
96
|
+
*/`,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-STORY] custom storyPathPattern accepts alternate extension",
|
|
100
|
+
code: `// @story stories/feature-010.1-CUSTOM.story.mdx`,
|
|
101
|
+
options: [
|
|
102
|
+
{
|
|
103
|
+
story: {
|
|
104
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
105
|
+
example: "stories/example-010.1-CUSTOM.story.mdx",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-REQ] custom requirementIdPattern accepts PROJECT-123 style IDs",
|
|
112
|
+
code: `// @req PROJECT-123`,
|
|
113
|
+
options: [
|
|
114
|
+
{
|
|
115
|
+
req: {
|
|
116
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
117
|
+
example: "PROJECT-123",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-BOTH] custom patterns accept alternative story and req shapes",
|
|
124
|
+
code: `/**
|
|
125
|
+
* @story stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx
|
|
126
|
+
* @req STORY-10
|
|
127
|
+
*/`,
|
|
128
|
+
options: [
|
|
129
|
+
{
|
|
130
|
+
story: {
|
|
131
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
132
|
+
example: "stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx",
|
|
133
|
+
},
|
|
134
|
+
req: {
|
|
135
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
136
|
+
example: "STORY-10",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-STORY-FLAT] flat storyPathPattern accepts alternate extension when nested config not provided",
|
|
143
|
+
code: `// @story stories/feature-010.1-CUSTOM.story.mdx`,
|
|
144
|
+
options: [
|
|
145
|
+
{
|
|
146
|
+
storyPathPattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
147
|
+
storyPathExample: "stories/example-010.1-CUSTOM.story.mdx",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-REQ-FLAT] flat requirementIdPattern accepts PROJECT-123 style IDs when nested config not provided",
|
|
153
|
+
code: `// @req PROJECT-123`,
|
|
154
|
+
options: [
|
|
155
|
+
{
|
|
156
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
157
|
+
requirementIdExample: "PROJECT-123",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-BOTH-FLAT] flat patterns accept alternative story and req shapes when nested config not provided",
|
|
163
|
+
code: `/**
|
|
164
|
+
* @story stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx
|
|
165
|
+
* @req STORY-10
|
|
166
|
+
*/`,
|
|
167
|
+
options: [
|
|
168
|
+
{
|
|
169
|
+
storyPathPattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
170
|
+
storyPathExample: "stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.mdx",
|
|
171
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
172
|
+
requirementIdExample: "STORY-10",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "[REQ-IMPLEMENTS-PARSE] valid single @implements with one story and one requirement (default patterns)",
|
|
178
|
+
code: `/**
|
|
179
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE
|
|
180
|
+
*/`,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "[REQ-IMPLEMENTS-PARSE] valid multiple @implements lines with different stories and requirements",
|
|
184
|
+
code: `/**
|
|
185
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION
|
|
186
|
+
* @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-FORMAT-SPECIFICATION
|
|
187
|
+
*/`,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "[REQ-MIXED-SUPPORT] valid mixed @story/@req/@implements usage in same block comment",
|
|
191
|
+
code: `/**
|
|
192
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
193
|
+
* @req REQ-MIXED-SUPPORT
|
|
194
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION REQ-MIXED-SUPPORT
|
|
58
195
|
*/`,
|
|
59
196
|
},
|
|
60
197
|
],
|
|
61
198
|
invalid: [
|
|
62
|
-
{
|
|
199
|
+
makeInvalidStory({
|
|
63
200
|
name: "[REQ-PATH-FORMAT] missing story path (single line)",
|
|
64
201
|
code: `// @story`,
|
|
202
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
203
|
+
}),
|
|
204
|
+
makeInvalidStory({
|
|
205
|
+
name: "[REQ-PATH-FORMAT] invalid story file extension",
|
|
206
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
207
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
208
|
+
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".',
|
|
209
|
+
}),
|
|
210
|
+
makeInvalidStory({
|
|
211
|
+
name: "[REQ-PATH-FORMAT] missing extension in story path",
|
|
212
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION`,
|
|
213
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
214
|
+
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".',
|
|
215
|
+
}),
|
|
216
|
+
makeInvalidStory({
|
|
217
|
+
name: "[REQ-PATH-FORMAT] story path must not use path traversal",
|
|
218
|
+
code: `// @story ../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
219
|
+
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".',
|
|
220
|
+
}),
|
|
221
|
+
makeInvalid({
|
|
222
|
+
name: "[REQ-REQ-FORMAT] missing req id (single line)",
|
|
223
|
+
code: `// @req`,
|
|
224
|
+
messageId: "invalidReqFormat",
|
|
225
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
226
|
+
}),
|
|
227
|
+
makeInvalid({
|
|
228
|
+
name: "[REQ-REQ-FORMAT] invalid req id format (single line)",
|
|
229
|
+
code: `// @req invalid-format`,
|
|
230
|
+
messageId: "invalidReqFormat",
|
|
231
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
232
|
+
}),
|
|
233
|
+
makeInvalid({
|
|
234
|
+
name: "[REQ-REQ-FORMAT] missing req identifier with trailing space",
|
|
235
|
+
code: `// @req `,
|
|
236
|
+
messageId: "invalidReqFormat",
|
|
237
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
238
|
+
}),
|
|
239
|
+
makeInvalidStory({
|
|
240
|
+
name: "[REQ-MULTILINE-SUPPORT] missing story path with multi-line block comment",
|
|
241
|
+
code: `/**
|
|
242
|
+
* @story
|
|
243
|
+
*/`,
|
|
244
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
245
|
+
}),
|
|
246
|
+
makeInvalidStory({
|
|
247
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line story path after collapsing whitespace",
|
|
248
|
+
code: `/**
|
|
249
|
+
* @story docs/stories/005.0-
|
|
250
|
+
* DEV-ANNOTATION-VALIDATION.story
|
|
251
|
+
*/`,
|
|
252
|
+
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".',
|
|
253
|
+
}),
|
|
254
|
+
makeInvalid({
|
|
255
|
+
name: "[REQ-MULTILINE-SUPPORT] missing req id with multi-line block comment",
|
|
256
|
+
code: `/**
|
|
257
|
+
* @req
|
|
258
|
+
*/`,
|
|
259
|
+
messageId: "invalidReqFormat",
|
|
260
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
261
|
+
}),
|
|
262
|
+
makeInvalid({
|
|
263
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line req id after collapsing whitespace",
|
|
264
|
+
code: `/**
|
|
265
|
+
* @req invalid-
|
|
266
|
+
* format
|
|
267
|
+
*/`,
|
|
268
|
+
messageId: "invalidReqFormat",
|
|
269
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
270
|
+
}),
|
|
271
|
+
{
|
|
272
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-EXAMPLES] custom story example appears in error message",
|
|
273
|
+
code: `// @story invalid/path.txt`,
|
|
274
|
+
options: [
|
|
275
|
+
{
|
|
276
|
+
story: {
|
|
277
|
+
pattern: "^stories\\/[^\\s]+\\.story\\.mdx$",
|
|
278
|
+
example: "stories/example-010.1-CUSTOM.story.mdx",
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
],
|
|
65
282
|
errors: [
|
|
66
283
|
{
|
|
67
284
|
messageId: "invalidStoryFormat",
|
|
68
285
|
data: {
|
|
69
|
-
details: '
|
|
286
|
+
details: 'Invalid story path "invalid/path.txt" for @story annotation. Expected a path like "stories/example-010.1-CUSTOM.story.mdx".',
|
|
70
287
|
},
|
|
71
288
|
},
|
|
72
289
|
],
|
|
73
290
|
},
|
|
74
291
|
{
|
|
75
|
-
name: "[REQ-
|
|
292
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-EXAMPLES] custom requirement example appears in error message",
|
|
293
|
+
code: `// @req not-matching`,
|
|
294
|
+
options: [
|
|
295
|
+
{
|
|
296
|
+
req: {
|
|
297
|
+
pattern: "^[A-Z]+-[0-9]+$",
|
|
298
|
+
example: "PROJECT-123",
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
errors: [
|
|
303
|
+
{
|
|
304
|
+
messageId: "invalidReqFormat",
|
|
305
|
+
data: {
|
|
306
|
+
details: 'Invalid requirement ID "not-matching" for @req annotation. Expected an identifier like "PROJECT-123" (uppercase letters, numbers, and dashes only).',
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK] invalid storyPathPattern falls back to default behavior",
|
|
76
313
|
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
314
|
+
options: [
|
|
315
|
+
{
|
|
316
|
+
// invalid regex should be caught by the rule and ignored
|
|
317
|
+
story: {
|
|
318
|
+
pattern: "[unclosed",
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
],
|
|
77
322
|
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
78
323
|
errors: [
|
|
324
|
+
{
|
|
325
|
+
// Configuration error should also be reported
|
|
326
|
+
messageId: "invalidRuleConfiguration",
|
|
327
|
+
data: {
|
|
328
|
+
details: 'Invalid regular expression for option "story.pattern": "[unclosed"',
|
|
329
|
+
},
|
|
330
|
+
},
|
|
79
331
|
{
|
|
80
332
|
messageId: "invalidStoryFormat",
|
|
81
333
|
data: {
|
|
334
|
+
// Because we fall back, we still use the default example text
|
|
82
335
|
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".',
|
|
83
336
|
},
|
|
84
337
|
},
|
|
85
338
|
],
|
|
86
339
|
},
|
|
87
340
|
{
|
|
88
|
-
name: "[REQ-
|
|
89
|
-
code: `// @
|
|
90
|
-
|
|
341
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK] invalid requirementIdPattern falls back to default behavior",
|
|
342
|
+
code: `// @req invalid-format`,
|
|
343
|
+
options: [
|
|
344
|
+
{
|
|
345
|
+
// invalid regex should be caught by the rule and ignored
|
|
346
|
+
req: {
|
|
347
|
+
pattern: "(unclosed",
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
],
|
|
91
351
|
errors: [
|
|
92
352
|
{
|
|
93
|
-
|
|
353
|
+
// Configuration error should also be reported
|
|
354
|
+
messageId: "invalidRuleConfiguration",
|
|
355
|
+
data: {
|
|
356
|
+
details: 'Invalid regular expression for option "req.pattern": "(unclosed"',
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
messageId: "invalidReqFormat",
|
|
94
361
|
data: {
|
|
95
|
-
|
|
362
|
+
// Because we fall back, we still use the default example text
|
|
363
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
96
364
|
},
|
|
97
365
|
},
|
|
98
366
|
],
|
|
99
367
|
},
|
|
100
368
|
{
|
|
101
|
-
name: "[REQ-
|
|
102
|
-
code: `// @story
|
|
369
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK-FLAT] invalid flat storyPathPattern falls back to default and reports config error",
|
|
370
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
371
|
+
options: [
|
|
372
|
+
{
|
|
373
|
+
// invalid regex should be caught by the rule and ignored
|
|
374
|
+
storyPathPattern: "[unclosed",
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
103
378
|
errors: [
|
|
104
379
|
{
|
|
380
|
+
// Configuration error should also be reported
|
|
381
|
+
messageId: "invalidRuleConfiguration",
|
|
382
|
+
data: {
|
|
383
|
+
details: 'Invalid regular expression for option "storyPathPattern": "[unclosed"',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
// Because we fall back, we still use the default example text
|
|
105
388
|
messageId: "invalidStoryFormat",
|
|
106
389
|
data: {
|
|
107
|
-
details: 'Invalid story path "
|
|
390
|
+
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
391
|
},
|
|
109
392
|
},
|
|
110
393
|
],
|
|
111
394
|
},
|
|
112
395
|
{
|
|
113
|
-
name: "[REQ-
|
|
114
|
-
code: `// @req`,
|
|
396
|
+
name: "[REQ-CONFIGURABLE-PATTERNS-FALLBACK-FLAT] invalid flat requirementIdPattern falls back to default and reports config error",
|
|
397
|
+
code: `// @req invalid-format`,
|
|
398
|
+
options: [
|
|
399
|
+
{
|
|
400
|
+
// invalid regex should be caught by the rule and ignored
|
|
401
|
+
requirementIdPattern: "(unclosed",
|
|
402
|
+
},
|
|
403
|
+
],
|
|
115
404
|
errors: [
|
|
116
405
|
{
|
|
406
|
+
// Configuration error should also be reported
|
|
407
|
+
messageId: "invalidRuleConfiguration",
|
|
408
|
+
data: {
|
|
409
|
+
details: 'Invalid regular expression for option "requirementIdPattern": "(unclosed"',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
// Because we fall back, we still use the default example text
|
|
117
414
|
messageId: "invalidReqFormat",
|
|
118
415
|
data: {
|
|
119
|
-
details: '
|
|
416
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
120
417
|
},
|
|
121
418
|
},
|
|
122
419
|
],
|
|
123
420
|
},
|
|
124
421
|
{
|
|
125
|
-
name: "[REQ-
|
|
126
|
-
code: `// @
|
|
422
|
+
name: "[REQ-PATTERN-CONFIG] nested story.pattern takes precedence over flat storyPathPattern and its example",
|
|
423
|
+
code: `// @story not-matching.mdx`,
|
|
424
|
+
options: [
|
|
425
|
+
{
|
|
426
|
+
story: {
|
|
427
|
+
pattern: "^stories\\/nested-only\\.story\\.mdx$",
|
|
428
|
+
example: "stories/nested-only.story.mdx",
|
|
429
|
+
},
|
|
430
|
+
storyPathPattern: "^docs\\/stories\\/should-not-apply\\.story\\.mdx$",
|
|
431
|
+
storyPathExample: "docs/stories/should-not-apply.story.mdx",
|
|
432
|
+
},
|
|
433
|
+
],
|
|
127
434
|
errors: [
|
|
128
435
|
{
|
|
129
|
-
messageId: "
|
|
436
|
+
messageId: "invalidStoryFormat",
|
|
130
437
|
data: {
|
|
131
|
-
details: 'Invalid
|
|
438
|
+
details: 'Invalid story path "not-matching.mdx" for @story annotation. Expected a path like "stories/nested-only.story.mdx".',
|
|
132
439
|
},
|
|
133
440
|
},
|
|
134
441
|
],
|
|
135
442
|
},
|
|
136
443
|
{
|
|
137
|
-
name: "[REQ-
|
|
138
|
-
code: `// @req `,
|
|
444
|
+
name: "[REQ-PATTERN-CONFIG] nested req.pattern takes precedence over flat requirementIdPattern and its example",
|
|
445
|
+
code: `// @req DOES-NOT-MATCH`,
|
|
446
|
+
options: [
|
|
447
|
+
{
|
|
448
|
+
req: {
|
|
449
|
+
pattern: "^REQ-[0-9]{4}$",
|
|
450
|
+
example: "REQ-0001",
|
|
451
|
+
},
|
|
452
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
453
|
+
requirementIdExample: "PROJECT-123",
|
|
454
|
+
},
|
|
455
|
+
],
|
|
139
456
|
errors: [
|
|
140
457
|
{
|
|
141
458
|
messageId: "invalidReqFormat",
|
|
142
459
|
data: {
|
|
143
|
-
details: '
|
|
460
|
+
details: 'Invalid requirement ID "DOES-NOT-MATCH" for @req annotation. Expected an identifier like "REQ-0001" (uppercase letters, numbers, and dashes only).',
|
|
144
461
|
},
|
|
145
462
|
},
|
|
146
463
|
],
|
|
147
464
|
},
|
|
148
465
|
{
|
|
149
|
-
name: "[REQ-
|
|
150
|
-
code:
|
|
151
|
-
|
|
152
|
-
|
|
466
|
+
name: "[REQ-EXAMPLE-MESSAGES] nested story example text overrides flat storyPathExample in error messages",
|
|
467
|
+
code: `// @story invalid/path.txt`,
|
|
468
|
+
options: [
|
|
469
|
+
{
|
|
470
|
+
story: {
|
|
471
|
+
pattern: "^stories\\/special\\/.+\\.story\\.mdx$",
|
|
472
|
+
example: "stories/special/example.story.mdx",
|
|
473
|
+
},
|
|
474
|
+
storyPathPattern: "^stories\\/ignored\\/.+\\.story\\.mdx$",
|
|
475
|
+
storyPathExample: "stories/ignored/example.story.mdx",
|
|
476
|
+
},
|
|
477
|
+
],
|
|
153
478
|
errors: [
|
|
154
479
|
{
|
|
155
480
|
messageId: "invalidStoryFormat",
|
|
156
481
|
data: {
|
|
157
|
-
details: '
|
|
482
|
+
details: 'Invalid story path "invalid/path.txt" for @story annotation. Expected a path like "stories/special/example.story.mdx".',
|
|
158
483
|
},
|
|
159
484
|
},
|
|
160
485
|
],
|
|
161
486
|
},
|
|
162
487
|
{
|
|
163
|
-
name: "[REQ-
|
|
164
|
-
code:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
488
|
+
name: "[REQ-EXAMPLE-MESSAGES] nested req example text overrides flat requirementIdExample in error messages",
|
|
489
|
+
code: `// @req bad-id`,
|
|
490
|
+
options: [
|
|
491
|
+
{
|
|
492
|
+
req: {
|
|
493
|
+
pattern: "^REQ-[A-Z]+-[0-9]{3}$",
|
|
494
|
+
example: "REQ-FOO-001",
|
|
495
|
+
},
|
|
496
|
+
requirementIdPattern: "^[A-Z]+-[0-9]+$",
|
|
497
|
+
requirementIdExample: "PROJECT-123",
|
|
498
|
+
},
|
|
499
|
+
],
|
|
168
500
|
errors: [
|
|
169
501
|
{
|
|
170
|
-
messageId: "
|
|
502
|
+
messageId: "invalidReqFormat",
|
|
171
503
|
data: {
|
|
172
|
-
details: 'Invalid
|
|
504
|
+
details: 'Invalid requirement ID "bad-id" for @req annotation. Expected an identifier like "REQ-FOO-001" (uppercase letters, numbers, and dashes only).',
|
|
173
505
|
},
|
|
174
506
|
},
|
|
175
507
|
],
|
|
176
508
|
},
|
|
509
|
+
makeInvalid({
|
|
510
|
+
name: "[REQ-IMPLEMENTS-PARSE] @implements with no value is invalid",
|
|
511
|
+
code: `/**
|
|
512
|
+
* @implements
|
|
513
|
+
*/`,
|
|
514
|
+
messageId: "invalidImplementsFormat",
|
|
515
|
+
details: 'Missing story path and requirement IDs for @implements annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
|
|
516
|
+
}),
|
|
517
|
+
makeInvalid({
|
|
518
|
+
name: "[REQ-IMPLEMENTS-PARSE] @implements with only story path and no requirement IDs is invalid",
|
|
519
|
+
code: `/**
|
|
520
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
521
|
+
*/`,
|
|
522
|
+
messageId: "invalidImplementsFormat",
|
|
523
|
+
details: 'Missing requirement IDs for @implements annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
|
|
524
|
+
}),
|
|
525
|
+
makeInvalid({
|
|
526
|
+
name: "[REQ-FORMAT-VALIDATION] @implements with invalid story path format",
|
|
527
|
+
code: `/**
|
|
528
|
+
* @implements invalid/path.txt REQ-IMPLEMENTS-PARSE
|
|
529
|
+
*/`,
|
|
530
|
+
messageId: "invalidImplementsFormat",
|
|
531
|
+
details: 'Invalid story path "invalid/path.txt" for @implements annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
532
|
+
}),
|
|
177
533
|
{
|
|
178
|
-
name: "[REQ-
|
|
534
|
+
name: "[REQ-FORMAT-VALIDATION] @implements with invalid requirement ID format",
|
|
179
535
|
code: `/**
|
|
180
|
-
* @
|
|
536
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID invalid-format
|
|
181
537
|
*/`,
|
|
182
538
|
errors: [
|
|
183
539
|
{
|
|
184
540
|
messageId: "invalidReqFormat",
|
|
185
541
|
data: {
|
|
186
|
-
details: '
|
|
542
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
187
543
|
},
|
|
188
544
|
},
|
|
189
545
|
],
|
|
190
546
|
},
|
|
191
547
|
{
|
|
192
|
-
name: "[REQ-
|
|
548
|
+
name: "[REQ-FORMAT-VALIDATION] @implements with multiple requirement IDs where one is invalid",
|
|
193
549
|
code: `/**
|
|
194
|
-
* @
|
|
195
|
-
* format
|
|
550
|
+
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID-1 REQ-VALID-2 bad-id
|
|
196
551
|
*/`,
|
|
197
552
|
errors: [
|
|
198
553
|
{
|
|
199
554
|
messageId: "invalidReqFormat",
|
|
200
555
|
data: {
|
|
201
|
-
details: 'Invalid requirement ID "
|
|
556
|
+
details: 'Invalid requirement ID "bad-id" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
202
557
|
},
|
|
203
558
|
},
|
|
204
559
|
],
|
|
@@ -32,6 +32,15 @@ describe("Valid Req Reference Rule (Story 010.0-DEV-DEEP-VALIDATION)", () => {
|
|
|
32
32
|
code: `// @story tests/fixtures/story_bullet.md
|
|
33
33
|
// @req REQ-BULLET-LIST`,
|
|
34
34
|
},
|
|
35
|
+
{
|
|
36
|
+
name: "[REQ-DEEP-IMPLEMENTS] single implements line with multiple requirements in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
|
|
37
|
+
code: `// @implements tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A`,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "[REQ-DEEP-IMPLEMENTS] multi-story implements with shared requirement IDs (see 010.2-DEV-MULTI-STORY-SUPPORT)",
|
|
41
|
+
code: `// @implements tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A
|
|
42
|
+
// @implements tests/fixtures/story_multi_b.md REQ-SHARED-ID REQ-ONLY-B`,
|
|
43
|
+
},
|
|
35
44
|
],
|
|
36
45
|
invalid: [
|
|
37
46
|
{
|
|
@@ -88,6 +97,31 @@ describe("Valid Req Reference Rule (Story 010.0-DEV-DEEP-VALIDATION)", () => {
|
|
|
88
97
|
},
|
|
89
98
|
],
|
|
90
99
|
},
|
|
100
|
+
{
|
|
101
|
+
name: "[REQ-DEEP-IMPLEMENTS] missing implements requirement in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
|
|
102
|
+
code: `// @implements tests/fixtures/story_multi_a.md REQ-NOT-IN-A`,
|
|
103
|
+
errors: [
|
|
104
|
+
{
|
|
105
|
+
messageId: "reqMissing",
|
|
106
|
+
data: {
|
|
107
|
+
reqId: "REQ-NOT-IN-A",
|
|
108
|
+
storyPath: "tests/fixtures/story_multi_a.md",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "[REQ-DEEP-IMPLEMENTS] disallow path traversal in implements story path (see 010.2-DEV-MULTI-STORY-SUPPORT)",
|
|
115
|
+
code: `// @implements ../tests/fixtures/story_multi_a.md REQ-SHARED-ID`,
|
|
116
|
+
errors: [
|
|
117
|
+
{
|
|
118
|
+
messageId: "invalidPath",
|
|
119
|
+
data: {
|
|
120
|
+
storyPath: "../tests/fixtures/story_multi_a.md",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
91
125
|
],
|
|
92
126
|
});
|
|
93
127
|
});
|
|
@@ -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 {};
|