eslint-plugin-traceability 1.11.1 → 1.11.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/CHANGELOG.md +2 -2
- package/README.md +3 -3
- package/lib/src/index.d.ts +10 -5
- package/lib/src/index.js +71 -6
- package/lib/src/maintenance/commands.js +2 -3
- package/lib/src/maintenance/update.js +1 -14
- package/lib/src/rules/helpers/require-story-core.d.ts +12 -4
- package/lib/src/rules/helpers/require-story-core.js +59 -30
- package/lib/src/rules/helpers/require-story-helpers.d.ts +7 -41
- package/lib/src/rules/helpers/require-story-helpers.js +13 -70
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +12 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -16
- package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +29 -3
- package/lib/src/rules/helpers/valid-annotation-format-validators.js +29 -3
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.js +10 -10
- package/lib/src/rules/helpers/valid-req-reference-helpers.d.ts +11 -0
- package/lib/src/rules/helpers/valid-req-reference-helpers.js +362 -0
- package/lib/src/rules/prefer-implements-annotation.js +7 -7
- package/lib/src/rules/require-story-annotation.d.ts +2 -0
- package/lib/src/rules/require-story-annotation.js +1 -1
- package/lib/src/rules/valid-req-reference.d.ts +4 -0
- package/lib/src/rules/valid-req-reference.js +5 -349
- package/lib/src/rules/valid-story-reference.d.ts +1 -1
- package/lib/src/rules/valid-story-reference.js +17 -10
- package/lib/src/utils/branch-annotation-helpers.d.ts +2 -2
- package/lib/src/utils/branch-annotation-helpers.js +96 -17
- package/lib/tests/cli-error-handling.test.js +1 -1
- package/lib/tests/config/eslint-config-validation.test.js +73 -0
- package/lib/tests/fixtures/stale/example.js +1 -1
- package/lib/tests/fixtures/update/example.js +1 -1
- package/lib/tests/integration/catch-annotation-prettier.integration.test.d.ts +1 -0
- package/lib/tests/integration/catch-annotation-prettier.integration.test.js +131 -0
- package/lib/tests/integration/dogfooding-validation.test.d.ts +1 -0
- package/lib/tests/integration/dogfooding-validation.test.js +94 -0
- package/lib/tests/maintenance/cli.test.js +37 -0
- package/lib/tests/maintenance/detect-isolated.test.js +5 -5
- package/lib/tests/perf/maintenance-cli-large-workspace.test.js +18 -0
- package/lib/tests/perf/require-branch-annotation-large-file.test.d.ts +1 -0
- package/lib/tests/perf/require-branch-annotation-large-file.test.js +67 -0
- package/lib/tests/perf/valid-annotation-format-large-file.test.d.ts +1 -0
- package/lib/tests/perf/valid-annotation-format-large-file.test.js +74 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +1 -0
- package/lib/tests/plugin-setup.test.js +12 -1
- package/lib/tests/rules/prefer-implements-annotation.test.js +84 -70
- package/lib/tests/rules/require-branch-annotation.test.js +33 -1
- package/lib/tests/rules/valid-annotation-format-internal.test.d.ts +8 -0
- package/lib/tests/rules/valid-annotation-format-internal.test.js +47 -0
- package/lib/tests/utils/branch-annotation-catch-insert-position.test.d.ts +1 -0
- package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +68 -0
- package/lib/tests/utils/branch-annotation-catch-position.test.d.ts +1 -0
- package/lib/tests/utils/branch-annotation-catch-position.test.js +115 -0
- package/lib/tests/utils/req-annotation-detection.test.d.ts +1 -0
- package/lib/tests/utils/req-annotation-detection.test.js +247 -0
- package/package.json +4 -4
- package/user-docs/api-reference.md +20 -12
- package/user-docs/examples.md +2 -1
- package/user-docs/migration-guide.md +11 -7
|
@@ -18,80 +18,87 @@ const ruleTester = new eslint_1.RuleTester({
|
|
|
18
18
|
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
|
-
describe("prefer-implements-annotation
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
reason: "comment mixes @story/@req with existing @supports annotations",
|
|
60
|
-
},
|
|
21
|
+
describe("prefer-supports-annotation / prefer-implements-annotation aliasing (Story 010.3-DEV-MIGRATE-TO-SUPPORTS)", () => {
|
|
22
|
+
const valid = [
|
|
23
|
+
{
|
|
24
|
+
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with only @story is ignored",
|
|
25
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction onlyStory() {}`,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with only @req is ignored",
|
|
29
|
+
code: `/**\n * @req REQ-ONLY\n */\nfunction onlyReq() {}`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @supports only is ignored",
|
|
33
|
+
code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction alreadyImplements() {}`,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @story and @supports but no @req is ignored",
|
|
37
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction storyAndSupportsNoReq() {}`,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @req and @supports but no @story is ignored",
|
|
41
|
+
code: `/**\n * @req REQ-ANNOTATION-REQUIRED\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction reqAndSupportsNoStory() {}`,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
const invalid = [
|
|
45
|
+
{
|
|
46
|
+
name: "[REQ-OPTIONAL-WARNING] single-story @story + @req block triggers preferImplements message",
|
|
47
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
|
|
48
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
|
|
49
|
+
errors: [{ messageId: "preferImplements" }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @supports triggers cannotAutoFix",
|
|
53
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction mixed() {}`,
|
|
54
|
+
errors: [
|
|
55
|
+
{
|
|
56
|
+
messageId: "cannotAutoFix",
|
|
57
|
+
data: {
|
|
58
|
+
reason: "comment mixes @story/@req with existing @supports annotations",
|
|
61
59
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "[REQ-MULTI-STORY-DETECT] multiple @story paths in same block trigger multiStoryDetected",
|
|
65
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md\n * @req REQ-BRANCH-DETECTION\n */\nfunction multiStory() {}`,
|
|
66
|
+
errors: [{ messageId: "multiStoryDetected" }],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @supports line",
|
|
70
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
|
|
71
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
|
|
72
|
+
errors: [{ messageId: "preferImplements" }],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @supports line containing all REQ IDs",
|
|
76
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ONE\n * @req REQ-TWO\n * @req REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
|
|
77
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ONE REQ-TWO REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
|
|
78
|
+
errors: [{ messageId: "preferImplements" }],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "[REQ-AUTO-FIX] complex @req content (extra description) does not auto-fix but still warns",
|
|
82
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED must handle extra description\n */\nfunction complexReqNoAutoFix() {}`,
|
|
83
|
+
errors: [{ messageId: "preferImplements" }],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "[REQ-AUTO-FIX] complex @story content (extra description) does not auto-fix but still warns",
|
|
87
|
+
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md additional descriptive text\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction complexStoryNoAutoFix() {}`,
|
|
88
|
+
errors: [{ messageId: "preferImplements" }],
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
ruleTester.run("prefer-implements-annotation", prefer_implements_annotation_1.default, {
|
|
92
|
+
valid,
|
|
93
|
+
invalid,
|
|
94
|
+
});
|
|
95
|
+
ruleTester.run("prefer-supports-annotation", prefer_implements_annotation_1.default, {
|
|
96
|
+
valid,
|
|
97
|
+
invalid,
|
|
92
98
|
});
|
|
93
99
|
});
|
|
94
100
|
describe("prefer-implements-annotation configuration severity (REQ-CONFIG-SEVERITY)", () => {
|
|
101
|
+
// Story 010.3 / REQ-RULE-NAME: verify aliasing semantics for new primary rule name and deprecated alias
|
|
95
102
|
test("rule is disabled by default in recommended and strict presets (not present in preset rule maps)", () => {
|
|
96
103
|
const recommended = src_1.configs.recommended;
|
|
97
104
|
expect(Array.isArray(recommended)).toBe(true);
|
|
@@ -99,27 +106,34 @@ describe("prefer-implements-annotation configuration severity (REQ-CONFIG-SEVERI
|
|
|
99
106
|
expect(firstConfig).toBeDefined();
|
|
100
107
|
const rules = firstConfig.rules || {};
|
|
101
108
|
expect(rules["traceability/prefer-implements-annotation"]).toBeUndefined();
|
|
109
|
+
expect(rules["traceability/prefer-supports-annotation"]).toBeUndefined();
|
|
102
110
|
const strict = src_1.configs.strict;
|
|
103
111
|
expect(Array.isArray(strict)).toBe(true);
|
|
104
112
|
const strictFirstConfig = strict[0];
|
|
105
113
|
expect(strictFirstConfig).toBeDefined();
|
|
106
114
|
const strictRules = strictFirstConfig.rules || {};
|
|
107
115
|
expect(strictRules["traceability/prefer-implements-annotation"]).toBeUndefined();
|
|
116
|
+
expect(strictRules["traceability/prefer-supports-annotation"]).toBeUndefined();
|
|
108
117
|
});
|
|
109
118
|
test("rule can be configured with severity 'warn' or 'error' in flat config", () => {
|
|
119
|
+
// Story 010.3 / REQ-RULE-NAME: both primary and alias rule keys must be accepted in flat config
|
|
110
120
|
const flatWarnConfig = {
|
|
111
121
|
files: ["**/*.ts"],
|
|
112
122
|
rules: {
|
|
113
123
|
"traceability/prefer-implements-annotation": "warn",
|
|
124
|
+
"traceability/prefer-supports-annotation": "warn",
|
|
114
125
|
},
|
|
115
126
|
};
|
|
116
127
|
expect(flatWarnConfig.rules["traceability/prefer-implements-annotation"]).toBe("warn");
|
|
128
|
+
expect(flatWarnConfig.rules["traceability/prefer-supports-annotation"]).toBe("warn");
|
|
117
129
|
const flatErrorConfig = {
|
|
118
130
|
files: ["**/*.ts"],
|
|
119
131
|
rules: {
|
|
120
132
|
"traceability/prefer-implements-annotation": "error",
|
|
133
|
+
"traceability/prefer-supports-annotation": "error",
|
|
121
134
|
},
|
|
122
135
|
};
|
|
123
136
|
expect(flatErrorConfig.rules["traceability/prefer-implements-annotation"]).toBe("error");
|
|
137
|
+
expect(flatErrorConfig.rules["traceability/prefer-supports-annotation"]).toBe("error");
|
|
124
138
|
});
|
|
125
139
|
});
|
|
@@ -11,7 +11,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
11
11
|
* @req REQ-ERROR-SPECIFIC - Branch-level missing-annotation error messages are specific and informative
|
|
12
12
|
* @req REQ-ERROR-CONSISTENCY - Branch-level missing-annotation error messages follow shared conventions
|
|
13
13
|
* @req REQ-ERROR-SUGGESTION - Branch-level missing-annotation errors include suggestions when applicable
|
|
14
|
-
* @
|
|
14
|
+
* @req REQ-NESTED-HANDLING - Nested branch annotations are correctly enforced without duplicative reporting
|
|
15
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-BRANCH-DETECTION REQ-NESTED-HANDLING
|
|
15
16
|
* @supports docs/stories/007.0-DEV-ERROR-REPORTING.story.md REQ-ERROR-SPECIFIC REQ-ERROR-CONSISTENCY REQ-ERROR-SUGGESTION
|
|
16
17
|
*/
|
|
17
18
|
const eslint_1 = require("eslint");
|
|
@@ -122,6 +123,18 @@ while (condition) {
|
|
|
122
123
|
// @req REQ-BRANCH-DETECTION
|
|
123
124
|
case 'a':
|
|
124
125
|
break;
|
|
126
|
+
}`,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "[REQ-NESTED-HANDLING] nested if-statements with annotations on outer and inner branches",
|
|
130
|
+
code: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
131
|
+
// @req REQ-BRANCH-DETECTION
|
|
132
|
+
if (outer) {
|
|
133
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
134
|
+
// @req REQ-NESTED-HANDLING
|
|
135
|
+
if (inner) {
|
|
136
|
+
doWork();
|
|
137
|
+
}
|
|
125
138
|
}`,
|
|
126
139
|
},
|
|
127
140
|
{
|
|
@@ -246,6 +259,25 @@ try {
|
|
|
246
259
|
// @story <story-file>.story.md
|
|
247
260
|
case 'a':
|
|
248
261
|
break;
|
|
262
|
+
}`,
|
|
263
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "[REQ-NESTED-HANDLING] missing annotations on nested if-statement inside annotated outer branch",
|
|
267
|
+
code: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
268
|
+
// @req REQ-BRANCH-DETECTION
|
|
269
|
+
if (outer) {
|
|
270
|
+
if (inner) {
|
|
271
|
+
doWork();
|
|
272
|
+
}
|
|
273
|
+
}`,
|
|
274
|
+
output: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
275
|
+
// @req REQ-BRANCH-DETECTION
|
|
276
|
+
if (outer) {
|
|
277
|
+
// @story <story-file>.story.md
|
|
278
|
+
if (inner) {
|
|
279
|
+
doWork();
|
|
280
|
+
}
|
|
249
281
|
}`,
|
|
250
282
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
251
283
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for: docs/stories/024.0-DEV-IGNORE-INLINE-CODE-REFS.story.md
|
|
3
|
+
* @story docs/stories/024.0-DEV-IGNORE-INLINE-CODE-REFS.story.md
|
|
4
|
+
* @req REQ-IGNORE-INLINE-CODE - Strip backtick-wrapped content before annotation detection
|
|
5
|
+
* @req REQ-PRESERVE-BOUNDARIES - Replace backtick-wrapped content with spaces to preserve word boundaries
|
|
6
|
+
* @req REQ-CENTRALIZED-FILTER - Apply backtick filtering in normalizeCommentLine for all rules
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for: docs/stories/024.0-DEV-IGNORE-INLINE-CODE-REFS.story.md
|
|
4
|
+
* @story docs/stories/024.0-DEV-IGNORE-INLINE-CODE-REFS.story.md
|
|
5
|
+
* @req REQ-IGNORE-INLINE-CODE - Strip backtick-wrapped content before annotation detection
|
|
6
|
+
* @req REQ-PRESERVE-BOUNDARIES - Replace backtick-wrapped content with spaces to preserve word boundaries
|
|
7
|
+
* @req REQ-CENTRALIZED-FILTER - Apply backtick filtering in normalizeCommentLine for all rules
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const globals_1 = require("@jest/globals");
|
|
11
|
+
const valid_annotation_format_internal_1 = require("../../src/rules/helpers/valid-annotation-format-internal");
|
|
12
|
+
(0, globals_1.describe)("normalizeCommentLine inline code filtering (Story 024.0-DEV-IGNORE-INLINE-CODE-REFS)", () => {
|
|
13
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE] ignores backtick-wrapped @story in line without real annotations", () => {
|
|
14
|
+
const raw = "This rule uses `@story` and other tags";
|
|
15
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
16
|
+
(0, globals_1.expect)(normalized).toBe("This rule uses and other tags");
|
|
17
|
+
(0, globals_1.expect)(normalized).not.toMatch(/@story|@req|@supports/);
|
|
18
|
+
});
|
|
19
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE] ignores backtick-wrapped @req in line without real annotations", () => {
|
|
20
|
+
const raw = "Legacy pattern `@req` should not be treated as annotation";
|
|
21
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
22
|
+
(0, globals_1.expect)(normalized).toBe("Legacy pattern should not be treated as annotation");
|
|
23
|
+
(0, globals_1.expect)(normalized).not.toMatch(/@story|@req|@supports/);
|
|
24
|
+
});
|
|
25
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE][REQ-PRESERVE-BOUNDARIES] preserves spacing when removing backtick segments", () => {
|
|
26
|
+
const raw = "`@story` + `@req` docs";
|
|
27
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
28
|
+
(0, globals_1.expect)(normalized).toBe(" + docs");
|
|
29
|
+
(0, globals_1.expect)(normalized).not.toMatch(/@story|@req|@supports/);
|
|
30
|
+
});
|
|
31
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE] still detects real @story annotation outside backticks", () => {
|
|
32
|
+
const raw = "using `@supports` and real @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md";
|
|
33
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
34
|
+
(0, globals_1.expect)(normalized).toBe("@story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md");
|
|
35
|
+
});
|
|
36
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE][REQ-PRESERVE-BOUNDARIES] handles multiple backtick segments on one line", () => {
|
|
37
|
+
const raw = "first `@story` and second `@req` markers";
|
|
38
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
39
|
+
(0, globals_1.expect)(normalized).toBe("first and second markers");
|
|
40
|
+
(0, globals_1.expect)(normalized).not.toMatch(/@story|@req|@supports/);
|
|
41
|
+
});
|
|
42
|
+
(0, globals_1.it)("[REQ-IGNORE-INLINE-CODE] leaves lines without backticks unchanged apart from existing normalization", () => {
|
|
43
|
+
const raw = " * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md";
|
|
44
|
+
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(raw);
|
|
45
|
+
(0, globals_1.expect)(normalized).toBe("@story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Unit tests for CatchClause insert position calculation.
|
|
5
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
6
|
+
* @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
|
|
7
|
+
* @supports docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md REQ-PRETTIER-AUTOFIX
|
|
8
|
+
*/
|
|
9
|
+
const branch_annotation_helpers_1 = require("../../src/utils/branch-annotation-helpers");
|
|
10
|
+
describe("CatchClause insert position (Story 025.0-DEV-CATCH-ANNOTATION-POSITION)", () => {
|
|
11
|
+
it("[REQ-PRETTIER-AUTOFIX] inserts annotations at the first statement inside the catch body", () => {
|
|
12
|
+
const lines = [
|
|
13
|
+
"try {",
|
|
14
|
+
" doSomething();",
|
|
15
|
+
"}",
|
|
16
|
+
"catch (error) {",
|
|
17
|
+
" handleError(error);",
|
|
18
|
+
"}",
|
|
19
|
+
];
|
|
20
|
+
const fixer = {
|
|
21
|
+
insertTextBeforeRange: jest.fn((r, t) => ({ r, t })),
|
|
22
|
+
};
|
|
23
|
+
const context = {
|
|
24
|
+
getSourceCode() {
|
|
25
|
+
return {
|
|
26
|
+
lines,
|
|
27
|
+
getCommentsBefore() {
|
|
28
|
+
return [];
|
|
29
|
+
},
|
|
30
|
+
getIndexFromLoc({ line, column }) {
|
|
31
|
+
// simple line/column to index mapping for the test: assume each line ends with "\n"
|
|
32
|
+
const prefix = lines.slice(0, line - 1).join("\n");
|
|
33
|
+
return prefix.length + (line > 1 ? 1 : 0) + column;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
report({ fix }) {
|
|
38
|
+
// immediately invoke the fixer to exercise the insert position
|
|
39
|
+
if (typeof fix === "function") {
|
|
40
|
+
fix(fixer);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
const node = {
|
|
45
|
+
type: "CatchClause",
|
|
46
|
+
loc: { start: { line: 4 } },
|
|
47
|
+
body: {
|
|
48
|
+
type: "BlockStatement",
|
|
49
|
+
loc: { start: { line: 4 } },
|
|
50
|
+
body: [
|
|
51
|
+
{
|
|
52
|
+
type: "ExpressionStatement",
|
|
53
|
+
loc: { start: { line: 5 } },
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const storyFixCountRef = { count: 0 };
|
|
59
|
+
(0, branch_annotation_helpers_1.reportMissingAnnotations)(context, node, storyFixCountRef);
|
|
60
|
+
expect(fixer.insertTextBeforeRange).toHaveBeenCalledTimes(1);
|
|
61
|
+
const [range, text] = fixer.insertTextBeforeRange.mock.calls[0];
|
|
62
|
+
// ensure we are inserting before the first statement in the catch body (line 5)
|
|
63
|
+
const expectedIndex = context.getSourceCode().getIndexFromLoc({ line: 5, column: 0 });
|
|
64
|
+
expect(range).toEqual([expectedIndex, expectedIndex]);
|
|
65
|
+
// and that the inserted text is prefixed with the inner indentation from line 5
|
|
66
|
+
expect(text.startsWith(" ")).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const branch_annotation_helpers_1 = require("../../src/utils/branch-annotation-helpers");
|
|
4
|
+
function createMockSourceCode(options) {
|
|
5
|
+
const { lines = [], commentsBefore = [], commentsInside = [] } = options;
|
|
6
|
+
return {
|
|
7
|
+
lines,
|
|
8
|
+
getCommentsBefore() {
|
|
9
|
+
return commentsBefore;
|
|
10
|
+
},
|
|
11
|
+
getCommentsInside(node) {
|
|
12
|
+
// exercise the code path that passes node.body into getCommentsInside
|
|
13
|
+
if (node && node.type === "BlockStatement") {
|
|
14
|
+
return commentsInside;
|
|
15
|
+
}
|
|
16
|
+
return [];
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
describe("gatherBranchCommentText CatchClause behavior (Story 025.0-DEV-CATCH-ANNOTATION-POSITION)", () => {
|
|
21
|
+
it("[REQ-DUAL-POSITION-DETECTION] prefers before-catch annotations when present", () => {
|
|
22
|
+
const sourceCode = createMockSourceCode({
|
|
23
|
+
commentsBefore: [
|
|
24
|
+
{ value: "@story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md" },
|
|
25
|
+
{ value: "@req REQ-BRANCH-DETECTION" },
|
|
26
|
+
],
|
|
27
|
+
commentsInside: [
|
|
28
|
+
{ value: "@story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md" },
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
const node = {
|
|
32
|
+
type: "CatchClause",
|
|
33
|
+
loc: { start: { line: 5 } },
|
|
34
|
+
body: { type: "BlockStatement" },
|
|
35
|
+
};
|
|
36
|
+
const text = (0, branch_annotation_helpers_1.gatherBranchCommentText)(sourceCode, node);
|
|
37
|
+
expect(text).toContain("@story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md");
|
|
38
|
+
expect(text).toContain("@req REQ-BRANCH-DETECTION");
|
|
39
|
+
});
|
|
40
|
+
it("[REQ-FALLBACK-LOGIC] falls back to inside-catch annotations when before-catch is missing", () => {
|
|
41
|
+
const sourceCode = createMockSourceCode({
|
|
42
|
+
commentsBefore: [],
|
|
43
|
+
commentsInside: [
|
|
44
|
+
{ value: "@story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md" },
|
|
45
|
+
{ value: "@req REQ-CATCH-PATH" },
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
const node = {
|
|
49
|
+
type: "CatchClause",
|
|
50
|
+
loc: { start: { line: 10 } },
|
|
51
|
+
body: { type: "BlockStatement" },
|
|
52
|
+
};
|
|
53
|
+
const text = (0, branch_annotation_helpers_1.gatherBranchCommentText)(sourceCode, node);
|
|
54
|
+
expect(text).toContain("@story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md");
|
|
55
|
+
expect(text).toContain("@req REQ-CATCH-PATH");
|
|
56
|
+
});
|
|
57
|
+
it("[REQ-FALLBACK-LOGIC] returns before-catch text when getCommentsInside is not available", () => {
|
|
58
|
+
const lines = [
|
|
59
|
+
"try {",
|
|
60
|
+
" doSomething();",
|
|
61
|
+
"}",
|
|
62
|
+
"catch (error) {",
|
|
63
|
+
" // body",
|
|
64
|
+
"}",
|
|
65
|
+
];
|
|
66
|
+
const sourceCode = {
|
|
67
|
+
lines,
|
|
68
|
+
getCommentsBefore() {
|
|
69
|
+
return [
|
|
70
|
+
{ value: "@story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md" },
|
|
71
|
+
{ value: "@req REQ-BRANCH-DETECTION" },
|
|
72
|
+
];
|
|
73
|
+
},
|
|
74
|
+
// intentionally omit getCommentsInside so that the CatchClause path
|
|
75
|
+
// falls back to the before-catch comments.
|
|
76
|
+
};
|
|
77
|
+
const node = {
|
|
78
|
+
type: "CatchClause",
|
|
79
|
+
loc: { start: { line: 4 } },
|
|
80
|
+
body: { type: "BlockStatement" },
|
|
81
|
+
};
|
|
82
|
+
const text = (0, branch_annotation_helpers_1.gatherBranchCommentText)(sourceCode, node);
|
|
83
|
+
expect(text).toContain("@story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md");
|
|
84
|
+
expect(text).toContain("@req REQ-BRANCH-DETECTION");
|
|
85
|
+
});
|
|
86
|
+
it("[REQ-FALLBACK-LOGIC] collects inside-catch comments using line-based fallback when getCommentsInside is unavailable", () => {
|
|
87
|
+
const lines = [
|
|
88
|
+
"try {",
|
|
89
|
+
" doSomething();",
|
|
90
|
+
"} catch (error) {",
|
|
91
|
+
" // @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md",
|
|
92
|
+
" // @req REQ-CATCH-LINE-FALLBACK",
|
|
93
|
+
" handleError(error);",
|
|
94
|
+
"}",
|
|
95
|
+
];
|
|
96
|
+
const sourceCode = {
|
|
97
|
+
lines,
|
|
98
|
+
getCommentsBefore() {
|
|
99
|
+
return [];
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const node = {
|
|
103
|
+
type: "CatchClause",
|
|
104
|
+
loc: { start: { line: 3 } },
|
|
105
|
+
body: {
|
|
106
|
+
type: "BlockStatement",
|
|
107
|
+
loc: { start: { line: 3 }, end: { line: 7 } },
|
|
108
|
+
body: [],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
const text = (0, branch_annotation_helpers_1.gatherBranchCommentText)(sourceCode, node);
|
|
112
|
+
expect(text).toContain("@story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md");
|
|
113
|
+
expect(text).toContain("@req REQ-CATCH-LINE-FALLBACK");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|