eslint-plugin-traceability 1.6.0 → 1.6.2
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/lib/src/index.d.ts +6 -0
- package/lib/src/index.js +6 -0
- package/lib/src/rules/helpers/require-story-helpers.d.ts +7 -0
- package/lib/src/rules/helpers/require-story-helpers.js +9 -2
- package/lib/src/rules/require-branch-annotation.js +5 -1
- package/lib/src/rules/require-req-annotation.d.ts +9 -4
- package/lib/src/rules/require-req-annotation.js +82 -22
- package/lib/src/rules/require-story-annotation.d.ts +8 -0
- package/lib/src/rules/require-story-annotation.js +17 -2
- package/lib/src/rules/valid-annotation-format.js +34 -2
- package/lib/src/rules/valid-req-reference.js +14 -0
- package/lib/src/rules/valid-story-reference.js +21 -0
- package/lib/src/utils/annotation-checker.d.ts +7 -1
- package/lib/src/utils/annotation-checker.js +145 -12
- package/lib/tests/cli-error-handling.test.js +1 -1
- package/lib/tests/plugin-default-export-and-configs.test.js +16 -0
- package/lib/tests/rules/auto-fix-behavior-008.test.js +76 -0
- package/lib/tests/rules/error-reporting.test.js +72 -23
- package/lib/tests/rules/require-branch-annotation.test.js +5 -1
- package/lib/tests/rules/require-req-annotation.test.js +228 -8
- package/lib/tests/rules/require-story-annotation.test.js +1 -0
- package/lib/tests/rules/valid-annotation-format.test.js +5 -0
- package/lib/tests/rules/valid-req-reference.test.js +6 -0
- package/lib/tests/rules/valid-story-reference.test.js +5 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkReqAnnotation = checkReqAnnotation;
|
|
4
4
|
const require_story_utils_1 = require("../rules/helpers/require-story-utils");
|
|
5
|
+
const require_story_io_1 = require("../rules/helpers/require-story-io");
|
|
5
6
|
/**
|
|
6
7
|
* Helper to retrieve the JSDoc comment for a node.
|
|
7
8
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -42,17 +43,129 @@ function combineComments(leading, before) {
|
|
|
42
43
|
function commentContainsReq(c) {
|
|
43
44
|
return c && typeof c.value === "string" && c.value.includes("@req");
|
|
44
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Line-based helper adapted from linesBeforeHasStory to detect @req.
|
|
48
|
+
*/
|
|
49
|
+
function linesBeforeHasReq(sourceCode, node) {
|
|
50
|
+
const lines = sourceCode && sourceCode.lines;
|
|
51
|
+
const startLine = node && node.loc && typeof node.loc.start?.line === "number"
|
|
52
|
+
? node.loc.start.line
|
|
53
|
+
: null;
|
|
54
|
+
if (!Array.isArray(lines) || typeof startLine !== "number") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const from = Math.max(0, startLine - 1 - require_story_io_1.LOOKBACK_LINES);
|
|
58
|
+
const to = Math.max(0, startLine - 1);
|
|
59
|
+
for (let i = from; i < to; i++) {
|
|
60
|
+
const text = lines[i];
|
|
61
|
+
if (typeof text === "string" && text.includes("@req")) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parent-chain helper adapted from parentChainHasStory to detect @req.
|
|
69
|
+
*/
|
|
70
|
+
function parentChainHasReq(sourceCode, node) {
|
|
71
|
+
let p = node && node.parent;
|
|
72
|
+
while (p) {
|
|
73
|
+
const pComments = typeof sourceCode?.getCommentsBefore === "function"
|
|
74
|
+
? sourceCode.getCommentsBefore(p) || []
|
|
75
|
+
: [];
|
|
76
|
+
if (Array.isArray(pComments) &&
|
|
77
|
+
pComments.some((c) => typeof c.value === "string" && c.value.includes("@req"))) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const pLeading = p.leadingComments || [];
|
|
81
|
+
if (Array.isArray(pLeading) &&
|
|
82
|
+
pLeading.some((c) => typeof c.value === "string" && c.value.includes("@req"))) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
p = p.parent;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Fallback text window helper adapted from fallbackTextBeforeHasStory to detect @req.
|
|
91
|
+
*/
|
|
92
|
+
function fallbackTextBeforeHasReq(sourceCode, node) {
|
|
93
|
+
if (typeof sourceCode?.getText !== "function" ||
|
|
94
|
+
!Array.isArray((node && node.range) || [])) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const range = node.range;
|
|
98
|
+
if (!Array.isArray(range) || typeof range[0] !== "number") {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const start = Math.max(0, range[0] - require_story_io_1.FALLBACK_WINDOW);
|
|
103
|
+
const textBefore = sourceCode.getText().slice(start, range[0]);
|
|
104
|
+
if (typeof textBefore === "string" && textBefore.includes("@req")) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
/* noop */
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
45
113
|
/**
|
|
46
114
|
* Helper to determine whether a JSDoc or any nearby comments contain a @req annotation.
|
|
47
115
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
48
116
|
* @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
|
|
49
117
|
*/
|
|
50
|
-
function hasReqAnnotation(jsdoc, comments) {
|
|
118
|
+
function hasReqAnnotation(jsdoc, comments, context, node) {
|
|
119
|
+
try {
|
|
120
|
+
const sourceCode = context && typeof context.getSourceCode === "function"
|
|
121
|
+
? context.getSourceCode()
|
|
122
|
+
: undefined;
|
|
123
|
+
if (sourceCode && node) {
|
|
124
|
+
if (linesBeforeHasReq(sourceCode, node) ||
|
|
125
|
+
parentChainHasReq(sourceCode, node) ||
|
|
126
|
+
fallbackTextBeforeHasReq(sourceCode, node)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Swallow detection errors and fall through to simple checks.
|
|
133
|
+
}
|
|
134
|
+
// BRANCH @req detection on JSDoc or comments
|
|
135
|
+
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
136
|
+
// @req REQ-ANNOTATION-REQ-DETECTION
|
|
51
137
|
return ((jsdoc &&
|
|
52
138
|
typeof jsdoc.value === "string" &&
|
|
53
139
|
jsdoc.value.includes("@req")) ||
|
|
54
140
|
comments.some(commentContainsReq));
|
|
55
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Determine the most appropriate node to attach an inserted JSDoc to.
|
|
144
|
+
* Prefers outer function-like constructs such as methods, variable declarators,
|
|
145
|
+
* or wrapping expression statements for function expressions.
|
|
146
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
147
|
+
* @req REQ-ANNOTATION-AUTOFIX - Provide autofix for missing @req annotation
|
|
148
|
+
*/
|
|
149
|
+
function getFixTargetNode(node) {
|
|
150
|
+
const parent = node && node.parent;
|
|
151
|
+
if (!parent) {
|
|
152
|
+
return node;
|
|
153
|
+
}
|
|
154
|
+
// If the node is part of a class/obj method definition, attach to the MethodDefinition
|
|
155
|
+
if (parent.type === "MethodDefinition") {
|
|
156
|
+
return parent;
|
|
157
|
+
}
|
|
158
|
+
// If the node is the init of a variable declarator, attach to the VariableDeclarator
|
|
159
|
+
if (parent.type === "VariableDeclarator" && parent.init === node) {
|
|
160
|
+
return parent;
|
|
161
|
+
}
|
|
162
|
+
// If the parent is an expression statement (e.g. IIFE or assigned via expression),
|
|
163
|
+
// attach to the outer ExpressionStatement.
|
|
164
|
+
if (parent.type === "ExpressionStatement") {
|
|
165
|
+
return parent;
|
|
166
|
+
}
|
|
167
|
+
return node;
|
|
168
|
+
}
|
|
56
169
|
/**
|
|
57
170
|
* Creates a fix function that inserts a missing @req JSDoc before the node.
|
|
58
171
|
* Returned function is a proper named function so no inline arrow is used.
|
|
@@ -60,8 +173,9 @@ function hasReqAnnotation(jsdoc, comments) {
|
|
|
60
173
|
* @req REQ-ANNOTATION-AUTOFIX - Provide autofix for missing @req annotation
|
|
61
174
|
*/
|
|
62
175
|
function createMissingReqFix(node) {
|
|
176
|
+
const target = getFixTargetNode(node);
|
|
63
177
|
return function missingReqFix(fixer) {
|
|
64
|
-
return fixer.insertTextBefore(
|
|
178
|
+
return fixer.insertTextBefore(target, "/** @req <REQ-ID> */\n");
|
|
65
179
|
};
|
|
66
180
|
}
|
|
67
181
|
/**
|
|
@@ -72,30 +186,49 @@ function createMissingReqFix(node) {
|
|
|
72
186
|
* @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
|
|
73
187
|
* @req REQ-ERROR-SPECIFIC - Provide specific error details including node name
|
|
74
188
|
* @req REQ-ERROR-LOCATION - Include contextual location information in errors
|
|
189
|
+
* @req REQ-ERROR-SUGGESTION - Provide actionable suggestions or fixes where possible
|
|
190
|
+
* @req REQ-ERROR-CONTEXT - Include contextual hints to help understand the error
|
|
75
191
|
*/
|
|
76
|
-
function reportMissing(context, node) {
|
|
77
|
-
const rawName = (0, require_story_utils_1.getNodeName)(node);
|
|
192
|
+
function reportMissing(context, node, enableFix = true) {
|
|
193
|
+
const rawName = (0, require_story_utils_1.getNodeName)(node) ?? (node && (0, require_story_utils_1.getNodeName)(node.parent));
|
|
78
194
|
const name = rawName ?? "(anonymous)";
|
|
79
|
-
|
|
80
|
-
node
|
|
195
|
+
const nameNode = (node && node.id && node.id.type === "Identifier"
|
|
196
|
+
? node.id
|
|
197
|
+
: node && node.key && node.key.type === "Identifier"
|
|
198
|
+
? node.key
|
|
199
|
+
: node) ?? node;
|
|
200
|
+
const reportOptions = {
|
|
201
|
+
node: nameNode,
|
|
81
202
|
messageId: "missingReq",
|
|
82
|
-
data: { name },
|
|
83
|
-
|
|
84
|
-
|
|
203
|
+
data: { name, functionName: name },
|
|
204
|
+
};
|
|
205
|
+
if (enableFix) {
|
|
206
|
+
reportOptions.fix = createMissingReqFix(node);
|
|
207
|
+
}
|
|
208
|
+
context.report(reportOptions);
|
|
85
209
|
}
|
|
86
210
|
/**
|
|
87
211
|
* Helper to check @req annotation presence on TS declare functions and method signatures.
|
|
212
|
+
* This helper is intentionally scope/exportPriority agnostic and focuses solely
|
|
213
|
+
* on detection and reporting of @req annotations for the given node.
|
|
88
214
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
89
215
|
* @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
|
|
216
|
+
* @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
|
|
217
|
+
* @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
|
|
90
218
|
*/
|
|
91
|
-
function checkReqAnnotation(context, node) {
|
|
219
|
+
function checkReqAnnotation(context, node, options) {
|
|
220
|
+
const { enableFix = true } = options ?? {};
|
|
92
221
|
const sourceCode = context.getSourceCode();
|
|
93
222
|
const jsdoc = getJsdocComment(sourceCode, node);
|
|
94
223
|
const leading = getLeadingComments(node);
|
|
95
224
|
const comments = getCommentsBefore(sourceCode, node);
|
|
96
225
|
const all = combineComments(leading, comments);
|
|
97
|
-
const hasReq = hasReqAnnotation(jsdoc, all);
|
|
226
|
+
const hasReq = hasReqAnnotation(jsdoc, all, context, node);
|
|
227
|
+
// BRANCH when a @req annotation is missing and must be reported
|
|
228
|
+
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
229
|
+
// @req REQ-ANNOTATION-REQ-DETECTION
|
|
230
|
+
// @req REQ-ANNOTATION-REPORTING
|
|
98
231
|
if (!hasReq) {
|
|
99
|
-
reportMissing(context, node);
|
|
232
|
+
reportMissing(context, node, enableFix);
|
|
100
233
|
}
|
|
101
234
|
}
|
|
@@ -39,6 +39,6 @@ describe("CLI Error Handling for Traceability Plugin (Story 001.0-DEV-PLUGIN-SET
|
|
|
39
39
|
});
|
|
40
40
|
// Expect non-zero exit and missing annotation message on stdout
|
|
41
41
|
expect(result.status).not.toBe(0);
|
|
42
|
-
expect(result.stdout).toContain("
|
|
42
|
+
expect(result.stdout).toContain("Function 'foo' must have an explicit @story annotation. Add a JSDoc or line comment with @story that points to the implementing story file (for example, docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md)");
|
|
43
43
|
});
|
|
44
44
|
});
|
|
@@ -36,7 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
/**
|
|
37
37
|
* Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
38
38
|
* @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
39
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
39
40
|
* @req REQ-PLUGIN-STRUCTURE - Validate plugin default export and configs in src/index.ts
|
|
41
|
+
* @req REQ-ERROR-SEVERITY - Validate error severity configuration in plugin configs
|
|
40
42
|
*/
|
|
41
43
|
const index_1 = __importStar(require("../src/index"));
|
|
42
44
|
describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", () => {
|
|
@@ -69,4 +71,18 @@ describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", ()
|
|
|
69
71
|
const strictRules = index_1.configs.strict[0].rules;
|
|
70
72
|
expect(strictRules).toEqual(index_1.configs.recommended[0].rules);
|
|
71
73
|
});
|
|
74
|
+
it("[REQ-ERROR-SEVERITY] configs.recommended maps valid-annotation-format to warn and others to error", () => {
|
|
75
|
+
const recommendedRules = index_1.configs.recommended[0].rules;
|
|
76
|
+
expect(recommendedRules).toHaveProperty("traceability/valid-annotation-format", "warn");
|
|
77
|
+
expect(recommendedRules).toHaveProperty("traceability/require-story-annotation", "error");
|
|
78
|
+
expect(recommendedRules).toHaveProperty("traceability/require-req-annotation", "error");
|
|
79
|
+
expect(recommendedRules).toHaveProperty("traceability/require-branch-annotation", "error");
|
|
80
|
+
expect(recommendedRules).toHaveProperty("traceability/valid-story-reference", "error");
|
|
81
|
+
expect(recommendedRules).toHaveProperty("traceability/valid-req-reference", "error");
|
|
82
|
+
});
|
|
83
|
+
it("[REQ-ERROR-SEVERITY] configs.strict uses same severity mapping as recommended", () => {
|
|
84
|
+
const strictRules = index_1.configs.strict[0].rules;
|
|
85
|
+
const recommendedRules = index_1.configs.recommended[0].rules;
|
|
86
|
+
expect(strictRules).toEqual(recommendedRules);
|
|
87
|
+
});
|
|
72
88
|
});
|
|
@@ -28,6 +28,10 @@ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
|
|
|
28
28
|
name: "[REQ-AUTOFIX-MISSING] already annotated function is unchanged",
|
|
29
29
|
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction alreadyAnnotated() {}`,
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: "[REQ-AUTOFIX-MISSING] already annotated class method is unchanged",
|
|
33
|
+
code: `class A {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
|
|
34
|
+
},
|
|
31
35
|
],
|
|
32
36
|
invalid: [
|
|
33
37
|
{
|
|
@@ -46,6 +50,78 @@ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
|
|
|
46
50
|
},
|
|
47
51
|
],
|
|
48
52
|
},
|
|
53
|
+
{
|
|
54
|
+
name: "[REQ-AUTOFIX-MISSING] adds @story before function expression when missing",
|
|
55
|
+
code: `const fnExpr = function() {};`,
|
|
56
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst fnExpr = function() {};`,
|
|
57
|
+
errors: [
|
|
58
|
+
{
|
|
59
|
+
messageId: "missingStory",
|
|
60
|
+
suggestions: [
|
|
61
|
+
{
|
|
62
|
+
desc: "Add JSDoc @story annotation for function 'fnExpr', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
|
|
63
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst fnExpr = function() {};`,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "[REQ-AUTOFIX-MISSING] adds @story before class method when missing",
|
|
71
|
+
code: `class C {\n method() {}\n}`,
|
|
72
|
+
output: `class C {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
|
|
73
|
+
errors: [
|
|
74
|
+
{
|
|
75
|
+
messageId: "missingStory",
|
|
76
|
+
suggestions: [
|
|
77
|
+
{
|
|
78
|
+
desc: "Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
|
|
79
|
+
output: `class C {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "[REQ-AUTOFIX-MISSING] adds @story before TS declare function when missing",
|
|
87
|
+
code: `declare function tsDecl(): void;`,
|
|
88
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ndeclare function tsDecl(): void;`,
|
|
89
|
+
languageOptions: {
|
|
90
|
+
parser: require("@typescript-eslint/parser"),
|
|
91
|
+
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
|
|
92
|
+
},
|
|
93
|
+
errors: [
|
|
94
|
+
{
|
|
95
|
+
messageId: "missingStory",
|
|
96
|
+
suggestions: [
|
|
97
|
+
{
|
|
98
|
+
desc: "Add JSDoc @story annotation for function 'tsDecl', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
|
|
99
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ndeclare function tsDecl(): void;`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "[REQ-AUTOFIX-MISSING] adds @story before TS method signature when missing",
|
|
107
|
+
code: `interface D {\n method(): void;\n}`,
|
|
108
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ninterface D {\n method(): void;\n}`,
|
|
109
|
+
languageOptions: {
|
|
110
|
+
parser: require("@typescript-eslint/parser"),
|
|
111
|
+
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
|
|
112
|
+
},
|
|
113
|
+
errors: [
|
|
114
|
+
{
|
|
115
|
+
messageId: "missingStory",
|
|
116
|
+
suggestions: [
|
|
117
|
+
{
|
|
118
|
+
desc: "Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
|
|
119
|
+
output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ninterface D {\n method(): void;\n}`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
49
125
|
],
|
|
50
126
|
});
|
|
51
127
|
});
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
* @req REQ-ERROR-SPECIFIC - Specific details about what annotation is missing or invalid
|
|
10
10
|
* @req REQ-ERROR-SUGGESTION - Suggest concrete steps to fix the issue
|
|
11
11
|
* @req REQ-ERROR-CONTEXT - Include relevant context in error messages
|
|
12
|
+
* @req REQ-ERROR-LOCATION - Include precise location information in error messages
|
|
12
13
|
*/
|
|
13
14
|
const eslint_1 = require("eslint");
|
|
14
15
|
const require_story_annotation_1 = __importDefault(require("../../src/rules/require-story-annotation"));
|
|
@@ -18,31 +19,79 @@ const ruleTester = new eslint_1.RuleTester({
|
|
|
18
19
|
},
|
|
19
20
|
});
|
|
20
21
|
describe("Error Reporting Enhancements for require-story-annotation (Story 007.0-DEV-ERROR-REPORTING)", () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
describe("valid cases", () => {
|
|
23
|
+
ruleTester.run("require-story-annotation", require_story_annotation_1.default, {
|
|
24
|
+
valid: [
|
|
25
|
+
{
|
|
26
|
+
name: "[007.0-DEV-ERROR-REPORTING] valid with existing annotation",
|
|
27
|
+
code: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */ function foo() {}`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
invalid: [],
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("REQ-ERROR-SPECIFIC - missing @story annotation should report specific details and suggestion", () => {
|
|
34
|
+
it("reports specific message, data, and suggestions for function 'bar'", () => {
|
|
35
|
+
const code = "function bar() {}";
|
|
36
|
+
const reported = [];
|
|
37
|
+
const context = {
|
|
38
|
+
id: "require-story-annotation",
|
|
39
|
+
options: [],
|
|
40
|
+
report: (descriptor) => {
|
|
41
|
+
reported.push(descriptor);
|
|
42
|
+
},
|
|
43
|
+
getFilename: () => "test.js",
|
|
44
|
+
getSourceCode: () => ({
|
|
45
|
+
text: code,
|
|
46
|
+
getText: () => code,
|
|
47
|
+
ast: {
|
|
48
|
+
type: "Program",
|
|
49
|
+
body: [],
|
|
50
|
+
sourceType: "module",
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
const listeners = require_story_annotation_1.default.create(context);
|
|
55
|
+
// Minimal synthetic AST nodes for the visitors
|
|
56
|
+
const programNode = {
|
|
57
|
+
type: "Program",
|
|
58
|
+
body: [
|
|
34
59
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
],
|
|
60
|
+
type: "FunctionDeclaration",
|
|
61
|
+
id: { type: "Identifier", name: "bar" },
|
|
62
|
+
params: [],
|
|
63
|
+
body: {
|
|
64
|
+
type: "BlockStatement",
|
|
65
|
+
body: [],
|
|
66
|
+
},
|
|
43
67
|
},
|
|
44
68
|
],
|
|
45
|
-
|
|
46
|
-
|
|
69
|
+
sourceType: "module",
|
|
70
|
+
};
|
|
71
|
+
const functionNode = programNode.body[0];
|
|
72
|
+
// Invoke visitors if they exist
|
|
73
|
+
if (typeof listeners.Program === "function") {
|
|
74
|
+
listeners.Program(programNode);
|
|
75
|
+
}
|
|
76
|
+
if (typeof listeners.FunctionDeclaration === "function") {
|
|
77
|
+
listeners.FunctionDeclaration(functionNode);
|
|
78
|
+
}
|
|
79
|
+
expect(reported.length).toBe(1);
|
|
80
|
+
const error = reported[0];
|
|
81
|
+
// Message template should be defined and contain the {{name}} placeholder
|
|
82
|
+
const template = require_story_annotation_1.default.meta?.messages?.missingStory;
|
|
83
|
+
expect(typeof template).toBe("string");
|
|
84
|
+
expect(template.length).toBeGreaterThan(0);
|
|
85
|
+
expect(template.includes("{{name}}")).toBe(true);
|
|
86
|
+
// Ensure messageId and data wiring is correct
|
|
87
|
+
expect(error.messageId).toBe("missingStory");
|
|
88
|
+
expect(error.data).toEqual({ name: "bar", functionName: "bar" });
|
|
89
|
+
// Suggestions
|
|
90
|
+
expect(Array.isArray(error.suggest)).toBe(true);
|
|
91
|
+
expect(error.suggest.length).toBeGreaterThanOrEqual(1);
|
|
92
|
+
const suggestion = error.suggest[0];
|
|
93
|
+
expect(suggestion.desc).toBe("Add JSDoc @story annotation for function 'bar', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */");
|
|
94
|
+
expect(suggestion.fix).toBeDefined();
|
|
95
|
+
});
|
|
47
96
|
});
|
|
48
97
|
});
|
|
@@ -4,9 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
/**
|
|
7
|
-
* Tests for: docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
7
|
+
* Tests for: docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md, docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
8
8
|
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
9
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
9
10
|
* @req REQ-BRANCH-DETECTION - Verify require-branch-annotation rule enforces branch annotations
|
|
11
|
+
* @req REQ-ERROR-SPECIFIC - Branch-level missing-annotation error messages are specific and informative
|
|
12
|
+
* @req REQ-ERROR-CONSISTENCY - Branch-level missing-annotation error messages follow shared conventions
|
|
13
|
+
* @req REQ-ERROR-SUGGESTION - Branch-level missing-annotation errors include suggestions when applicable
|
|
10
14
|
*/
|
|
11
15
|
const eslint_1 = require("eslint");
|
|
12
16
|
const require_branch_annotation_1 = __importDefault(require("../../src/rules/require-branch-annotation"));
|