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.
Files changed (58) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +3 -3
  3. package/lib/src/index.d.ts +10 -5
  4. package/lib/src/index.js +71 -6
  5. package/lib/src/maintenance/commands.js +2 -3
  6. package/lib/src/maintenance/update.js +1 -14
  7. package/lib/src/rules/helpers/require-story-core.d.ts +12 -4
  8. package/lib/src/rules/helpers/require-story-core.js +59 -30
  9. package/lib/src/rules/helpers/require-story-helpers.d.ts +7 -41
  10. package/lib/src/rules/helpers/require-story-helpers.js +13 -70
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +12 -13
  12. package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -16
  13. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +29 -3
  14. package/lib/src/rules/helpers/valid-annotation-format-validators.js +29 -3
  15. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +3 -3
  16. package/lib/src/rules/helpers/valid-annotation-utils.js +10 -10
  17. package/lib/src/rules/helpers/valid-req-reference-helpers.d.ts +11 -0
  18. package/lib/src/rules/helpers/valid-req-reference-helpers.js +362 -0
  19. package/lib/src/rules/prefer-implements-annotation.js +7 -7
  20. package/lib/src/rules/require-story-annotation.d.ts +2 -0
  21. package/lib/src/rules/require-story-annotation.js +1 -1
  22. package/lib/src/rules/valid-req-reference.d.ts +4 -0
  23. package/lib/src/rules/valid-req-reference.js +5 -349
  24. package/lib/src/rules/valid-story-reference.d.ts +1 -1
  25. package/lib/src/rules/valid-story-reference.js +17 -10
  26. package/lib/src/utils/branch-annotation-helpers.d.ts +2 -2
  27. package/lib/src/utils/branch-annotation-helpers.js +96 -17
  28. package/lib/tests/cli-error-handling.test.js +1 -1
  29. package/lib/tests/config/eslint-config-validation.test.js +73 -0
  30. package/lib/tests/fixtures/stale/example.js +1 -1
  31. package/lib/tests/fixtures/update/example.js +1 -1
  32. package/lib/tests/integration/catch-annotation-prettier.integration.test.d.ts +1 -0
  33. package/lib/tests/integration/catch-annotation-prettier.integration.test.js +131 -0
  34. package/lib/tests/integration/dogfooding-validation.test.d.ts +1 -0
  35. package/lib/tests/integration/dogfooding-validation.test.js +94 -0
  36. package/lib/tests/maintenance/cli.test.js +37 -0
  37. package/lib/tests/maintenance/detect-isolated.test.js +5 -5
  38. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +18 -0
  39. package/lib/tests/perf/require-branch-annotation-large-file.test.d.ts +1 -0
  40. package/lib/tests/perf/require-branch-annotation-large-file.test.js +67 -0
  41. package/lib/tests/perf/valid-annotation-format-large-file.test.d.ts +1 -0
  42. package/lib/tests/perf/valid-annotation-format-large-file.test.js +74 -0
  43. package/lib/tests/plugin-default-export-and-configs.test.js +1 -0
  44. package/lib/tests/plugin-setup.test.js +12 -1
  45. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -70
  46. package/lib/tests/rules/require-branch-annotation.test.js +33 -1
  47. package/lib/tests/rules/valid-annotation-format-internal.test.d.ts +8 -0
  48. package/lib/tests/rules/valid-annotation-format-internal.test.js +47 -0
  49. package/lib/tests/utils/branch-annotation-catch-insert-position.test.d.ts +1 -0
  50. package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +68 -0
  51. package/lib/tests/utils/branch-annotation-catch-position.test.d.ts +1 -0
  52. package/lib/tests/utils/branch-annotation-catch-position.test.js +115 -0
  53. package/lib/tests/utils/req-annotation-detection.test.d.ts +1 -0
  54. package/lib/tests/utils/req-annotation-detection.test.js +247 -0
  55. package/package.json +4 -4
  56. package/user-docs/api-reference.md +20 -12
  57. package/user-docs/examples.md +2 -1
  58. 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 rule (Story 010.3-DEV-MIGRATE-TO-SUPPORTS)", () => {
22
- ruleTester.run("prefer-implements-annotation", prefer_implements_annotation_1.default, {
23
- valid: [
24
- {
25
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with only @story is ignored",
26
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction onlyStory() {}`,
27
- },
28
- {
29
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with only @req is ignored",
30
- code: `/**\n * @req REQ-ONLY\n */\nfunction onlyReq() {}`,
31
- },
32
- {
33
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @supports only is ignored",
34
- code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction alreadyImplements() {}`,
35
- },
36
- {
37
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @story and @supports but no @req is ignored",
38
- 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() {}`,
39
- },
40
- {
41
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @req and @supports but no @story is ignored",
42
- code: `/**\n * @req REQ-ANNOTATION-REQUIRED\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction reqAndSupportsNoStory() {}`,
43
- },
44
- ],
45
- invalid: [
46
- {
47
- name: "[REQ-OPTIONAL-WARNING] single-story @story + @req block triggers preferImplements message",
48
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
49
- output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
50
- errors: [{ messageId: "preferImplements" }],
51
- },
52
- {
53
- name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @supports triggers cannotAutoFix",
54
- 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() {}`,
55
- errors: [
56
- {
57
- messageId: "cannotAutoFix",
58
- data: {
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
- name: "[REQ-MULTI-STORY-DETECT] multiple @story paths in same block trigger multiStoryDetected",
66
- 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() {}`,
67
- errors: [{ messageId: "multiStoryDetected" }],
68
- },
69
- {
70
- name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @supports line",
71
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
72
- output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
73
- errors: [{ messageId: "preferImplements" }],
74
- },
75
- {
76
- name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @supports line containing all REQ IDs",
77
- 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() {}`,
78
- output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ONE REQ-TWO REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
79
- errors: [{ messageId: "preferImplements" }],
80
- },
81
- {
82
- name: "[REQ-AUTO-FIX] complex @req content (extra description) does not auto-fix but still warns",
83
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED must handle extra description\n */\nfunction complexReqNoAutoFix() {}`,
84
- errors: [{ messageId: "preferImplements" }],
85
- },
86
- {
87
- name: "[REQ-AUTO-FIX] complex @story content (extra description) does not auto-fix but still warns",
88
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md additional descriptive text\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction complexStoryNoAutoFix() {}`,
89
- errors: [{ messageId: "preferImplements" }],
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
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-BRANCH-DETECTION
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,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,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 {};