eslint-plugin-traceability 1.9.0 → 1.10.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.
@@ -1,247 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const valid_annotation_options_1 = require("./helpers/valid-annotation-options");
4
- const valid_annotation_utils_1 = require("./helpers/valid-annotation-utils");
5
- const valid_implements_utils_1 = require("./helpers/valid-implements-utils");
6
4
  const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-format-internal");
7
- /**
8
- * Report an invalid @story annotation without applying a fix.
9
- *
10
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
11
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
12
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
13
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
14
- */
15
- function reportInvalidStoryFormat(context, comment, collapsed, options) {
16
- context.report({
17
- node: comment,
18
- messageId: "invalidStoryFormat",
19
- data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, options) },
20
- });
21
- }
22
- /**
23
- * Compute the text replacement for an invalid @story annotation within a comment.
24
- *
25
- * This helper:
26
- * - finds the @story tag in the raw comment text,
27
- * - computes the character range of its value,
28
- * - and returns an ESLint fix that replaces only that range.
29
- *
30
- * Returns null when the tag or value cannot be safely located.
31
- *
32
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
33
- * @req REQ-AUTOFIX-SAFE
34
- * @req REQ-AUTOFIX-PRESERVE
35
- */
36
- function createStoryFix(context, comment, fixed) {
37
- const sourceCode = context.getSourceCode();
38
- const commentText = sourceCode.getText(comment);
39
- const search = "@story";
40
- const tagIndex = commentText.indexOf(search);
41
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
42
- // @req REQ-AUTOFIX-SAFE - Skip auto-fix when @story tag cannot be reliably located
43
- if (tagIndex === valid_annotation_utils_1.TAG_NOT_FOUND_INDEX) {
44
- return null;
45
- }
46
- const afterTagIndex = tagIndex + search.length;
47
- const rest = commentText.slice(afterTagIndex);
48
- const valueMatch = rest.match(/[^\S\r\n]*([^\r\n*]+)/);
49
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
50
- // @req REQ-AUTOFIX-SAFE - Abort auto-fix when story value range cannot be safely determined
51
- if (!valueMatch || valueMatch.index === undefined) {
52
- return null;
53
- }
54
- const valueStartInComment = afterTagIndex +
55
- valueMatch.index +
56
- (valueMatch[0].length - valueMatch[1].length);
57
- const valueEndInComment = valueStartInComment + valueMatch[1].length;
58
- const start = comment.range[0];
59
- const fixRange = [
60
- start + valueStartInComment,
61
- start + valueEndInComment,
62
- ];
63
- return () => (fixer) => fixer.replaceTextRange(fixRange, fixed);
64
- }
65
- /**
66
- * Report an invalid @story annotation and attempt a minimal, safe auto-fix
67
- * for common path suffix issues by locating and replacing the path text
68
- * within the original comment.
69
- *
70
- * This helper:
71
- * - only adjusts the story path suffix when a safe, well-understood
72
- * transformation is available, satisfying REQ-AUTOFIX-SAFE.
73
- * - preserves all surrounding comment formatting, spacing, and text
74
- * outside the path substring, satisfying REQ-AUTOFIX-PRESERVE.
75
- *
76
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
77
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
78
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
79
- * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
80
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
81
- * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
82
- * @req REQ-AUTOFIX-PRESERVE - Auto-fix must preserve surrounding formatting and comments
83
- */
84
- function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
85
- const fixFactory = createStoryFix(context, comment, fixed);
86
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
87
- // @req REQ-AUTOFIX-SAFE - Fall back to reporting without fix when safe fix cannot be created
88
- if (!fixFactory) {
89
- reportInvalidStoryFormat(context, comment, collapsed, (0, valid_annotation_options_1.getResolvedDefaults)());
90
- return;
91
- }
92
- context.report({
93
- node: comment,
94
- messageId: "invalidStoryFormat",
95
- data: {
96
- details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, (0, valid_annotation_options_1.getResolvedDefaults)()),
97
- },
98
- fix: fixFactory(),
99
- });
100
- }
101
- /**
102
- * Validate a @story annotation value and report detailed errors when needed.
103
- * Where safe and unambiguous, apply an automatic fix for missing suffixes.
104
- *
105
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
106
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
107
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
108
- * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
109
- * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
110
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
111
- * @req REQ-REGEX-VALIDATION - Validate configurable story regex patterns and fall back safely
112
- * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
113
- * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
114
- */
115
- function validateStoryAnnotation(context, comment, rawValue, options) {
116
- const trimmed = rawValue.trim();
117
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
118
- // @req REQ-PATH-FORMAT - Treat missing @story value as a specific validation error
119
- if (!trimmed) {
120
- context.report({
121
- node: comment,
122
- messageId: "invalidStoryFormat",
123
- data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("missing", null, options) },
124
- });
125
- return;
126
- }
127
- const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
128
- const pathPattern = options.storyPattern;
129
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
130
- // @req REQ-PATH-FORMAT - Accept @story value when it matches configured storyPattern
131
- if (pathPattern.test(collapsed)) {
132
- return;
133
- }
134
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
135
- // @req REQ-PATH-FORMAT - Reject @story values containing internal whitespace as invalid
136
- if (/\s/.test(trimmed)) {
137
- reportInvalidStoryFormat(context, comment, collapsed, options);
138
- return;
139
- }
140
- const fixed = (0, valid_annotation_utils_1.getFixedStoryPath)(collapsed);
141
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
142
- // @req REQ-AUTOFIX-FORMAT - Apply suffix-only auto-fix when it yields a pattern-compliant path
143
- if (fixed && pathPattern.test(fixed)) {
144
- reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed);
145
- return;
146
- }
147
- reportInvalidStoryFormat(context, comment, collapsed, options);
148
- }
149
- /**
150
- * Validate a @req annotation value and report detailed errors when needed.
151
- *
152
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
153
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
154
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
155
- * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
156
- * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
157
- * @req REQ-REGEX-VALIDATION - Validate configurable requirement regex patterns and fall back safely
158
- * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
159
- * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
160
- */
161
- function validateReqAnnotation(context, comment, rawValue, options) {
162
- const trimmed = rawValue.trim();
163
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
164
- // @req REQ-REQ-FORMAT - Treat missing @req value as a specific validation error
165
- if (!trimmed) {
166
- context.report({
167
- node: comment,
168
- messageId: "invalidReqFormat",
169
- data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("missing", null, options) },
170
- });
171
- return;
172
- }
173
- const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
174
- const reqPattern = options.reqPattern;
175
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
176
- // @req REQ-REQ-FORMAT - Flag @req identifiers that do not match the configured pattern
177
- if (!reqPattern.test(collapsed)) {
178
- context.report({
179
- node: comment,
180
- messageId: "invalidReqFormat",
181
- data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", collapsed, options) },
182
- });
183
- }
184
- }
185
- /**
186
- * Validate an @supports annotation value and report detailed errors when needed.
187
- *
188
- * Expected format:
189
- * @supports <storyPath> <REQ-ID> [<REQ-ID> ...]
190
- *
191
- * Validation rules:
192
- * - Value must include at least a story path and one requirement ID.
193
- * - Story path must match the same storyPattern used for @story (no auto-fix).
194
- * - Each subsequent token must match reqPattern and is validated individually.
195
- *
196
- * Story path issues are reported with "invalidImplementsFormat" and
197
- * requirement ID issues reuse the existing "invalidReqFormat" message.
198
- *
199
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
200
- * @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
201
- * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
202
- * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
203
- */
204
- function validateImplementsAnnotation(context, comment, rawValue, options) {
205
- const deps = {
206
- MIN_IMPLEMENTS_TOKENS: valid_implements_utils_1.MIN_IMPLEMENTS_TOKENS,
207
- reportMissingImplementsReqIds: valid_implements_utils_1.reportMissingImplementsReqIds,
208
- reportMissingImplementsValue: valid_implements_utils_1.reportMissingImplementsValue,
209
- reportInvalidImplementsReqId: valid_implements_utils_1.reportInvalidImplementsReqId,
210
- reportInvalidImplementsStoryPath: valid_implements_utils_1.reportInvalidImplementsStoryPath,
211
- };
212
- (0, valid_implements_utils_1.validateImplementsAnnotationHelper)(deps, context, comment, {
213
- rawValue,
214
- options,
215
- });
216
- }
217
- /**
218
- * Finalize and validate the currently pending annotation, if any.
219
- *
220
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
221
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
222
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
223
- * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
224
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
225
- * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
226
- */
227
- function finalizePendingAnnotation(context, comment, options, pending) {
228
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
229
- // @req REQ-MULTILINE-SUPPORT - Do nothing when there is no pending multi-line annotation to finalize
230
- if (!pending) {
231
- return null;
232
- }
233
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
234
- // @req REQ-SYNTAX-VALIDATION - Dispatch to @story or @req validator based on pending annotation type
235
- // @req REQ-AUTOFIX-FORMAT - Route to story validator which may apply safe auto-fixes
236
- // @req REQ-MIXED-SUPPORT - Ensure @story and @req annotations are handled independently
237
- if (pending.type === "story") {
238
- validateStoryAnnotation(context, comment, pending.value, options);
239
- }
240
- else {
241
- validateReqAnnotation(context, comment, pending.value, options);
242
- }
243
- return null;
244
- }
5
+ const valid_annotation_format_validators_1 = require("./helpers/valid-annotation-format-validators");
245
6
  /**
246
7
  * Process a single normalized comment line and update the pending annotation state.
247
8
  *
@@ -269,7 +30,7 @@ function processCommentLine({ normalized, pending, context, comment, options, })
269
30
  // @req REQ-IMPLEMENTS-PARSE - Immediately validate @supports without starting multi-line state
270
31
  if (isImplements) {
271
32
  const implementsValue = normalized.replace(/^@supports\b/, "").trim();
272
- validateImplementsAnnotation(context, comment, implementsValue, options);
33
+ (0, valid_annotation_format_validators_1.validateImplementsAnnotation)(context, comment, implementsValue, options);
273
34
  return pending;
274
35
  }
275
36
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
@@ -279,7 +40,7 @@ function processCommentLine({ normalized, pending, context, comment, options, })
279
40
  // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
280
41
  // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
281
42
  if (isStory || isReq) {
282
- finalizePendingAnnotation(context, comment, options, pending);
43
+ (0, valid_annotation_format_validators_1.finalizePendingAnnotation)(context, comment, options, pending);
283
44
  const value = normalized.replace(/^@story\b|^@req\b/, "").trim();
284
45
  return {
285
46
  type: isStory ? "story" : "req",
@@ -287,6 +48,12 @@ function processCommentLine({ normalized, pending, context, comment, options, })
287
48
  hasValue: value.trim().length > 0,
288
49
  };
289
50
  }
51
+ // Implement JSDoc tag coexistence behavior: terminate @story/@req values when a new non-traceability JSDoc tag line (e.g., @param, @returns) is encountered.
52
+ // @supports docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md REQ-ANNOTATION-TERMINATION REQ-CONTINUATION-LOGIC
53
+ if ((0, valid_annotation_format_internal_1.isNonTraceabilityJSDocTagLine)(normalized)) {
54
+ (0, valid_annotation_format_validators_1.finalizePendingAnnotation)(context, comment, options, pending);
55
+ return null;
56
+ }
290
57
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
291
58
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
292
59
  // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
@@ -344,7 +111,7 @@ function processComment(context, comment, options) {
344
111
  options,
345
112
  });
346
113
  });
347
- finalizePendingAnnotation(context, comment, options, pending);
114
+ (0, valid_annotation_format_validators_1.finalizePendingAnnotation)(context, comment, options, pending);
348
115
  }
349
116
  exports.default = {
350
117
  meta: {
@@ -4,8 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /**
7
- * Tests for: docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
7
+ * Tests for:
8
+ * - docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
9
+ * - docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md
8
10
  * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-FRAMEWORK-COMPAT REQ-TEST-PATTERN-DETECT
11
+ * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-NO-INFERENCE
9
12
  */
