eslint-plugin-traceability 1.2.0 → 1.4.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
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: docs/stories/007.0-DEV-ERROR-REPORTING.story.md
8
+ * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
9
+ * @req REQ-ERROR-SPECIFIC - Specific details about what annotation is missing or invalid
10
+ * @req REQ-ERROR-SUGGESTION - Suggest concrete steps to fix the issue
11
+ * @req REQ-ERROR-CONTEXT - Include relevant context in error messages
12
+ */
13
+ const eslint_1 = require("eslint");
14
+ const require_story_annotation_1 = __importDefault(require("../../src/rules/require-story-annotation"));
15
+ const ruleTester = new eslint_1.RuleTester({
16
+ languageOptions: {
17
+ parserOptions: { ecmaVersion: 2020, sourceType: "module" },
18
+ },
19
+ });
20
+ describe("Error Reporting Enhancements for require-story-annotation (Story 007.0-DEV-ERROR-REPORTING)", () => {
21
+ ruleTester.run("require-story-annotation", require_story_annotation_1.default, {
22
+ valid: [
23
+ {
24
+ name: "[007.0-DEV-ERROR-REPORTING] valid with existing annotation",
25
+ code: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */ function foo() {}`,
26
+ },
27
+ ],
28
+ invalid: [
29
+ {
30
+ name: "[REQ-ERROR-SPECIFIC] missing @story annotation should report specific details and suggestion",
31
+ code: `function bar() {}`,
32
+ errors: [
33
+ {
34
+ messageId: "missingStory",
35
+ data: { name: "bar" },
36
+ suggestions: [
37
+ {
38
+ desc: "Add JSDoc @story annotation for function 'bar', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
39
+ output: "/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction bar() {}",
40
+ },
41
+ ],
42
+ },
43
+ ],
44
+ },
45
+ ],
46
+ });
47
+ });
@@ -120,6 +120,18 @@ while (condition) {
120
120
  doSomething();
121
121
  }`,
122
122
  },
123
+ {
124
+ name: "[REQ-CONFIGURABLE-SCOPE] custom branchTypes ignores unlisted branch types",
125
+ code: `switch (value) { case 'a': break; }`,
126
+ options: [{ branchTypes: ["IfStatement"] }],
127
+ },
128
+ {
129
+ name: "[REQ-CONFIGURABLE-SCOPE] custom branchTypes only enforce listed types",
130
+ code: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
131
+ // @req REQ-BRANCH-DETECTION
132
+ if (condition) {}`,
133
+ options: [{ branchTypes: ["IfStatement", "SwitchCase"] }],
134
+ },
123
135
  ],
124
136
  invalid: [
125
137
  {
@@ -248,6 +260,32 @@ try {
248
260
  { messageId: "missingAnnotation", data: { missing: "@req" } },
249
261
  ],
250
262
  },
263
+ {
264
+ name: "[REQ-CONFIGURABLE-SCOPE] missing annotations on configured branch type ForStatement",
265
+ code: `for (let i = 0; i < 3; i++) {}`,
266
+ options: [{ branchTypes: ["ForStatement"] }],
267
+ output: `// @story <story-file>.story.md
268
+ for (let i = 0; i < 3; i++) {}`,
269
+ errors: [
270
+ { messageId: "missingAnnotation", data: { missing: "@story" } },
271
+ { messageId: "missingAnnotation", data: { missing: "@req" } },
272
+ ],
273
+ },
274
+ ],
275
+ });
276
+ ruleTester.run("require-branch-annotation", require_branch_annotation_1.default, {
277
+ valid: [],
278
+ invalid: [
279
+ {
280
+ name: "[REQ-CONFIGURABLE-SCOPE] invalid branchTypes option should error schema",
281
+ code: "if (condition) {}",
282
+ options: [{ branchTypes: ["UnknownType"] }],
283
+ errors: [
284
+ {
285
+ message: /should be equal to one of the allowed values/,
286
+ },
287
+ ],
288
+ },
251
289
  ],
252
290
  });
253
291
  });
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ // @ts-nocheck
6
7
  /**
7
8
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
8
9
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -65,52 +66,100 @@ declare function tsDecl(): void;`,
65
66
  {
66
67
  name: "[REQ-ANNOTATION-REQUIRED] missing @story annotation on function",
67
68
  code: `function bar() {}`,
68
- output: `/** @story <story-file>.story.md */\nfunction bar() {}`,
69
- errors: [{ messageId: "missingStory" }],
69
+ errors: [
70
+ {
71
+ messageId: "missingStory",
72
+ suggestions: [
73
+ {
74
+ desc: `Add JSDoc @story annotation for function 'bar', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
75
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction bar() {}`,
76
+ },
77
+ ],
78
+ },
79
+ ],
70
80
  },
71
81
  {
72
82
  name: "[REQ-ANNOTATION-REQUIRED] missing @story on function expression",
73
83
  code: `const fnExpr = function() {};`,
74
- output: `/** @story <story-file>.story.md */\nconst fnExpr = function() {};`,
75
- errors: [{ messageId: "missingStory" }],
84
+ errors: [
85
+ {
86
+ messageId: "missingStory",
87
+ suggestions: [
88
+ {
89
+ desc: `Add JSDoc @story annotation for function 'fnExpr', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
90
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst fnExpr = function() {};`,
91
+ },
92
+ ],
93
+ },
94
+ ],
76
95
  },
77
96
  {
78
97
  name: "[REQ-ANNOTATION-REQUIRED] missing @story on arrow function",
79
98
  code: `const arrowFn = () => {};`,
80
- output: `/** @story <story-file>.story.md */\nconst arrowFn = () => {};`,
81
- errors: [{ messageId: "missingStory" }],
99
+ errors: [
100
+ {
101
+ messageId: "missingStory",
102
+ suggestions: [
103
+ {
104
+ desc: `Add JSDoc @story annotation for function 'arrowFn', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
105
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst arrowFn = () => {};`,
106
+ },
107
+ ],
108
+ },
109
+ ],
82
110
  },
83
111
  {
84
112
  name: "[REQ-ANNOTATION-REQUIRED] missing @story on class method",
85
113
  code: `class C {\n method() {}\n}`,
86
- output: `class C {\n /** @story <story-file>.story.md */\n method() {}\n}`,
87
- errors: [{ messageId: "missingStory" }],
114
+ errors: [
115
+ {
116
+ messageId: "missingStory",
117
+ suggestions: [
118
+ {
119
+ desc: `Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
120
+ output: `class C {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
121
+ },
122
+ ],
123
+ },
124
+ ],
88
125
  },
89
126
  {
90
127
  name: "[REQ-ANNOTATION-REQUIRED] missing @story on TS declare function",
91
128
  code: `declare function tsDecl(): void;`,
92
- output: `/** @story <story-file>.story.md */
93
- declare function tsDecl(): void;`,
94
129
  languageOptions: {
95
130
  parser: require("@typescript-eslint/parser"),
96
131
  parserOptions: { ecmaVersion: 2020, sourceType: "module" },
97
132
  },
98
- errors: [{ messageId: "missingStory" }],
133
+ errors: [
134
+ {
135
+ messageId: "missingStory",
136
+ suggestions: [
137
+ {
138
+ desc: `Add JSDoc @story annotation for function 'tsDecl', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
139
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ndeclare function tsDecl(): void;`,
140
+ },
141
+ ],
142
+ },
143
+ ],
99
144
  },
100
145
  {
101
146
  name: "[REQ-ANNOTATION-REQUIRED] missing @story on TS method signature",
102
- code: `interface D {
103
- method(): void;
104
- }`,
105
- output: `/** @story <story-file>.story.md */
106
- interface D {
107
- method(): void;
108
- }`,
147
+ code: `interface D {\n method(): void;\n}`,
109
148
  languageOptions: {
110
149
  parser: require("@typescript-eslint/parser"),
111
150
  parserOptions: { ecmaVersion: 2020, sourceType: "module" },
112
151
  },
113
- errors: [{ messageId: "missingStory" }],
152
+ errors: [
153
+ {
154
+ messageId: "missingStory",
155
+ suggestions: [
156
+ {
157
+ desc: `Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
158
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ninterface D {\n method(): void;\n}`,
159
+ },
160
+ ],
161
+ },
162
+ ],
114
163
  },
115
164
  ],
116
165
  });
@@ -131,16 +180,34 @@ interface D {
131
180
  {
132
181
  name: "[exportPriority] exported function missing @story annotation",
133
182
  code: `export function exportedMissing() {}`,
134
- output: `/** @story <story-file>.story.md */\nexport function exportedMissing() {}`,
135
183
  options: [{ exportPriority: "exported" }],
136
- errors: [{ messageId: "missingStory" }],
184
+ errors: [
185
+ {
186
+ messageId: "missingStory",
187
+ suggestions: [
188
+ {
189
+ desc: `Add JSDoc @story annotation for function 'exportedMissing', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
190
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nexport function exportedMissing() {}`,
191
+ },
192
+ ],
193
+ },
194
+ ],
137
195
  },
138
196
  {
139
197
  name: "[exportPriority] exported arrow function missing @story annotation",
140
198
  code: `export const arrowExported = () => {};`,
141
- output: `/** @story <story-file>.story.md */\nexport const arrowExported = () => {};`,
142
199
  options: [{ exportPriority: "exported" }],
143
- errors: [{ messageId: "missingStory" }],
200
+ errors: [
201
+ {
202
+ messageId: "missingStory",
203
+ suggestions: [
204
+ {
205
+ desc: `Add JSDoc @story annotation for function 'arrowExported', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
206
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nexport const arrowExported = () => {};`,
207
+ },
208
+ ],
209
+ },
210
+ ],
144
211
  },
145
212
  ],
146
213
  });
@@ -157,8 +224,17 @@ interface D {
157
224
  name: "[scope] function declaration missing annotation when scope is FunctionDeclaration",
158
225
  code: `function onlyDecl() {}`,
159
226
  options: [{ scope: ["FunctionDeclaration"] }],
160
- output: `/** @story <story-file>.story.md */\nfunction onlyDecl() {}`,
161
- errors: [{ messageId: "missingStory" }],
227
+ errors: [
228
+ {
229
+ messageId: "missingStory",
230
+ suggestions: [
231
+ {
232
+ desc: `Add JSDoc @story annotation for function 'onlyDecl', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
233
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction onlyDecl() {}`,
234
+ },
235
+ ],
236
+ },
237
+ ],
162
238
  },
163
239
  ],
164
240
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
6
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
7
+ */
8
+ const eslint_1 = require("eslint");
9
+ const annotation_checker_1 = require("../../src/utils/annotation-checker");
10
+ const ruleTester = new eslint_1.RuleTester();
11
+ const rule = {
12
+ meta: {
13
+ type: "problem",
14
+ fixable: "code",
15
+ docs: {
16
+ description: "Test helper for checking @req annotation",
17
+ recommended: "error",
18
+ },
19
+ messages: { missingReq: "Missing @req annotation" },
20
+ schema: [],
21
+ },
22
+ create(context) {
23
+ return {
24
+ /**
25
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
26
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
27
+ */
28
+ TSDeclareFunction: (node) => (0, annotation_checker_1.checkReqAnnotation)(context, node),
29
+ /**
30
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
31
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
32
+ */
33
+ TSMethodSignature: (node) => (0, annotation_checker_1.checkReqAnnotation)(context, node),
34
+ };
35
+ },
36
+ };
37
+ describe("annotation-checker helper", () => {
38
+ ruleTester.run("annotation-checker", rule, {
39
+ valid: [
40
+ {
41
+ name: "[REQ-TYPESCRIPT-SUPPORT] valid TSDeclareFunction with @req",
42
+ code: `/** @req REQ-TEST */\ndeclare function foo(): void;`,
43
+ languageOptions: {
44
+ parser: require("@typescript-eslint/parser"),
45
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
46
+ },
47
+ },
48
+ {
49
+ name: "[REQ-TYPESCRIPT-SUPPORT] valid TSMethodSignature with @req",
50
+ code: `interface I { /** @req REQ-TEST */ method(): void; }`,
51
+ languageOptions: {
52
+ parser: require("@typescript-eslint/parser"),
53
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
54
+ },
55
+ },
56
+ ],
57
+ invalid: [
58
+ {
59
+ name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSDeclareFunction",
60
+ code: `declare function foo(): void;`,
61
+ output: `/** @req <REQ-ID> */\ndeclare function foo(): void;`,
62
+ errors: [{ messageId: "missingReq" }],
63
+ languageOptions: {
64
+ parser: require("@typescript-eslint/parser"),
65
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
66
+ },
67
+ },
68
+ {
69
+ name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
70
+ code: `interface I { method(): void; }`,
71
+ output: `interface I { /** @req <REQ-ID> */\nmethod(): void; }`,
72
+ errors: [{ messageId: "missingReq" }],
73
+ languageOptions: {
74
+ parser: require("@typescript-eslint/parser"),
75
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
76
+ },
77
+ },
78
+ ],
79
+ });
80
+ });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Unit tests for branch annotation helpers
5
+ * Tests for: docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
6
+ * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
7
+ * @req REQ-CONFIGURABLE-SCOPE - Allow configuration of branch types for annotation enforcement
8
+ */
9
+ const branch_annotation_helpers_1 = require("../../src/utils/branch-annotation-helpers");
10
+ describe("validateBranchTypes helper", () => {
11
+ let context;
12
+ beforeEach(() => {
13
+ context = { options: [], report: jest.fn() };
14
+ });
15
+ it("should return default branch types when no options provided", () => {
16
+ const result = (0, branch_annotation_helpers_1.validateBranchTypes)(context);
17
+ expect(Array.isArray(result)).toBe(true);
18
+ expect(result).toEqual(branch_annotation_helpers_1.DEFAULT_BRANCH_TYPES);
19
+ });
20
+ it("should return custom branch types when valid options provided", () => {
21
+ context.options = [{ branchTypes: ["IfStatement", "ForStatement"] }];
22
+ const result = (0, branch_annotation_helpers_1.validateBranchTypes)(context);
23
+ expect(Array.isArray(result)).toBe(true);
24
+ expect(result).toEqual(["IfStatement", "ForStatement"]);
25
+ });
26
+ it("should return listener when invalid branch types provided and report errors", () => {
27
+ const invalid = ["UnknownType", "Foo"];
28
+ context.options = [{ branchTypes: invalid }];
29
+ // Invoke helper
30
+ const result = (0, branch_annotation_helpers_1.validateBranchTypes)(context);
31
+ // Should return a listener object
32
+ expect(typeof result).toBe("object");
33
+ expect(result).toHaveProperty("Program");
34
+ // Call the Program listener
35
+ const fakeNode = {};
36
+ result.Program(fakeNode);
37
+ // report should be called for each invalid type
38
+ expect(context.report).toHaveBeenCalledTimes(invalid.length);
39
+ invalid.forEach((t) => {
40
+ expect(context.report).toHaveBeenCalledWith(expect.objectContaining({
41
+ node: fakeNode,
42
+ message: expect.stringContaining(`Value "${t}" should be equal to one of the allowed values:`),
43
+ }));
44
+ });
45
+ });
46
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-traceability",
3
- "version": "1.2.0",
3
+ "version": "1.4.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",
@@ -19,7 +19,7 @@
19
19
  "test": "jest --ci --bail",
20
20
  "format": "prettier --write .",
21
21
  "format:check": "prettier --check .",
22
- "duplication": "jscpd src tests --reporters console --threshold 3",
22
+ "duplication": "jscpd src tests --reporters console --threshold 3 --ignore tests/utils/**",
23
23
  "audit:dev-high": "node scripts/generate-dev-deps-audit.js",
24
24
  "smoke-test": "./scripts/smoke-test.sh",
25
25
  "prepare": "husky install"