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.
Files changed (48) hide show
  1. package/README.md +39 -1
  2. package/lib/src/index.d.ts +30 -27
  3. package/lib/src/index.js +51 -31
  4. package/lib/src/maintenance/cli.d.ts +12 -0
  5. package/lib/src/maintenance/cli.js +279 -0
  6. package/lib/src/maintenance/detect.js +27 -12
  7. package/lib/src/maintenance/update.js +42 -34
  8. package/lib/src/maintenance/utils.js +30 -30
  9. package/lib/src/rules/helpers/require-story-io.js +51 -15
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
  12. package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
  13. package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
  14. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
  15. package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
  16. package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
  17. package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
  18. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
  19. package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
  20. package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
  21. package/lib/src/rules/prefer-implements-annotation.js +276 -0
  22. package/lib/src/rules/valid-annotation-format.js +255 -208
  23. package/lib/src/rules/valid-req-reference.js +210 -29
  24. package/lib/src/rules/valid-story-reference.d.ts +7 -0
  25. package/lib/src/rules/valid-story-reference.js +38 -80
  26. package/lib/src/utils/annotation-checker.js +2 -145
  27. package/lib/src/utils/branch-annotation-helpers.js +12 -3
  28. package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
  29. package/lib/src/utils/reqAnnotationDetection.js +152 -0
  30. package/lib/tests/maintenance/cli.test.d.ts +1 -0
  31. package/lib/tests/maintenance/cli.test.js +172 -0
  32. package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
  33. package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
  34. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
  35. package/lib/tests/rules/require-branch-annotation.test.js +3 -2
  36. package/lib/tests/rules/require-req-annotation.test.js +57 -68
  37. package/lib/tests/rules/require-story-annotation.test.js +13 -28
  38. package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
  39. package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
  40. package/lib/tests/rules/valid-annotation-format.test.js +395 -40
  41. package/lib/tests/rules/valid-req-reference.test.js +34 -0
  42. package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
  43. package/lib/tests/utils/annotation-checker.test.js +24 -17
  44. package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
  45. package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
  46. package/lib/tests/utils/ts-language-options.d.ts +22 -0
  47. package/lib/tests/utils/ts-language-options.js +27 -0
  48. 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: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
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-PATH-FORMAT] invalid story file extension",
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-PATH-FORMAT] missing extension in story path",
89
- code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION`,
90
- output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
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
- messageId: "invalidStoryFormat",
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
- 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".',
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-PATH-FORMAT] story path must not use path traversal",
102
- code: `// @story ../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
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 "../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
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-REQ-FORMAT] missing req id (single line)",
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: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
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-REQ-FORMAT] invalid req id format (single line)",
126
- code: `// @req invalid-format`,
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: "invalidReqFormat",
436
+ messageId: "invalidStoryFormat",
130
437
  data: {
131
- details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
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-REQ-FORMAT] missing req identifier with trailing space",
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: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
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-MULTILINE-SUPPORT] missing story path with multi-line block comment",
150
- code: `/**
151
- * @story
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: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
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-MULTILINE-SUPPORT] invalid multi-line story path after collapsing whitespace",
164
- code: `/**
165
- * @story docs/stories/005.0-
166
- * DEV-ANNOTATION-VALIDATION.story
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: "invalidStoryFormat",
502
+ messageId: "invalidReqFormat",
171
503
  data: {
172
- 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".',
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-MULTILINE-SUPPORT] missing req id with multi-line block comment",
534
+ name: "[REQ-FORMAT-VALIDATION] @implements with invalid requirement ID format",
179
535
  code: `/**
180
- * @req
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: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
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-MULTILINE-SUPPORT] invalid multi-line req id after collapsing whitespace",
548
+ name: "[REQ-FORMAT-VALIDATION] @implements with multiple requirement IDs where one is invalid",
193
549
  code: `/**
194
- * @req invalid-
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 "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
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 {};