10
13
  const eslint_1 = require("eslint");
11
14
  const require_test_traceability_1 = __importDefault(require("../../src/rules/require-test-traceability"));
@@ -14,7 +17,7 @@ const ruleTester = new eslint_1.RuleTester({
14
17
  parserOptions: { ecmaVersion: 2020, sourceType: "module" },
15
18
  },
16
19
  });
17
- describe("require-test-traceability rule (Story 020.0-DEV-TEST-ANNOTATION-VALIDATION)", () => {
20
+ describe("require-test-traceability rule (Stories 020.0 and 021.0)", () => {
18
21
  ruleTester.run("require-test-traceability", require_test_traceability_1.default, {
19
22
  valid: [
20
23
  {
@@ -32,24 +35,58 @@ describe("require-test-traceability rule (Story 020.0-DEV-TEST-ANNOTATION-VALIDA
32
35
  code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT\n */\ndescribe('Story 020.0-DEV-TEST-ANNOTATION-VALIDATION', () => {});`,
33
36
  filename: "src/not-a-test-file.ts",
34
37
  },
38
+ {
39
+ // [REQ-TEST-FIX-PREFIX-FORMAT] already-correct [REQ-XXX] prefix is left unchanged by auto-fix
40
+ code: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\ndescribe('Story 021.0-DEV-TEST-ANNOTATION-AUTO-FIX', () => { it('[REQ-TRACE-123] behaves correctly', () => {}); });`,
41
+ filename: "tests/rules/correct-prefix-autofix.test.ts",
42
+ },
35
43
  ],
36
44
  invalid: [
37
45
  {
38
- // [REQ-TEST-FILE-SUPPORTS] missing @supports in test file
46
+ // [REQ-TEST-FIX-TEMPLATE] missing @supports in test file -> insert default placeholder template
39
47
  code: `describe('Story 020.0-DEV-TEST-ANNOTATION-VALIDATION', () => { it('[REQ-ONE] works', () => {}); });`,
48
+ output: `/**\n * @supports docs/stories/XXX.X-STORY-NAME.story.md REQ-XXX-YYY REQ-XXX-ZZZ\n * TODO: Replace the placeholder story path and REQ-IDs with real values for this test file.\n */\ndescribe('Story 020.0-DEV-TEST-ANNOTATION-VALIDATION', () => { it('[REQ-ONE] works', () => {}); });`,
40
49
  filename: "tests/rules/missing-supports.test.ts",
41
50
  errors: [{ messageId: "missingFileSupports" }],
42
51
  },
43
52
  {
44
- // [REQ-TEST-DESCRIBE-STORY] describe without story phrase
53
+ // [REQ-TEST-DESCRIBE-STORY] describe without story phrase still reported (no auto-fix)
45
54
  code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY\n */\ndescribe('no story reference here', () => {});`,
46
55
  filename: "tests/rules/bad-describe.test.ts",
47
56
  errors: [{ messageId: "missingDescribeStory" }],
48
57
  },
49
58
  {
50
- // [REQ-TEST-IT-REQ-PREFIX] test name without [REQ-XXX] prefix
59
+ // [REQ-TEST-IT-REQ-PREFIX][REQ-TEST-FIX-NO-INFERENCE] test name without any REQ prefix -> error but no auto-fix
51
60
  code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-IT-REQ-PREFIX\n */\nit('missing prefix', () => {});`,
52
- filename: "tests/rules/bad-test-name.test.ts",
61
+ filename: "tests/rules/bad-test-name-no-prefix.test.ts",
62
+ errors: [{ messageId: "missingReqPrefix" }],
63
+ },
64
+ {
65
+ // [REQ-TEST-FIX-PREFIX-FORMAT] malformed prefix with extra spaces in brackets
66
+ code: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[ REQ-TEST-FIX ] does something', () => {});`,
67
+ output: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[REQ-TEST-FIX] does something', () => {});`,
68
+ filename: "tests/rules/malformed-prefix-spacing.test.ts",
69
+ errors: [{ messageId: "missingReqPrefix" }],
70
+ },
71
+ {
72
+ // [REQ-TEST-FIX-PREFIX-FORMAT] malformed prefix with underscore delimiter
73
+ code: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[REQ_TEST_FIX] does something', () => {});`,
74
+ output: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[REQ-TEST-FIX] does something', () => {});`,
75
+ filename: "tests/rules/malformed-prefix-underscore.test.ts",
76
+ errors: [{ messageId: "missingReqPrefix" }],
77
+ },
78
+ {
79
+ // [REQ-TEST-FIX-PREFIX-FORMAT] malformed prefix with lowercase req
80
+ code: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[req-lowercase] bad casing', () => {});`,
81
+ output: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[REQ-LOWERCASE] bad casing', () => {});`,
82
+ filename: "tests/rules/malformed-prefix-lowercase.test.ts",
83
+ errors: [{ messageId: "missingReqPrefix" }],
84
+ },
85
+ {
86
+ // [REQ-TEST-FIX-PREFIX-FORMAT] malformed prefix using parentheses
87
+ code: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('(REQ-PAREN) with parens', () => {});`,
88
+ output: `/**\n * @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT\n */\nit('[REQ-PAREN] with parens', () => {});`,
89
+ filename: "tests/rules/malformed-prefix-parens.test.ts",
53
90
  errors: [{ messageId: "missingReqPrefix" }],
54
91
  },
55
92
  ],
@@ -23,6 +23,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
23
23
  * @req REQ-SUPPORTS-PARSE - Rule parses @supports annotations with story and requirement references
24
24
  * @req REQ-FORMAT-VALIDATION - Rule validates story and requirement formats inside @supports annotations
25
25
  * @req REQ-MIXED-SUPPORT - Rule supports mixed @story/@req/@supports usage in the same comment
26
+ * Tests for: docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
27
+ * @story docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
28
+ * @req REQ-JSDOC-TAG-COEXISTENCE - Rule allows traceability annotations to coexist with other JSDoc tags
29
+ * @req REQ-ANNOTATION-TERMINATION - Rule correctly terminates traceability annotation values at JSDoc tag boundaries
30
+ * @req REQ-JSDOC-BOUNDARY-DETECTION - Rule detects @param/@returns and similar tags as boundaries
31
+ * @req REQ-CONTINUATION-LOGIC - Rule correctly decides when to continue or stop multi-line traceability values
32
+ * @req REQ-NO-FALSE-POSITIVES - Rule does not report false positives when JSDoc tags follow traceability tags
33
+ * @req REQ-PRESERVE-MULTILINE - Rule preserves multi-line story/req values without including following JSDoc tags
26
34
  */
27
35
  const eslint_1 = require("eslint");
28
36
  const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
@@ -194,6 +202,69 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
194
202
  * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION REQ-MIXED-SUPPORT
195
203
  */`,
196
204
  },
205
+ {
206
+ name: "[REQ-JSDOC-TAG-COEXISTENCE] traceability before other JSDoc tags",
207
+ code: `/**
208
+ * @story docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
209
+ * @req REQ-JSDOC-TAG-COEXISTENCE
210
+ * @param {string} id - Identifier for the lookup.
211
+ * @returns {Promise<void>} - Completes when finished.
212
+ */
213
+ function fetchById(id) {
214
+ return Promise.resolve();
215
+ }`,
216
+ },
217
+ {
218
+ name: "[REQ-JSDOC-TAG-COEXISTENCE] traceability after other JSDoc tags",
219
+ code: `/**
220
+ * Fetch a user by id.
221
+ *
222
+ * @param {string} id - Identifier for the lookup.
223
+ * @returns {Promise<void>} - Completes when finished.
224
+ * @story docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
225
+ * @req REQ-ANNOTATION-TERMINATION
226
+ */
227
+ function fetchUser(id) {
228
+ return Promise.resolve();
229
+ }`,
230
+ },
231
+ {
232
+ name: "[REQ-JSDOC-TAG-COEXISTENCE] mixed positions of traceability and other JSDoc tags",
233
+ code: `/**
234
+ * Update a record with new data.
235
+ *
236
+ * @story docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
237
+ * @param {string} id - Identifier.
238
+ * @req REQ-JSDOC-BOUNDARY-DETECTION
239
+ * @param {object} payload - Updated fields.
240
+ * @returns {boolean} - True if updated.
241
+ * @req REQ-CONTINUATION-LOGIC
242
+ */
243
+ function updateRecord(id, payload) {
244
+ return true;
245
+ }`,
246
+ },
247
+ {
248
+ name: "[REQ-PRESERVE-MULTILINE] multi-line @story annotation before other JSDoc tags",
249
+ code: `/**
250
+ * @story docs/stories/022.0-DEV-
251
+ * JSDOC-COEXISTENCE.story.md
252
+ * @param {string} id - Identifier for the lookup.
253
+ * @returns {Promise<void>} - Completes when finished.
254
+ */
255
+ function loadForStory(id) {
256
+ return Promise.resolve();
257
+ }`,
258
+ },
259
+ {
260
+ name: "[REQ-NO-FALSE-POSITIVES] JSDoc tags do not pollute requirement ID when following @req",
261
+ code: `/**
262
+ * @req REQ-OPTIMIZATION
263
+ * @param {object} data - Input payload.
264
+ * @returns {void}
265
+ */
266
+ function optimize(data) {}`,
267
+ },
197
268
  ],
198
269
  invalid: [
199
270
  makeInvalidStory({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-traceability",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "A customizable ESLint plugin that enforces traceability annotations in your code, ensuring each implementation is linked to its requirement or test case.",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",
@@ -197,7 +197,7 @@ Description: Enforces traceability conventions in test files by requiring:
197
197
  - Story references in top-level `describe` blocks.
198
198
  - Requirement identifiers in `it`/`test` names using a `[REQ-XXX]` prefix.
199
199
 
200
- The rule is designed to complement the function-level rules (such as `require-story-annotation` and `require-req-annotation`) by ensuring that tests explicitly declare which requirements and stories they validate. It is enabled in both the `recommended` and `strict` presets alongside the other core rules.
200
+ The rule is designed to complement the function-level rules (such as `require-story-annotation` and `require-req-annotation`) by ensuring that tests explicitly declare which requirements and stories they validate. It is enabled in both the `recommended` and `strict` presets alongside the other core rules. For Story 021.0, this rule also provides targeted auto-fix capabilities: when run with `--fix`, it can (1) insert a safe, non-semantic file-level `@supports` placeholder template at the top of matching test files when the annotation is missing, including clear TODO guidance for humans to replace the template with a real story and requirement reference, and (2) normalize malformed `[REQ-XXX]` prefixes that already contain an identifiable requirement ID, correcting spacing, bracket/parenthesis usage, underscores, and casing while preserving the original ID text and never inventing new requirement identifiers.
201
201
 
202
202
  Options:
203
203
 
@@ -207,6 +207,9 @@ The rule accepts an optional configuration object:
207
207
  - `requireDescribeStory` (boolean, optional) – When `true` (default), requires that each top-level `describe` block include a story reference somewhere in its description text (for example, a path such as `docs/stories/010.0-PAYMENTS.story.md` or a shorter project-specific alias that your team uses consistently).
208
208
  - `requireTestReqPrefix` (boolean, optional) – When `true` (default), requires each `it`/`test` block name to begin with a requirement identifier in square brackets, such as `[REQ-PAYMENTS-REFUND]`. The exact `REQ-` pattern is shared with the `valid-annotation-format` rule’s requirement ID checks.
209
209
  - `describePattern` (string, optional) – A JavaScript regular expression **source** (without leading and trailing `/`) that the `describe` description text must match when `requireDescribeStory` is enabled. This lets you enforce a project-specific format such as requiring a canonical story path or a `STORY-` style identifier in the `describe` string. If omitted, a built-in default that loosely matches a typical story path (similar to `docs/stories/<name>.story.md`) is used.
210
+ - `autoFixTestTemplate` (boolean, optional) – When `true` (default), allows the rule’s `--fix` mode to insert a file-level `@supports` placeholder template at the top of test files that are missing it. The template is intentionally non-semantic and includes TODO-style guidance so humans can later replace it with a real story path and requirement IDs; disabling this option prevents the rule from inserting the template automatically.
211
+ - `autoFixTestPrefixFormat` (boolean, optional) – When `true` (default), enables safe normalization of malformed `[REQ-XXX]` prefixes in `it`/`test` names during `--fix`. The rule only rewrites prefixes that already contain a recognizable requirement identifier and limits changes to formatting concerns (spacing, square brackets vs. parentheses, underscore and dash usage, and letter casing) without fabricating new IDs or guessing requirement names.
212
+ - `testSupportsTemplate` (string, optional) – Overrides the default file-level `@supports` placeholder template used when `autoFixTestTemplate` is enabled. This string should be a complete JSDoc-style block (for example, including `/**`, `*`, and `*/`) that encodes your project’s preferred TODO guidance or placeholder story path; it is inserted verbatim at the top of matching test files that lack a `@supports` annotation, and is never interpreted or expanded by the rule.
210
213
 
211
214
  Behavior notes:
212
215
 
@@ -567,4 +570,5 @@ If `--from` or `--to` is missing, the CLI prints an error, shows the help text,
567
570
  In CI:
568
571
 
569
572
  ```bash
570
- npm run traceability:verify
573
+ npm run traceability:verify
574
+ ```