eslint-plugin-traceability 1.8.3 → 1.10.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/CHANGELOG.md +3 -3
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.js +2 -0
- package/lib/src/rules/helpers/require-test-traceability-helpers.d.ts +34 -0
- package/lib/src/rules/helpers/require-test-traceability-helpers.js +258 -0
- package/lib/src/rules/require-test-traceability.d.ts +24 -0
- package/lib/src/rules/require-test-traceability.js +112 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +2 -0
- package/lib/tests/rules/require-test-traceability.test.d.ts +1 -0
- package/lib/tests/rules/require-test-traceability.test.js +94 -0
- package/package.json +1 -1
- package/user-docs/api-reference.md +50 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# [1.10.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.9.0...v1.10.0) (2025-12-05)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* add safe auto-fix support for test traceability rule ([b511e40](https://github.com/voder-ai/eslint-plugin-traceability/commit/b511e40593791ac4b25af9c71d3f377b3245ea74))
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
package/lib/src/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { detectStaleAnnotations, updateAnnotationReferences, batchUpdateAnnotati
|
|
|
14
14
|
* @story docs/stories/002.0-DYNAMIC-RULE-LOADING.story.md
|
|
15
15
|
* @req REQ-RULE-LIST - Enumerate supported rule file names for plugin discovery
|
|
16
16
|
*/
|
|
17
|
-
declare const RULE_NAMES: readonly ["require-story-annotation", "require-req-annotation", "require-branch-annotation", "valid-annotation-format", "valid-story-reference", "valid-req-reference", "prefer-implements-annotation"];
|
|
17
|
+
declare const RULE_NAMES: readonly ["require-story-annotation", "require-req-annotation", "require-branch-annotation", "valid-annotation-format", "valid-story-reference", "valid-req-reference", "prefer-implements-annotation", "require-test-traceability"];
|
|
18
18
|
type RuleName = (typeof RULE_NAMES)[number];
|
|
19
19
|
declare const rules: Record<RuleName, Rule.RuleModule>;
|
|
20
20
|
declare const plugin: {
|
package/lib/src/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const RULE_NAMES = [
|
|
|
18
18
|
"valid-story-reference",
|
|
19
19
|
"valid-req-reference",
|
|
20
20
|
"prefer-implements-annotation",
|
|
21
|
+
"require-test-traceability",
|
|
21
22
|
];
|
|
22
23
|
const rules = {};
|
|
23
24
|
exports.rules = rules;
|
|
@@ -89,6 +90,7 @@ const TRACEABILITY_RULE_SEVERITIES = {
|
|
|
89
90
|
"traceability/valid-annotation-format": "warn",
|
|
90
91
|
"traceability/valid-story-reference": "error",
|
|
91
92
|
"traceability/valid-req-reference": "error",
|
|
93
|
+
"traceability/require-test-traceability": "error",
|
|
92
94
|
};
|
|
93
95
|
/**
|
|
94
96
|
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type TestTraceabilityAutoFixOptions = {
|
|
2
|
+
autoFixTestTemplate: boolean;
|
|
3
|
+
testSupportsTemplate?: string;
|
|
4
|
+
};
|
|
5
|
+
export type CallExpressionOptions = {
|
|
6
|
+
sourceCode: any;
|
|
7
|
+
describeRegex: RegExp;
|
|
8
|
+
requireDescribeStory: boolean;
|
|
9
|
+
requireTestReqPrefix: boolean;
|
|
10
|
+
autoFixTestPrefixFormat: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Determine if a file should be treated as a test file based on patterns.
|
|
14
|
+
*
|
|
15
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT
|
|
16
|
+
*/
|
|
17
|
+
export declare function determineIsTestFile(filename: string, rawPatterns?: string[]): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Ensure the file has a @supports annotation listing tested requirements.
|
|
20
|
+
*
|
|
21
|
+
* When auto-fix is enabled, a placeholder @supports JSDoc is inserted at the
|
|
22
|
+
* top of the file (after any shebang) using a safe, non-semantic template.
|
|
23
|
+
*
|
|
24
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS REQ-TEST-SUPPORTS-VALID
|
|
25
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
26
|
+
*/
|
|
27
|
+
export declare function ensureFileSupportsAnnotation(context: any, sourceCode: any, autoFixOptions: TestTraceabilityAutoFixOptions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Build a CallExpression visitor for the main rule create() function.
|
|
30
|
+
*
|
|
31
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-NESTED-DESCRIBE REQ-TEST-ERROR-CONTEXT
|
|
32
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleCallExpression(context: any, options: CallExpressionOptions): (node: any) => void;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.determineIsTestFile = determineIsTestFile;
|
|
4
|
+
exports.ensureFileSupportsAnnotation = ensureFileSupportsAnnotation;
|
|
5
|
+
exports.handleCallExpression = handleCallExpression;
|
|
6
|
+
/**
|
|
7
|
+
* Helper utilities for the require-test-traceability rule.
|
|
8
|
+
*
|
|
9
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT REQ-TEST-FRAMEWORK-COMPAT
|
|
10
|
+
* @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
|
|
11
|
+
*/
|
|
12
|
+
const NOT_FOUND = -1;
|
|
13
|
+
const REQ_PREFIX_LENGTH = 3;
|
|
14
|
+
const QUOTES = ["'", '"', "`"];
|
|
15
|
+
/**
|
|
16
|
+
* Determine if a file should be treated as a test file based on patterns.
|
|
17
|
+
*
|
|
18
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT
|
|
19
|
+
*/
|
|
20
|
+
function determineIsTestFile(filename, rawPatterns = [
|
|
21
|
+
"/tests/",
|
|
22
|
+
"/test/",
|
|
23
|
+
"/__tests__",
|
|
24
|
+
".test.",
|
|
25
|
+
".spec.",
|
|
26
|
+
]) {
|
|
27
|
+
return rawPatterns.some((pattern) => filename.includes(pattern.replace("**", "")));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the placeholder @supports template comment for a test file.
|
|
31
|
+
*
|
|
32
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER
|
|
33
|
+
*/
|
|
34
|
+
function buildSupportsTemplateComment(customTemplate) {
|
|
35
|
+
const baseTemplate = (customTemplate && customTemplate.trim()) ||
|
|
36
|
+
"@supports docs/stories/XXX.X-STORY-NAME.story.md REQ-XXX-YYY REQ-XXX-ZZZ";
|
|
37
|
+
const lines = [
|
|
38
|
+
"/**",
|
|
39
|
+
` * ${baseTemplate}`,
|
|
40
|
+
" * TODO: Replace the placeholder story path and REQ-IDs with real values for this test file.",
|
|
41
|
+
" */",
|
|
42
|
+
"",
|
|
43
|
+
];
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Insert the file-level @supports template comment at a safe location.
|
|
48
|
+
*
|
|
49
|
+
* The template is inserted after a shebang line if present, otherwise at the
|
|
50
|
+
* very start of the file. This preserves executable semantics while adding
|
|
51
|
+
* only comment text.
|
|
52
|
+
*
|
|
53
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
54
|
+
*/
|
|
55
|
+
function insertSupportsTemplate(fixer, sourceCode, customTemplate) {
|
|
56
|
+
const text = sourceCode.text || "";
|
|
57
|
+
let insertIndex = 0;
|
|
58
|
+
// Preserve shebang: it must remain the very first characters in the file.
|
|
59
|
+
if (text.startsWith("#!")) {
|
|
60
|
+
const firstNewline = text.indexOf("\n");
|
|
61
|
+
insertIndex = firstNewline === NOT_FOUND ? text.length : firstNewline + 1;
|
|
62
|
+
}
|
|
63
|
+
const templateComment = buildSupportsTemplateComment(customTemplate);
|
|
64
|
+
return fixer.insertTextBeforeRange([insertIndex, insertIndex], templateComment);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Ensure the file has a @supports annotation listing tested requirements.
|
|
68
|
+
*
|
|
69
|
+
* When auto-fix is enabled, a placeholder @supports JSDoc is inserted at the
|
|
70
|
+
* top of the file (after any shebang) using a safe, non-semantic template.
|
|
71
|
+
*
|
|
72
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS REQ-TEST-SUPPORTS-VALID
|
|
73
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
74
|
+
*/
|
|
75
|
+
function ensureFileSupportsAnnotation(context, sourceCode, autoFixOptions) {
|
|
76
|
+
const fileComments = sourceCode.getAllComments() || [];
|
|
77
|
+
const fileHasSupports = fileComments.some((comment) => /@supports\b/.test(comment.value || ""));
|
|
78
|
+
if (!fileHasSupports) {
|
|
79
|
+
const node = fileComments[0] || (sourceCode.ast && sourceCode.ast);
|
|
80
|
+
context.report({
|
|
81
|
+
node: node,
|
|
82
|
+
messageId: "missingFileSupports",
|
|
83
|
+
fix: autoFixOptions.autoFixTestTemplate === false
|
|
84
|
+
? undefined
|
|
85
|
+
: (fixer) => insertSupportsTemplate(fixer, sourceCode, autoFixOptions.testSupportsTemplate),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a callee name corresponds to a test framework function.
|
|
91
|
+
*
|
|
92
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FRAMEWORK-COMPAT
|
|
93
|
+
*/
|
|
94
|
+
function isTestCallName(name) {
|
|
95
|
+
return ["describe", "it", "test", "context"].includes(name);
|
|
96
|
+
}
|
|
97
|
+
function getCalleeName(node) {
|
|
98
|
+
if (node.callee.type === "Identifier") {
|
|
99
|
+
return node.callee.name;
|
|
100
|
+
}
|
|
101
|
+
if (node.callee.type === "MemberExpression" &&
|
|
102
|
+
node.callee.object.type === "Identifier") {
|
|
103
|
+
return node.callee.object.name;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function getFirstArgumentLiteral(node) {
|
|
108
|
+
const arg = node.arguments && node.arguments[0];
|
|
109
|
+
if (!arg)
|
|
110
|
+
return null;
|
|
111
|
+
if (arg.type === "Literal" && typeof arg.value === "string") {
|
|
112
|
+
return arg.value;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Normalize a raw REQ identifier string to canonical REQ-XXX format.
|
|
118
|
+
*
|
|
119
|
+
* This helper performs only local, format-level normalization without
|
|
120
|
+
* inferring new requirement IDs.
|
|
121
|
+
*
|
|
122
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-NO-INFERENCE
|
|
123
|
+
*/
|
|
124
|
+
function normalizeReqId(raw) {
|
|
125
|
+
let id = raw.trim().toUpperCase();
|
|
126
|
+
if (!id.startsWith("REQ")) {
|
|
127
|
+
return id;
|
|
128
|
+
}
|
|
129
|
+
let rest = id.slice(REQ_PREFIX_LENGTH);
|
|
130
|
+
// Drop leading separators after "REQ"
|
|
131
|
+
rest = rest.replace(/^[-_\s:]+/, "");
|
|
132
|
+
// Convert internal whitespace/underscores to hyphens
|
|
133
|
+
rest = rest.replace(/[\s_]+/g, "-");
|
|
134
|
+
// Collapse multiple hyphens
|
|
135
|
+
rest = rest.replace(/-+/g, "-");
|
|
136
|
+
return rest ? `REQ-${rest}` : "REQ-";
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Normalize malformed [REQ-XXX] prefixes in test names.
|
|
140
|
+
*
|
|
141
|
+
* Handles cases such as:
|
|
142
|
+
* - "[ REQ-XXX ] ..." -> "[REQ-XXX] ..."
|
|
143
|
+
* - "[REQ_XXX] ..." -> "[REQ-XXX] ..."
|
|
144
|
+
* - "(REQ-XXX) ..." -> "[REQ-XXX] ..."
|
|
145
|
+
* - "[req-xxx] ..." -> "[REQ-XXX] ..."
|
|
146
|
+
*
|
|
147
|
+
* Only operates when a REQ identifier is already present at the start of the
|
|
148
|
+
* string; it never invents new IDs.
|
|
149
|
+
*
|
|
150
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
151
|
+
*/
|
|
152
|
+
function normalizeReqPrefixInDescription(description) {
|
|
153
|
+
const canonicalPattern = /^\[REQ-[^\]]+]/;
|
|
154
|
+
if (canonicalPattern.test(description)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// Leading square brackets with optional spacing.
|
|
158
|
+
const squareMatch = description.match(/^\[\s*(REQ[^\]]*?)\s*](.*)$/i);
|
|
159
|
+
if (squareMatch) {
|
|
160
|
+
const normalizedId = normalizeReqId(squareMatch[1]);
|
|
161
|
+
return `[${normalizedId}]${squareMatch[2] ?? ""}`;
|
|
162
|
+
}
|
|
163
|
+
// Leading parentheses with optional spacing.
|
|
164
|
+
const parenMatch = description.match(/^\(\s*(REQ[^)]*?)\s*\)(.*)$/i);
|
|
165
|
+
if (parenMatch) {
|
|
166
|
+
const normalizedId = normalizeReqId(parenMatch[1]);
|
|
167
|
+
return `[${normalizedId}]${parenMatch[2] ?? ""}`;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create a string literal with the same quote style as the original node.
|
|
173
|
+
*
|
|
174
|
+
* This helper rewrites only the literal value while preserving the original
|
|
175
|
+
* quoting character (`'`, `"`, or `` ` ``) and escaping rules.
|
|
176
|
+
*
|
|
177
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PRESERVE
|
|
178
|
+
*/
|
|
179
|
+
function createUpdatedStringLiteralRaw(originalNode, newValue, sourceCode) {
|
|
180
|
+
const raw = sourceCode.getText(originalNode);
|
|
181
|
+
const firstChar = raw[0];
|
|
182
|
+
if (QUOTES.includes(firstChar)) {
|
|
183
|
+
const quote = firstChar;
|
|
184
|
+
const escaped = newValue
|
|
185
|
+
.replace(/\\/g, "\\\\")
|
|
186
|
+
.replace(new RegExp(`\\${quote}`, "g"), `\\${quote}`);
|
|
187
|
+
return `${quote}${escaped}${quote}`;
|
|
188
|
+
}
|
|
189
|
+
// Fallback: let JSON.stringify choose a safe representation.
|
|
190
|
+
return JSON.stringify(newValue);
|
|
191
|
+
}
|
|
192
|
+
function handleDescribeCall(context, node, description, options) {
|
|
193
|
+
const { describeRegex, requireDescribeStory } = options;
|
|
194
|
+
if (!requireDescribeStory)
|
|
195
|
+
return;
|
|
196
|
+
if (!describeRegex.test(description)) {
|
|
197
|
+
context.report({
|
|
198
|
+
node: node,
|
|
199
|
+
messageId: "missingDescribeStory",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function handleItOrTestCall(context, node, description, options) {
|
|
204
|
+
const { sourceCode, requireTestReqPrefix, autoFixTestPrefixFormat } = options;
|
|
205
|
+
if (!requireTestReqPrefix)
|
|
206
|
+
return;
|
|
207
|
+
if (!/^\[REQ-[^\]]+]/.test(description)) {
|
|
208
|
+
const normalizedDescription = autoFixTestPrefixFormat !== false
|
|
209
|
+
? normalizeReqPrefixInDescription(description)
|
|
210
|
+
: null;
|
|
211
|
+
context.report({
|
|
212
|
+
node: node,
|
|
213
|
+
messageId: "missingReqPrefix",
|
|
214
|
+
...(autoFixTestPrefixFormat !== false &&
|
|
215
|
+
normalizedDescription !== null &&
|
|
216
|
+
node.arguments &&
|
|
217
|
+
node.arguments[0] &&
|
|
218
|
+
node.arguments[0].type === "Literal" &&
|
|
219
|
+
typeof node.arguments[0].value === "string"
|
|
220
|
+
? {
|
|
221
|
+
fix(fixer) {
|
|
222
|
+
const literalNode = node.arguments[0];
|
|
223
|
+
const newRaw = createUpdatedStringLiteralRaw(literalNode, normalizedDescription, sourceCode);
|
|
224
|
+
return fixer.replaceText(literalNode, newRaw);
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Build a CallExpression visitor for the main rule create() function.
|
|
233
|
+
*
|
|
234
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-NESTED-DESCRIBE REQ-TEST-ERROR-CONTEXT
|
|
235
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
236
|
+
*/
|
|
237
|
+
function handleCallExpression(context, options) {
|
|
238
|
+
const { describeRegex, requireDescribeStory } = options;
|
|
239
|
+
return (node) => {
|
|
240
|
+
const calleeName = getCalleeName(node);
|
|
241
|
+
if (!calleeName || !isTestCallName(calleeName)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const description = getFirstArgumentLiteral(node);
|
|
245
|
+
if (!description)
|
|
246
|
+
return;
|
|
247
|
+
if (calleeName === "describe") {
|
|
248
|
+
handleDescribeCall(context, node, description, {
|
|
249
|
+
describeRegex,
|
|
250
|
+
requireDescribeStory,
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (calleeName === "it" || calleeName === "test") {
|
|
255
|
+
handleItOrTestCall(context, node, description, options);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Rule } from "eslint";
|
|
2
|
+
/**
|
|
3
|
+
* Enforce traceability conventions in test files.
|
|
4
|
+
*
|
|
5
|
+
* This rule validates that:
|
|
6
|
+
* - Test files have a file-level @supports annotation listing tested requirements.
|
|
7
|
+
* - describe()/it()/test()/context() blocks include story and requirement references
|
|
8
|
+
* following project conventions.
|
|
9
|
+
* - When ESLint runs with --fix, safe, non-semantic auto-fixes are applied for
|
|
10
|
+
* missing file-level @supports and malformed [REQ-XXX] prefixes in test names.
|
|
11
|
+
*
|
|
12
|
+
* @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
|
|
13
|
+
* @req REQ-TEST-FILE-SUPPORTS
|
|
14
|
+
* @req REQ-TEST-DESCRIBE-STORY
|
|
15
|
+
* @req REQ-TEST-IT-REQ-PREFIX
|
|
16
|
+
* @req REQ-TEST-SUPPORTS-VALID
|
|
17
|
+
* @req REQ-TEST-PATTERN-DETECT
|
|
18
|
+
* @req REQ-TEST-FRAMEWORK-COMPAT
|
|
19
|
+
* @req REQ-TEST-NESTED-DESCRIBE
|
|
20
|
+
* @req REQ-TEST-ERROR-CONTEXT
|
|
21
|
+
* @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
|
|
22
|
+
*/
|
|
23
|
+
declare const rule: Rule.RuleModule;
|
|
24
|
+
export default rule;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const require_test_traceability_helpers_1 = require("./helpers/require-test-traceability-helpers");
|
|
4
|
+
/**
|
|
5
|
+
* Enforce traceability conventions in test files.
|
|
6
|
+
*
|
|
7
|
+
* This rule validates that:
|
|
8
|
+
* - Test files have a file-level @supports annotation listing tested requirements.
|
|
9
|
+
* - describe()/it()/test()/context() blocks include story and requirement references
|
|
10
|
+
* following project conventions.
|
|
11
|
+
* - When ESLint runs with --fix, safe, non-semantic auto-fixes are applied for
|
|
12
|
+
* missing file-level @supports and malformed [REQ-XXX] prefixes in test names.
|
|
13
|
+
*
|
|
14
|
+
* @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
|
|
15
|
+
* @req REQ-TEST-FILE-SUPPORTS
|
|
16
|
+
* @req REQ-TEST-DESCRIBE-STORY
|
|
17
|
+
* @req REQ-TEST-IT-REQ-PREFIX
|
|
18
|
+
* @req REQ-TEST-SUPPORTS-VALID
|
|
19
|
+
* @req REQ-TEST-PATTERN-DETECT
|
|
20
|
+
* @req REQ-TEST-FRAMEWORK-COMPAT
|
|
21
|
+
* @req REQ-TEST-NESTED-DESCRIBE
|
|
22
|
+
* @req REQ-TEST-ERROR-CONTEXT
|
|
23
|
+
* @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
|
|
24
|
+
*/
|
|
25
|
+
const rule = {
|
|
26
|
+
meta: {
|
|
27
|
+
type: "problem",
|
|
28
|
+
docs: {
|
|
29
|
+
description: "Enforce traceability annotations and naming conventions in test files",
|
|
30
|
+
recommended: "error",
|
|
31
|
+
},
|
|
32
|
+
fixable: "code",
|
|
33
|
+
schema: [
|
|
34
|
+
{
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
testFilePatterns: {
|
|
38
|
+
type: "array",
|
|
39
|
+
items: { type: "string" },
|
|
40
|
+
default: [
|
|
41
|
+
"**/tests/**/*.test.{js,ts}",
|
|
42
|
+
"**/tests/**/*.spec.{js,ts}",
|
|
43
|
+
"**/__tests__/**/*.{js,ts}",
|
|
44
|
+
"**/*.{test,spec}.{js,ts}",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
requireDescribeStory: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
default: true,
|
|
50
|
+
},
|
|
51
|
+
requireTestReqPrefix: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default: true,
|
|
54
|
+
},
|
|
55
|
+
describePattern: {
|
|
56
|
+
type: "string",
|
|
57
|
+
default: "Story [0-9]+\\.[0-9]+-",
|
|
58
|
+
},
|
|
59
|
+
autoFixTestTemplate: {
|
|
60
|
+
type: "boolean",
|
|
61
|
+
default: true,
|
|
62
|
+
},
|
|
63
|
+
autoFixTestPrefixFormat: {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
default: true,
|
|
66
|
+
},
|
|
67
|
+
testSupportsTemplate: {
|
|
68
|
+
type: "string",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
messages: {
|
|
75
|
+
missingFileSupports: "Test file must have @supports annotation listing tested requirements.",
|
|
76
|
+
missingDescribeStory: "describe() block should reference story (e.g., 'Story 009.0-DEV-...').",
|
|
77
|
+
missingReqPrefix: "Test name should start with requirement ID (e.g., '[REQ-MAINT-DETECT] ...').",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
create(context) {
|
|
81
|
+
const filename = context.getFilename();
|
|
82
|
+
const rawOptions = (context.options && context.options[0]) || {};
|
|
83
|
+
const { testFilePatterns = [
|
|
84
|
+
"/tests/",
|
|
85
|
+
"/test/",
|
|
86
|
+
"/__tests__",
|
|
87
|
+
".test.",
|
|
88
|
+
".spec.",
|
|
89
|
+
], requireDescribeStory = true, requireTestReqPrefix = true, describePattern = "Story [0-9]+\\.[0-9]+-", autoFixTestTemplate = true, autoFixTestPrefixFormat = true, testSupportsTemplate, } = rawOptions;
|
|
90
|
+
const isTestFile = (0, require_test_traceability_helpers_1.determineIsTestFile)(filename, testFilePatterns);
|
|
91
|
+
if (!isTestFile)
|
|
92
|
+
return {};
|
|
93
|
+
const sourceCode = context.getSourceCode();
|
|
94
|
+
(0, require_test_traceability_helpers_1.ensureFileSupportsAnnotation)(context, sourceCode, {
|
|
95
|
+
autoFixTestTemplate,
|
|
96
|
+
testSupportsTemplate,
|
|
97
|
+
});
|
|
98
|
+
const describeRegex = new RegExp(describePattern);
|
|
99
|
+
return {
|
|
100
|
+
// @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-NESTED-DESCRIBE REQ-TEST-ERROR-CONTEXT
|
|
101
|
+
// @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
102
|
+
CallExpression: (0, require_test_traceability_helpers_1.handleCallExpression)(context, {
|
|
103
|
+
sourceCode,
|
|
104
|
+
describeRegex,
|
|
105
|
+
requireDescribeStory,
|
|
106
|
+
requireTestReqPrefix,
|
|
107
|
+
autoFixTestPrefixFormat,
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
exports.default = rule;
|
|
@@ -56,6 +56,7 @@ describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", ()
|
|
|
56
56
|
"valid-story-reference",
|
|
57
57
|
"valid-req-reference",
|
|
58
58
|
"prefer-implements-annotation",
|
|
59
|
+
"require-test-traceability",
|
|
59
60
|
];
|
|
60
61
|
// Act: get actual rule names from plugin
|
|
61
62
|
const actual = Object.keys(index_1.rules);
|
|
@@ -80,6 +81,7 @@ describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", ()
|
|
|
80
81
|
expect(recommendedRules).toHaveProperty("traceability/require-branch-annotation", "error");
|
|
81
82
|
expect(recommendedRules).toHaveProperty("traceability/valid-story-reference", "error");
|
|
82
83
|
expect(recommendedRules).toHaveProperty("traceability/valid-req-reference", "error");
|
|
84
|
+
expect(recommendedRules).toHaveProperty("traceability/require-test-traceability", "error");
|
|
83
85
|
});
|
|
84
86
|
it("[REQ-ERROR-SEVERITY] configs.strict uses same severity mapping as recommended", () => {
|
|
85
87
|
const strictRules = index_1.configs.strict[0].rules;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
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
|
|
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
|
|
12
|
+
*/
|
|
13
|
+
const eslint_1 = require("eslint");
|
|
14
|
+
const require_test_traceability_1 = __importDefault(require("../../src/rules/require-test-traceability"));
|
|
15
|
+
const ruleTester = new eslint_1.RuleTester({
|
|
16
|
+
languageOptions: {
|
|
17
|
+
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
describe("require-test-traceability rule (Stories 020.0 and 021.0)", () => {
|
|
21
|
+
ruleTester.run("require-test-traceability", require_test_traceability_1.default, {
|
|
22
|
+
valid: [
|
|
23
|
+
{
|
|
24
|
+
// [REQ-TEST-FILE-SUPPORTS] file-level @supports present and describe/test satisfied
|
|
25
|
+
code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS\n */\ndescribe('Story 020.0-DEV-TEST-ANNOTATION-VALIDATION', () => { it('[REQ-EXAMPLE] does something', () => {}); });`,
|
|
26
|
+
filename: "tests/rules/require-test-traceability.test.ts",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
// [REQ-TEST-FRAMEWORK-COMPAT] mocha style `context` is treated as a test call but only name checks apply
|
|
30
|
+
code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FRAMEWORK-COMPAT\n */\ncontext('Story 020.0-DEV-TEST-ANNOTATION-VALIDATION', () => {});`,
|
|
31
|
+
filename: "tests/some/context.test.ts",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
// Ensure non-test files are ignored (REQ-TEST-PATTERN-DETECT)
|
|
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', () => {});`,
|
|
36
|
+
filename: "src/not-a-test-file.ts",
|
|
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
|
+
},
|
|
43
|
+
],
|
|
44
|
+
invalid: [
|
|
45
|
+
{
|
|
46
|
+
// [REQ-TEST-FIX-TEMPLATE] missing @supports in test file -> insert default placeholder template
|
|
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', () => {}); });`,
|
|
49
|
+
filename: "tests/rules/missing-supports.test.ts",
|
|
50
|
+
errors: [{ messageId: "missingFileSupports" }],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
// [REQ-TEST-DESCRIBE-STORY] describe without story phrase still reported (no auto-fix)
|
|
54
|
+
code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY\n */\ndescribe('no story reference here', () => {});`,
|
|
55
|
+
filename: "tests/rules/bad-describe.test.ts",
|
|
56
|
+
errors: [{ messageId: "missingDescribeStory" }],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
// [REQ-TEST-IT-REQ-PREFIX][REQ-TEST-FIX-NO-INFERENCE] test name without any REQ prefix -> error but no auto-fix
|
|
60
|
+
code: `/**\n * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-IT-REQ-PREFIX\n */\nit('missing prefix', () => {});`,
|
|
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",
|
|
90
|
+
errors: [{ messageId: "missingReqPrefix" }],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
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",
|
|
@@ -189,6 +189,55 @@ function example() {
|
|
|
189
189
|
}
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
+
### traceability/require-test-traceability
|
|
193
|
+
|
|
194
|
+
Description: Enforces traceability conventions in test files by requiring:
|
|
195
|
+
|
|
196
|
+
- A file-level `@supports` annotation indicating which story and requirement(s) the test file exercises.
|
|
197
|
+
- Story references in top-level `describe` blocks.
|
|
198
|
+
- Requirement identifiers in `it`/`test` names using a `[REQ-XXX]` prefix.
|
|
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. 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
|
+
|
|
202
|
+
Options:
|
|
203
|
+
|
|
204
|
+
The rule accepts an optional configuration object:
|
|
205
|
+
|
|
206
|
+
- `testFilePatterns` (string[], optional) – Glob-style patterns (relative to the project root) used to identify test files. Only files matching at least one pattern are checked by this rule. Defaults to `["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"]`.
|
|
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
|
+
- `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
|
+
- `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.
|
|
213
|
+
|
|
214
|
+
Behavior notes:
|
|
215
|
+
|
|
216
|
+
- The rule only analyzes files whose paths match `testFilePatterns`.
|
|
217
|
+
- File-level `@supports` annotations are typically placed in a JSDoc block at the top of the file; the rule checks that at least one `@supports` tag is present and that it includes a story/requirement reference (for example, `@supports docs/stories/010.0-PAYMENTS.story.md#REQ-PAYMENTS-REFUND`).
|
|
218
|
+
- Top-level `describe` calls (such as `describe("payments refunds docs/stories/010.0-PAYMENTS.story.md", ...)`) are inspected when `requireDescribeStory` is `true`. Their first argument must be a string literal that satisfies `describePattern`.
|
|
219
|
+
- Test cases declared via `it(...)` or `test(...)` must use a string literal name beginning with a requirement prefix like `[REQ-PAYMENTS-REFUND]` when `requireTestReqPrefix` is `true`.
|
|
220
|
+
|
|
221
|
+
Default Severity: `error`
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
/**
|
|
227
|
+
* @supports docs/stories/010.0-PAYMENTS.story.md#REQ-PAYMENTS-REFUND
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
describe("Refunds flow docs/stories/010.0-PAYMENTS.story.md", () => {
|
|
231
|
+
it("[REQ-PAYMENTS-REFUND] issues refund on successful request", () => {
|
|
232
|
+
// ...
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("[REQ-PAYMENTS-REFUND-EDGE] handles partial refunds", () => {
|
|
236
|
+
// ...
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
192
241
|
## Configuration Presets
|
|
193
242
|
|
|
194
243
|
The plugin provides two built-in presets for easy configuration:
|
|
@@ -208,6 +257,7 @@ Core rules enabled by the `recommended` preset:
|
|
|
208
257
|
- `traceability/valid-annotation-format`: `warn`
|
|
209
258
|
- `traceability/valid-story-reference`: `error`
|
|
210
259
|
- `traceability/valid-req-reference`: `error`
|
|
260
|
+
- `traceability/require-test-traceability`: `error`
|
|
211
261
|
|
|
212
262
|
Usage:
|
|
213
263
|
|