eslint-plugin-traceability 1.8.0 → 1.8.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/CHANGELOG.md +5 -5
- package/README.md +28 -29
- package/SECURITY.md +135 -0
- package/lib/src/index.d.ts +6 -35
- package/lib/src/index.js +8 -5
- package/lib/src/maintenance/cli.js +12 -16
- package/lib/src/maintenance/detect.js +28 -1
- package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
- package/lib/src/rules/helpers/require-story-io.js +13 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
- package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
- package/lib/src/rules/prefer-implements-annotation.js +21 -21
- package/lib/src/rules/valid-annotation-format.js +50 -24
- package/lib/src/rules/valid-req-reference.js +9 -9
- package/lib/src/utils/annotation-checker.js +3 -1
- package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
- package/lib/src/utils/reqAnnotationDetection.js +28 -28
- package/lib/tests/config/flat-config-presets-integration.test.d.ts +1 -0
- package/lib/tests/config/flat-config-presets-integration.test.js +75 -0
- package/lib/tests/maintenance/batch.test.js +11 -11
- package/lib/tests/maintenance/cli.test.js +34 -27
- package/lib/tests/maintenance/report.test.js +7 -7
- package/lib/tests/plugin-default-export-and-configs.test.js +0 -2
- package/lib/tests/rules/prefer-implements-annotation.test.js +48 -15
- package/lib/tests/rules/require-branch-annotation.test.js +15 -36
- package/lib/tests/rules/require-req-annotation.test.js +31 -104
- package/lib/tests/rules/require-story-annotation.test.js +3 -3
- package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
- package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
- package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
- package/lib/tests/rules/valid-annotation-format.test.js +23 -23
- package/lib/tests/rules/valid-req-reference.test.js +9 -9
- package/lib/tests/rules/valid-story-reference.test.js +4 -43
- package/lib/tests/utils/annotation-checker.test.js +2 -6
- package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
- package/lib/tests/utils/fsTestHelpers.js +26 -0
- package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
- package/lib/tests/utils/ioTestHelpers.js +24 -0
- package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
- package/lib/tests/utils/temp-dir-helpers.js +61 -0
- package/package.json +8 -7
- package/user-docs/api-reference.md +37 -20
- package/user-docs/eslint-9-setup-guide.md +89 -6
- package/user-docs/migration-guide.md +37 -21
- package/docs/ci-cd-pipeline.md +0 -224
- package/docs/cli-integration.md +0 -22
- package/docs/code-quality-refactor-opportunities-2025-12-03.md +0 -78
- package/docs/config-presets.md +0 -38
- package/docs/conventional-commits-guide.md +0 -185
- package/docs/custom-rules-development-guide.md +0 -659
- package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +0 -26
- package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +0 -111
- package/docs/decisions/002-jest-for-eslint-testing.accepted.md +0 -137
- package/docs/decisions/003-code-quality-ratcheting-plan.md +0 -48
- package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +0 -196
- package/docs/decisions/005-github-actions-validation-tooling.accepted.md +0 -144
- package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +0 -227
- package/docs/decisions/007-github-releases-over-changelog.accepted.md +0 -216
- package/docs/decisions/008-ci-audit-flags.accepted.md +0 -60
- package/docs/decisions/009-security-focused-lint-rules.accepted.md +0 -64
- package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +0 -184
- package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +0 -190
- package/docs/decisions/adr-accept-dev-dep-risk-glob.md +0 -40
- package/docs/decisions/adr-commit-branch-tests.md +0 -54
- package/docs/decisions/adr-maintenance-cli-interface.md +0 -140
- package/docs/decisions/adr-pre-push-parity.md +0 -112
- package/docs/decisions/code-quality-ratcheting-plan.md +0 -53
- package/docs/dependency-health.md +0 -238
- package/docs/eslint-9-setup-guide.md +0 -517
- package/docs/eslint-plugin-development-guide.md +0 -487
- package/docs/functionality-coverage-2025-12-03.md +0 -250
- package/docs/jest-testing-guide.md +0 -100
- package/docs/rules/prefer-implements-annotation.md +0 -219
- package/docs/rules/require-branch-annotation.md +0 -71
- package/docs/rules/require-req-annotation.md +0 -203
- package/docs/rules/require-story-annotation.md +0 -159
- package/docs/rules/valid-annotation-format.md +0 -418
- package/docs/rules/valid-req-reference.md +0 -153
- package/docs/rules/valid-story-reference.md +0 -120
- package/docs/security-incidents/2025-11-17-glob-cli-incident.md +0 -45
- package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +0 -45
- package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +0 -93
- package/docs/security-incidents/2025-11-18-tar-race-condition.md +0 -43
- package/docs/security-incidents/2025-12-03-dependency-health-review.md +0 -58
- package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +0 -104
- package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +0 -37
- package/docs/security-incidents/dependency-override-rationale.md +0 -57
- package/docs/security-incidents/dev-deps-high.json +0 -116
- package/docs/security-incidents/handling-procedure.md +0 -54
- package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +0 -92
- package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +0 -82
- package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +0 -112
- package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +0 -153
- package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +0 -138
- package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +0 -144
- package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +0 -163
- package/docs/stories/008.0-DEV-AUTO-FIX.story.md +0 -150
- package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +0 -117
- package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +0 -124
- package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +0 -149
- package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +0 -216
- package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +0 -236
- package/docs/stories/developer-story.map.md +0 -120
- package/docs/ts-jest-presets-guide.md +0 -548
|
@@ -13,13 +13,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
* @req REQ-MAINT-SAFE - Clear exit codes and non-destructive dry-run
|
|
14
14
|
*/
|
|
15
15
|
const fs_1 = __importDefault(require("fs"));
|
|
16
|
-
const os_1 = __importDefault(require("os"));
|
|
17
16
|
const path_1 = __importDefault(require("path"));
|
|
18
17
|
const cli_1 = require("../../src/maintenance/cli");
|
|
19
|
-
|
|
20
|
-
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "maint-cli-"));
|
|
21
|
-
return tmpDir;
|
|
22
|
-
}
|
|
18
|
+
const temp_dir_helpers_1 = require("../utils/temp-dir-helpers");
|
|
23
19
|
describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
24
20
|
let originalCwd;
|
|
25
21
|
beforeAll(() => {
|
|
@@ -29,7 +25,8 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
29
25
|
process.chdir(originalCwd);
|
|
30
26
|
});
|
|
31
27
|
it("[REQ-MAINT-DETECT] detect exits with code 0 and message when no stale annotations", () => {
|
|
32
|
-
const
|
|
28
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
29
|
+
const dir = temp.dir;
|
|
33
30
|
process.chdir(dir);
|
|
34
31
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
35
32
|
const code = (0, cli_1.runMaintenanceCli)(["node", "traceability-maint", "detect"]);
|
|
@@ -39,11 +36,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
39
36
|
}
|
|
40
37
|
finally {
|
|
41
38
|
logSpy.mockRestore();
|
|
42
|
-
|
|
39
|
+
temp.cleanup();
|
|
43
40
|
}
|
|
44
41
|
});
|
|
45
42
|
it("[REQ-MAINT-VERIFY] verify exits with code 0 when annotations valid", () => {
|
|
46
|
-
const
|
|
43
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
44
|
+
const dir = temp.dir;
|
|
47
45
|
process.chdir(dir);
|
|
48
46
|
const tsContent = `/**\n * @story my-story.story.md\n */`;
|
|
49
47
|
fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
|
|
@@ -56,11 +54,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
56
54
|
}
|
|
57
55
|
finally {
|
|
58
56
|
logSpy.mockRestore();
|
|
59
|
-
|
|
57
|
+
temp.cleanup();
|
|
60
58
|
}
|
|
61
59
|
});
|
|
62
60
|
it("[REQ-MAINT-REPORT] report prints human-readable summary and exits 0", () => {
|
|
63
|
-
const
|
|
61
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
62
|
+
const dir = temp.dir;
|
|
64
63
|
process.chdir(dir);
|
|
65
64
|
const tsContent = `/**\n * @story missing.story.md\n */`;
|
|
66
65
|
fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
|
|
@@ -74,11 +73,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
74
73
|
}
|
|
75
74
|
finally {
|
|
76
75
|
logSpy.mockRestore();
|
|
77
|
-
|
|
76
|
+
temp.cleanup();
|
|
78
77
|
}
|
|
79
78
|
});
|
|
80
79
|
it("[REQ-MAINT-UPDATE] update performs replacements and exits 0", () => {
|
|
81
|
-
const
|
|
80
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
81
|
+
const dir = temp.dir;
|
|
82
82
|
process.chdir(dir);
|
|
83
83
|
const tsContent = `/**\n * @story old.path.md\n */`;
|
|
84
84
|
fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
|
|
@@ -99,11 +99,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
99
99
|
}
|
|
100
100
|
finally {
|
|
101
101
|
logSpy.mockRestore();
|
|
102
|
-
|
|
102
|
+
temp.cleanup();
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
105
|
it("[REQ-MAINT-SAFE] update requires --from and --to and exits 2 when missing", () => {
|
|
106
|
-
const
|
|
106
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
107
|
+
const dir = temp.dir;
|
|
107
108
|
process.chdir(dir);
|
|
108
109
|
const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
109
110
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
@@ -116,11 +117,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
116
117
|
finally {
|
|
117
118
|
errorSpy.mockRestore();
|
|
118
119
|
logSpy.mockRestore();
|
|
119
|
-
|
|
120
|
+
temp.cleanup();
|
|
120
121
|
}
|
|
121
122
|
});
|
|
122
123
|
it("[REQ-MAINT-SAFE] dry-run does not modify files and exits 0", () => {
|
|
123
|
-
const
|
|
124
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
125
|
+
const dir = temp.dir;
|
|
124
126
|
process.chdir(dir);
|
|
125
127
|
const tsContent = `/**\n * @story old.path.md\n */`;
|
|
126
128
|
fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
|
|
@@ -142,11 +144,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
142
144
|
}
|
|
143
145
|
finally {
|
|
144
146
|
logSpy.mockRestore();
|
|
145
|
-
|
|
147
|
+
temp.cleanup();
|
|
146
148
|
}
|
|
147
149
|
});
|
|
148
150
|
it("[REQ-MAINT-SAFE] report exits 2 and prints error on invalid --format value", () => {
|
|
149
|
-
const
|
|
151
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
152
|
+
const dir = temp.dir;
|
|
150
153
|
process.chdir(dir);
|
|
151
154
|
const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
152
155
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
@@ -167,11 +170,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
167
170
|
finally {
|
|
168
171
|
errorSpy.mockRestore();
|
|
169
172
|
logSpy.mockRestore();
|
|
170
|
-
|
|
173
|
+
temp.cleanup();
|
|
171
174
|
}
|
|
172
175
|
});
|
|
173
176
|
it("[REQ-MAINT-DETECT] detect supports --json output", () => {
|
|
174
|
-
const
|
|
177
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
178
|
+
const dir = temp.dir;
|
|
175
179
|
process.chdir(dir);
|
|
176
180
|
const tsContent = `/**\n * @story stale.story.md\n */`;
|
|
177
181
|
fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
|
|
@@ -191,11 +195,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
191
195
|
}
|
|
192
196
|
finally {
|
|
193
197
|
logSpy.mockRestore();
|
|
194
|
-
|
|
198
|
+
temp.cleanup();
|
|
195
199
|
}
|
|
196
200
|
});
|
|
197
201
|
it("[REQ-MAINT-DETECT] detect with non-existent --root exits 0 and reports no stale annotations", () => {
|
|
198
|
-
const
|
|
202
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
203
|
+
const dir = temp.dir;
|
|
199
204
|
process.chdir(dir);
|
|
200
205
|
const missingRoot = path_1.default.join(dir, "missing-root");
|
|
201
206
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
@@ -212,11 +217,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
212
217
|
}
|
|
213
218
|
finally {
|
|
214
219
|
logSpy.mockRestore();
|
|
215
|
-
|
|
220
|
+
temp.cleanup();
|
|
216
221
|
}
|
|
217
222
|
});
|
|
218
223
|
it("[REQ-MAINT-SAFE] prints help and exits 0 when no subcommand is provided", () => {
|
|
219
|
-
const
|
|
224
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
225
|
+
const dir = temp.dir;
|
|
220
226
|
process.chdir(dir);
|
|
221
227
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
222
228
|
const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
@@ -231,11 +237,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
231
237
|
finally {
|
|
232
238
|
logSpy.mockRestore();
|
|
233
239
|
errorSpy.mockRestore();
|
|
234
|
-
|
|
240
|
+
temp.cleanup();
|
|
235
241
|
}
|
|
236
242
|
});
|
|
237
243
|
it("[REQ-MAINT-SAFE] detect catches filesystem permission errors and exits 2 with prefixed error message", () => {
|
|
238
|
-
const
|
|
244
|
+
const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
|
|
245
|
+
const dir = temp.dir;
|
|
239
246
|
process.chdir(dir);
|
|
240
247
|
const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
241
248
|
const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
@@ -255,7 +262,7 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
|
255
262
|
statSpy.mockRestore();
|
|
256
263
|
errorSpy.mockRestore();
|
|
257
264
|
logSpy.mockRestore();
|
|
258
|
-
|
|
265
|
+
temp.cleanup();
|
|
259
266
|
}
|
|
260
267
|
});
|
|
261
268
|
});
|
|
@@ -41,27 +41,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
41
|
*/
|
|
42
42
|
const fs = __importStar(require("fs"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
|
-
const
|
|
44
|
+
const temp_dir_helpers_1 = require("../utils/temp-dir-helpers");
|
|
45
45
|
const report_1 = require("../../src/maintenance/report");
|
|
46
46
|
describe("generateMaintenanceReport (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
|
|
47
|
-
let
|
|
47
|
+
let temp;
|
|
48
48
|
beforeAll(() => {
|
|
49
|
-
|
|
49
|
+
temp = (0, temp_dir_helpers_1.createTempDir)("report-test-");
|
|
50
50
|
});
|
|
51
51
|
afterAll(() => {
|
|
52
|
-
|
|
52
|
+
temp.cleanup();
|
|
53
53
|
});
|
|
54
54
|
it("[REQ-MAINT-REPORT] should return empty string when no operations", () => {
|
|
55
|
-
const report = (0, report_1.generateMaintenanceReport)(
|
|
55
|
+
const report = (0, report_1.generateMaintenanceReport)(temp.dir);
|
|
56
56
|
expect(report).toBe("");
|
|
57
57
|
});
|
|
58
58
|
it("[REQ-MAINT-REPORT] should report stale story annotation", () => {
|
|
59
|
-
const filePath = path.join(
|
|
59
|
+
const filePath = path.join(temp.dir, "stub.md");
|
|
60
60
|
const content = `/**
|
|
61
61
|
* @story non-existent.story.md
|
|
62
62
|
*/`;
|
|
63
63
|
fs.writeFileSync(filePath, content);
|
|
64
|
-
const report = (0, report_1.generateMaintenanceReport)(
|
|
64
|
+
const report = (0, report_1.generateMaintenanceReport)(temp.dir);
|
|
65
65
|
expect(report).toContain("non-existent.story.md");
|
|
66
66
|
});
|
|
67
67
|
});
|
|
@@ -80,12 +80,10 @@ describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", ()
|
|
|
80
80
|
expect(recommendedRules).toHaveProperty("traceability/require-branch-annotation", "error");
|
|
81
81
|
expect(recommendedRules).toHaveProperty("traceability/valid-story-reference", "error");
|
|
82
82
|
expect(recommendedRules).toHaveProperty("traceability/valid-req-reference", "error");
|
|
83
|
-
expect(recommendedRules).toHaveProperty("traceability/prefer-implements-annotation", "warn");
|
|
84
83
|
});
|
|
85
84
|
it("[REQ-ERROR-SEVERITY] configs.strict uses same severity mapping as recommended", () => {
|
|
86
85
|
const strictRules = index_1.configs.strict[0].rules;
|
|
87
86
|
const recommendedRules = index_1.configs.recommended[0].rules;
|
|
88
87
|
expect(strictRules).toEqual(recommendedRules);
|
|
89
|
-
expect(strictRules).toHaveProperty("traceability/prefer-implements-annotation", "warn");
|
|
90
88
|
});
|
|
91
89
|
});
|
|
@@ -4,20 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
/**
|
|
7
|
-
* Tests for: docs/stories/010.3-DEV-MIGRATE-TO-
|
|
8
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
9
|
-
* @req REQ-OPTIONAL-WARNING - Verify rule emits recommendations for legacy @story/@req usage
|
|
10
|
-
* @req REQ-MULTI-STORY-DETECT - Verify rule detects multi-story and mixed-annotation patterns
|
|
7
|
+
* Tests for: docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
8
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
9
|
+
* @req REQ-OPTIONAL-WARNING - Verify rule emits recommendations for legacy @story/@req usage and migration to @supports
|
|
10
|
+
* @req REQ-MULTI-STORY-DETECT - Verify rule detects multi-story and mixed-annotation patterns involving @supports
|
|
11
11
|
* @req REQ-CONFIG-SEVERITY - Verify rule is disabled by default and can be enabled as warn/error
|
|
12
12
|
*/
|
|
13
13
|
const eslint_1 = require("eslint");
|
|
14
14
|
const prefer_implements_annotation_1 = __importDefault(require("../../src/rules/prefer-implements-annotation"));
|
|
15
|
+
const src_1 = require("../../src");
|
|
15
16
|
const ruleTester = new eslint_1.RuleTester({
|
|
16
17
|
languageOptions: {
|
|
17
18
|
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
|
|
18
19
|
},
|
|
19
20
|
});
|
|
20
|
-
describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-
|
|
21
|
+
describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-SUPPORTS)", () => {
|
|
21
22
|
ruleTester.run("prefer-implements-annotation", prefer_implements_annotation_1.default, {
|
|
22
23
|
valid: [
|
|
23
24
|
{
|
|
@@ -29,25 +30,25 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
|
|
|
29
30
|
code: `/**\n * @req REQ-ONLY\n */\nfunction onlyReq() {}`,
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
|
-
name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @
|
|
33
|
-
code: `/**\n * @
|
|
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() {}`,
|
|
34
35
|
},
|
|
35
36
|
],
|
|
36
37
|
invalid: [
|
|
37
38
|
{
|
|
38
39
|
name: "[REQ-OPTIONAL-WARNING] single-story @story + @req block triggers preferImplements message",
|
|
39
40
|
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
|
|
40
|
-
output: `/**\n * @
|
|
41
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
|
|
41
42
|
errors: [{ messageId: "preferImplements" }],
|
|
42
43
|
},
|
|
43
44
|
{
|
|
44
|
-
name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @
|
|
45
|
-
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n * @
|
|
45
|
+
name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @supports triggers cannotAutoFix",
|
|
46
|
+
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() {}`,
|
|
46
47
|
errors: [
|
|
47
48
|
{
|
|
48
49
|
messageId: "cannotAutoFix",
|
|
49
50
|
data: {
|
|
50
|
-
reason: "comment mixes @story/@req with existing @
|
|
51
|
+
reason: "comment mixes @story/@req with existing @supports annotations",
|
|
51
52
|
},
|
|
52
53
|
},
|
|
53
54
|
],
|
|
@@ -58,15 +59,15 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
|
|
|
58
59
|
errors: [{ messageId: "multiStoryDetected" }],
|
|
59
60
|
},
|
|
60
61
|
{
|
|
61
|
-
name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @
|
|
62
|
+
name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @supports line",
|
|
62
63
|
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
|
|
63
|
-
output: `/**\n * @
|
|
64
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
|
|
64
65
|
errors: [{ messageId: "preferImplements" }],
|
|
65
66
|
},
|
|
66
67
|
{
|
|
67
|
-
name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @
|
|
68
|
+
name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @supports line containing all REQ IDs",
|
|
68
69
|
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() {}`,
|
|
69
|
-
output: `/**\n * @
|
|
70
|
+
output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ONE REQ-TWO REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
|
|
70
71
|
errors: [{ messageId: "preferImplements" }],
|
|
71
72
|
},
|
|
72
73
|
{
|
|
@@ -82,3 +83,35 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
|
|
|
82
83
|
],
|
|
83
84
|
});
|
|
84
85
|
});
|
|
86
|
+
describe("prefer-implements-annotation configuration severity (REQ-CONFIG-SEVERITY)", () => {
|
|
87
|
+
test("rule is disabled by default in recommended and strict presets (not present in preset rule maps)", () => {
|
|
88
|
+
const recommended = src_1.configs.recommended;
|
|
89
|
+
expect(Array.isArray(recommended)).toBe(true);
|
|
90
|
+
const firstConfig = recommended[0];
|
|
91
|
+
expect(firstConfig).toBeDefined();
|
|
92
|
+
const rules = firstConfig.rules || {};
|
|
93
|
+
expect(rules["traceability/prefer-implements-annotation"]).toBeUndefined();
|
|
94
|
+
const strict = src_1.configs.strict;
|
|
95
|
+
expect(Array.isArray(strict)).toBe(true);
|
|
96
|
+
const strictFirstConfig = strict[0];
|
|
97
|
+
expect(strictFirstConfig).toBeDefined();
|
|
98
|
+
const strictRules = strictFirstConfig.rules || {};
|
|
99
|
+
expect(strictRules["traceability/prefer-implements-annotation"]).toBeUndefined();
|
|
100
|
+
});
|
|
101
|
+
test("rule can be configured with severity 'warn' or 'error' in flat config", () => {
|
|
102
|
+
const flatWarnConfig = {
|
|
103
|
+
files: ["**/*.ts"],
|
|
104
|
+
rules: {
|
|
105
|
+
"traceability/prefer-implements-annotation": "warn",
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
expect(flatWarnConfig.rules["traceability/prefer-implements-annotation"]).toBe("warn");
|
|
109
|
+
const flatErrorConfig = {
|
|
110
|
+
files: ["**/*.ts"],
|
|
111
|
+
rules: {
|
|
112
|
+
"traceability/prefer-implements-annotation": "error",
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
expect(flatErrorConfig.rules["traceability/prefer-implements-annotation"]).toBe("error");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -17,6 +17,10 @@ const require_branch_annotation_1 = __importDefault(require("../../src/rules/req
|
|
|
17
17
|
const ruleTester = new eslint_1.RuleTester({
|
|
18
18
|
languageOptions: { parserOptions: { ecmaVersion: 2020 } },
|
|
19
19
|
});
|
|
20
|
+
const makeMissingAnnotationErrors = (...missing) => missing.map((item) => ({
|
|
21
|
+
messageId: "missingAnnotation",
|
|
22
|
+
data: { missing: item },
|
|
23
|
+
}));
|
|
20
24
|
const runRule = (tests) => ruleTester.run("require-branch-annotation", require_branch_annotation_1.default, tests);
|
|
21
25
|
describe("Require Branch Annotation Rule (Story 004.0-DEV-BRANCH-ANNOTATIONS)", () => {
|
|
22
26
|
runRule({
|
|
@@ -144,10 +148,7 @@ if (condition) {}`,
|
|
|
144
148
|
code: `if (condition) {}`,
|
|
145
149
|
output: `// @story <story-file>.story.md
|
|
146
150
|
if (condition) {}`,
|
|
147
|
-
errors:
|
|
148
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
149
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
150
|
-
],
|
|
151
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
151
152
|
},
|
|
152
153
|
{
|
|
153
154
|
name: "[REQ-BRANCH-DETECTION] missing @req on for loop when only story present",
|
|
@@ -156,7 +157,7 @@ for (let i = 0; i < 5; i++) {}`,
|
|
|
156
157
|
output: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
157
158
|
// @req <REQ-ID>
|
|
158
159
|
for (let i = 0; i < 5; i++) {}`,
|
|
159
|
-
errors:
|
|
160
|
+
errors: makeMissingAnnotationErrors("@req"),
|
|
160
161
|
},
|
|
161
162
|
{
|
|
162
163
|
name: "[REQ-BRANCH-DETECTION] missing @story on while loop when only req present",
|
|
@@ -165,9 +166,7 @@ while (true) {}`,
|
|
|
165
166
|
output: `// @req REQ-BRANCH-DETECTION
|
|
166
167
|
// @story <story-file>.story.md
|
|
167
168
|
while (true) {}`,
|
|
168
|
-
errors:
|
|
169
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
170
|
-
],
|
|
169
|
+
errors: makeMissingAnnotationErrors("@story"),
|
|
171
170
|
},
|
|
172
171
|
{
|
|
173
172
|
name: "[REQ-BRANCH-DETECTION] missing annotations on switch-case",
|
|
@@ -180,10 +179,7 @@ while (true) {}`,
|
|
|
180
179
|
case 'a':
|
|
181
180
|
break;
|
|
182
181
|
}`,
|
|
183
|
-
errors:
|
|
184
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
185
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
186
|
-
],
|
|
182
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
187
183
|
},
|
|
188
184
|
{
|
|
189
185
|
name: "[REQ-BRANCH-DETECTION] missing annotations on do-while loop",
|
|
@@ -194,10 +190,7 @@ while (true) {}`,
|
|
|
194
190
|
do {
|
|
195
191
|
action();
|
|
196
192
|
} while (condition);`,
|
|
197
|
-
errors:
|
|
198
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
199
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
200
|
-
],
|
|
193
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
201
194
|
},
|
|
202
195
|
{
|
|
203
196
|
name: "[REQ-BRANCH-DETECTION] missing annotations on for-of loop",
|
|
@@ -208,10 +201,7 @@ do {
|
|
|
208
201
|
for (const item of items) {
|
|
209
202
|
process(item);
|
|
210
203
|
}`,
|
|
211
|
-
errors:
|
|
212
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
213
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
214
|
-
],
|
|
204
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
215
205
|
},
|
|
216
206
|
{
|
|
217
207
|
name: "[REQ-BRANCH-DETECTION] missing annotations on for-in loop",
|
|
@@ -222,10 +212,7 @@ for (const item of items) {
|
|
|
222
212
|
for (const key in object) {
|
|
223
213
|
console.log(key);
|
|
224
214
|
}`,
|
|
225
|
-
errors:
|
|
226
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
227
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
228
|
-
],
|
|
215
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
229
216
|
},
|
|
230
217
|
{
|
|
231
218
|
name: "[REQ-BRANCH-DETECTION] missing annotations on try-catch blocks",
|
|
@@ -241,10 +228,8 @@ try {
|
|
|
241
228
|
handleError(error);
|
|
242
229
|
}`,
|
|
243
230
|
errors: [
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
247
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
231
|
+
...makeMissingAnnotationErrors("@story", "@req"),
|
|
232
|
+
...makeMissingAnnotationErrors("@story", "@req"),
|
|
248
233
|
],
|
|
249
234
|
},
|
|
250
235
|
{
|
|
@@ -260,10 +245,7 @@ try {
|
|
|
260
245
|
case 'a':
|
|
261
246
|
break;
|
|
262
247
|
}`,
|
|
263
|
-
errors:
|
|
264
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
265
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
266
|
-
],
|
|
248
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
267
249
|
},
|
|
268
250
|
{
|
|
269
251
|
name: "[REQ-CONFIGURABLE-SCOPE] missing annotations on configured branch type ForStatement",
|
|
@@ -271,10 +253,7 @@ try {
|
|
|
271
253
|
options: [{ branchTypes: ["ForStatement"] }],
|
|
272
254
|
output: `// @story <story-file>.story.md
|
|
273
255
|
for (let i = 0; i < 3; i++) {}`,
|
|
274
|
-
errors:
|
|
275
|
-
{ messageId: "missingAnnotation", data: { missing: "@story" } },
|
|
276
|
-
{ messageId: "missingAnnotation", data: { missing: "@req" } },
|
|
277
|
-
],
|
|
256
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
278
257
|
},
|
|
279
258
|
],
|
|
280
259
|
});
|