eslint-plugin-traceability 1.6.1 → 1.6.3
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.js +13 -1
- package/lib/src/rules/require-story-annotation.d.ts +4 -0
- package/lib/src/rules/require-story-annotation.js +5 -1
- package/lib/src/rules/valid-annotation-format.js +14 -2
- package/lib/src/rules/valid-req-reference.js +14 -0
- package/lib/src/rules/valid-story-reference.js +93 -9
- package/lib/src/utils/annotation-checker.js +94 -4
- package/lib/src/utils/storyReferenceUtils.d.ts +28 -0
- package/lib/src/utils/storyReferenceUtils.js +32 -0
- 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/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 +102 -17
- 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 +268 -2
- package/package.json +1 -1
|
@@ -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"));
|
|
@@ -108,17 +108,32 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
108
108
|
{
|
|
109
109
|
name: "[REQ-ANNOTATION-REQUIRED] missing @req on function without JSDoc",
|
|
110
110
|
code: `function baz() {}`,
|
|
111
|
-
errors: [
|
|
111
|
+
errors: [
|
|
112
|
+
{
|
|
113
|
+
messageId: "missingReq",
|
|
114
|
+
data: { name: "baz", functionName: "baz" },
|
|
115
|
+
},
|
|
116
|
+
],
|
|
112
117
|
},
|
|
113
118
|
{
|
|
114
119
|
name: "[REQ-ANNOTATION-REQUIRED] missing @req on function with only @story annotation",
|
|
115
120
|
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction qux() {}`,
|
|
116
|
-
errors: [
|
|
121
|
+
errors: [
|
|
122
|
+
{
|
|
123
|
+
messageId: "missingReq",
|
|
124
|
+
data: { name: "qux", functionName: "qux" },
|
|
125
|
+
},
|
|
126
|
+
],
|
|
117
127
|
},
|
|
118
128
|
{
|
|
119
129
|
name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSDeclareFunction",
|
|
120
130
|
code: `declare function baz(): void;`,
|
|
121
|
-
errors: [
|
|
131
|
+
errors: [
|
|
132
|
+
{
|
|
133
|
+
messageId: "missingReq",
|
|
134
|
+
data: { name: "baz", functionName: "baz" },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
122
137
|
languageOptions: {
|
|
123
138
|
parser: require("@typescript-eslint/parser"),
|
|
124
139
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -127,7 +142,12 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
127
142
|
{
|
|
128
143
|
name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
|
|
129
144
|
code: `interface I { method(): void; }`,
|
|
130
|
-
errors: [
|
|
145
|
+
errors: [
|
|
146
|
+
{
|
|
147
|
+
messageId: "missingReq",
|
|
148
|
+
data: { name: "method", functionName: "method" },
|
|
149
|
+
},
|
|
150
|
+
],
|
|
131
151
|
languageOptions: {
|
|
132
152
|
parser: require("@typescript-eslint/parser"),
|
|
133
153
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -136,27 +156,52 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
136
156
|
{
|
|
137
157
|
name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on FunctionExpression assigned to variable",
|
|
138
158
|
code: `const fn = function () {};`,
|
|
139
|
-
errors: [
|
|
159
|
+
errors: [
|
|
160
|
+
{
|
|
161
|
+
messageId: "missingReq",
|
|
162
|
+
data: { name: "fn", functionName: "fn" },
|
|
163
|
+
},
|
|
164
|
+
],
|
|
140
165
|
},
|
|
141
166
|
{
|
|
142
167
|
name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on anonymous FunctionExpression (no variable name)",
|
|
143
168
|
code: `(function () {})();`,
|
|
144
|
-
errors: [
|
|
169
|
+
errors: [
|
|
170
|
+
{
|
|
171
|
+
messageId: "missingReq",
|
|
172
|
+
data: { name: "(anonymous)", functionName: "(anonymous)" },
|
|
173
|
+
},
|
|
174
|
+
],
|
|
145
175
|
},
|
|
146
176
|
{
|
|
147
177
|
name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in class",
|
|
148
178
|
code: `class C {\n m() {}\n}`,
|
|
149
|
-
errors: [
|
|
179
|
+
errors: [
|
|
180
|
+
{
|
|
181
|
+
messageId: "missingReq",
|
|
182
|
+
data: { name: "m", functionName: "m" },
|
|
183
|
+
},
|
|
184
|
+
],
|
|
150
185
|
},
|
|
151
186
|
{
|
|
152
187
|
name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in object literal",
|
|
153
188
|
code: `const o = { m() {} };`,
|
|
154
|
-
errors: [
|
|
189
|
+
errors: [
|
|
190
|
+
{
|
|
191
|
+
messageId: "missingReq",
|
|
192
|
+
data: { name: "m", functionName: "m" },
|
|
193
|
+
},
|
|
194
|
+
],
|
|
155
195
|
},
|
|
156
196
|
{
|
|
157
197
|
name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on TS FunctionExpression in variable declarator",
|
|
158
198
|
code: `const fn = function () {};`,
|
|
159
|
-
errors: [
|
|
199
|
+
errors: [
|
|
200
|
+
{
|
|
201
|
+
messageId: "missingReq",
|
|
202
|
+
data: { name: "fn", functionName: "fn" },
|
|
203
|
+
},
|
|
204
|
+
],
|
|
160
205
|
languageOptions: {
|
|
161
206
|
parser: require("@typescript-eslint/parser"),
|
|
162
207
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -165,7 +210,12 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
165
210
|
{
|
|
166
211
|
name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on exported TS FunctionExpression in variable declarator",
|
|
167
212
|
code: `export const fn = function () {};`,
|
|
168
|
-
errors: [
|
|
213
|
+
errors: [
|
|
214
|
+
{
|
|
215
|
+
messageId: "missingReq",
|
|
216
|
+
data: { name: "fn", functionName: "fn" },
|
|
217
|
+
},
|
|
218
|
+
],
|
|
169
219
|
languageOptions: {
|
|
170
220
|
parser: require("@typescript-eslint/parser"),
|
|
171
221
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -175,43 +225,78 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
175
225
|
name: "[REQ-CONFIGURABLE-SCOPE][Story 003.0] FunctionDeclaration still reported when scope only includes FunctionDeclaration",
|
|
176
226
|
code: `function scoped() {}`,
|
|
177
227
|
options: [{ scope: ["FunctionDeclaration"] }],
|
|
178
|
-
errors: [
|
|
228
|
+
errors: [
|
|
229
|
+
{
|
|
230
|
+
messageId: "missingReq",
|
|
231
|
+
data: { name: "scoped", functionName: "scoped" },
|
|
232
|
+
},
|
|
233
|
+
],
|
|
179
234
|
},
|
|
180
235
|
{
|
|
181
236
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported function reported when exportPriority is 'exported'",
|
|
182
237
|
code: `export function exportedFn() {}`,
|
|
183
238
|
options: [{ exportPriority: "exported" }],
|
|
184
|
-
errors: [
|
|
239
|
+
errors: [
|
|
240
|
+
{
|
|
241
|
+
messageId: "missingReq",
|
|
242
|
+
data: { name: "exportedFn", functionName: "exportedFn" },
|
|
243
|
+
},
|
|
244
|
+
],
|
|
185
245
|
},
|
|
186
246
|
{
|
|
187
247
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported function reported when exportPriority is 'non-exported'",
|
|
188
248
|
code: `function nonExported() {}`,
|
|
189
249
|
options: [{ exportPriority: "non-exported" }],
|
|
190
|
-
errors: [
|
|
250
|
+
errors: [
|
|
251
|
+
{
|
|
252
|
+
messageId: "missingReq",
|
|
253
|
+
data: { name: "nonExported", functionName: "nonExported" },
|
|
254
|
+
},
|
|
255
|
+
],
|
|
191
256
|
},
|
|
192
257
|
{
|
|
193
258
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported method reported when exportPriority is 'exported'",
|
|
194
259
|
code: `export class C {\n m() {}\n}`,
|
|
195
|
-
errors: [
|
|
260
|
+
errors: [
|
|
261
|
+
{
|
|
262
|
+
messageId: "missingReq",
|
|
263
|
+
data: { name: "m", functionName: "m" },
|
|
264
|
+
},
|
|
265
|
+
],
|
|
196
266
|
options: [{ exportPriority: "exported" }],
|
|
197
267
|
},
|
|
198
268
|
{
|
|
199
269
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported method reported when exportPriority is 'non-exported'",
|
|
200
270
|
code: `class C {\n m() {}\n}`,
|
|
201
|
-
errors: [
|
|
271
|
+
errors: [
|
|
272
|
+
{
|
|
273
|
+
messageId: "missingReq",
|
|
274
|
+
data: { name: "m", functionName: "m" },
|
|
275
|
+
},
|
|
276
|
+
],
|
|
202
277
|
options: [{ exportPriority: "non-exported" }],
|
|
203
278
|
},
|
|
204
279
|
{
|
|
205
280
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported FunctionExpression reported when exportPriority is 'exported'",
|
|
206
281
|
code: `export const fn = function () {};`,
|
|
207
282
|
options: [{ exportPriority: "exported" }],
|
|
208
|
-
errors: [
|
|
283
|
+
errors: [
|
|
284
|
+
{
|
|
285
|
+
messageId: "missingReq",
|
|
286
|
+
data: { name: "fn", functionName: "fn" },
|
|
287
|
+
},
|
|
288
|
+
],
|
|
209
289
|
},
|
|
210
290
|
{
|
|
211
291
|
name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported FunctionExpression reported when exportPriority is 'non-exported'",
|
|
212
292
|
code: `const fn = function () {};`,
|
|
213
293
|
options: [{ exportPriority: "non-exported" }],
|
|
214
|
-
errors: [
|
|
294
|
+
errors: [
|
|
295
|
+
{
|
|
296
|
+
messageId: "missingReq",
|
|
297
|
+
data: { name: "fn", functionName: "fn" },
|
|
298
|
+
},
|
|
299
|
+
],
|
|
215
300
|
},
|
|
216
301
|
],
|
|
217
302
|
});
|
|
@@ -105,6 +105,7 @@ declare function tsDecl(): void;`,
|
|
|
105
105
|
errors: [
|
|
106
106
|
{
|
|
107
107
|
messageId: "missingStory",
|
|
108
|
+
data: { name: "method", functionName: "method" },
|
|
108
109
|
suggestions: [
|
|
109
110
|
{
|
|
110
111
|
desc: `Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
|
|
@@ -7,6 +7,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
* Tests for: docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
8
8
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
9
9
|
* @req REQ-FORMAT-SPECIFICATION - Verify valid-annotation-format rule enforces annotation format syntax
|
|
10
|
+
* Tests for: docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
11
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
12
|
+
* @req REQ-ERROR-MESSAGES-CONSISTENT - Verify invalid annotation errors use consistent wording and structure
|
|
13
|
+
* @req REQ-ERROR-MESSAGES-ACTIONABLE - Verify invalid annotation errors provide actionable guidance and examples
|
|
14
|
+
* @req REQ-ERROR-MESSAGES-IDENTIFIERS - Verify invalid annotation errors echo the offending identifier/path in the message
|
|
10
15
|
*/
|
|
11
16
|
const eslint_1 = require("eslint");
|
|
12
17
|
const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
|
|
@@ -7,6 +7,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
* Tests for: docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
8
8
|
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
9
9
|
* @req REQ-DEEP-PARSE - Verify valid-req-reference rule enforces existing requirement content
|
|
10
|
+
*
|
|
11
|
+
* Additional coverage for error reporting behavior:
|
|
12
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
13
|
+
* @req REQ-ERROR-SPECIFIC - Verify requirement-level errors identify the exact missing requirement
|
|
14
|
+
* @req REQ-ERROR-CONTEXT - Verify requirement-level errors include relevant story path context
|
|
15
|
+
* @req REQ-ERROR-CONSISTENCY - Verify requirement-level error messages are consistent across cases
|
|
10
16
|
*/
|
|
11
17
|
const eslint_1 = require("eslint");
|
|
12
18
|
const valid_req_reference_1 = __importDefault(require("../../src/rules/valid-req-reference"));
|