eslint-plugin-traceability 1.4.8 → 1.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/rules/valid-story-reference.js +76 -8
- package/lib/src/utils/storyReferenceUtils.d.ts +68 -0
- package/lib/src/utils/storyReferenceUtils.js +107 -13
- package/lib/tests/rules/{require-story-core.branches.test.js → require-story-core-edgecases.test.js} +2 -2
- package/lib/tests/rules/{require-story-io.branches.test.d.ts → require-story-helpers-edgecases.test.d.ts} +1 -1
- package/lib/tests/rules/{require-story-helpers.branches.test.js → require-story-helpers-edgecases.test.js} +2 -2
- package/lib/tests/rules/{require-story-helpers.branches.test.d.ts → require-story-io-behavior.test.d.ts} +1 -1
- package/lib/tests/rules/{require-story-io.branches.test.js → require-story-io-behavior.test.js} +2 -2
- package/lib/tests/rules/{require-story-visitors.branches.test.d.ts → require-story-visitors-edgecases.test.d.ts} +1 -1
- package/lib/tests/rules/{require-story-visitors.branches.test.js → require-story-visitors-edgecases.test.js} +2 -2
- package/lib/tests/rules/valid-story-reference.test.js +79 -0
- package/package.json +2 -1
- /package/lib/tests/rules/{require-story-core.branches.test.d.ts → require-story-core-edgecases.test.d.ts} +0 -0
|
@@ -35,12 +35,63 @@ function validateStoryPath(opts) {
|
|
|
35
35
|
requireExt,
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Report any problems related to the existence or accessibility of the
|
|
40
|
+
* referenced story file. Filesystem and I/O errors are surfaced with a
|
|
41
|
+
* dedicated diagnostic that differentiates them from missing files.
|
|
42
|
+
*
|
|
43
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
44
|
+
* @req REQ-FILE-EXISTENCE - Ensure referenced files exist
|
|
45
|
+
* @req REQ-ERROR-HANDLING - Differentiate missing files from filesystem errors
|
|
46
|
+
*/
|
|
47
|
+
function reportExistenceProblems(opts) {
|
|
48
|
+
const { storyPath, commentNode, context, cwd, storyDirs } = opts;
|
|
49
|
+
const result = (0, storyReferenceUtils_1.normalizeStoryPath)(storyPath, cwd, storyDirs);
|
|
50
|
+
const existenceResult = result.existence;
|
|
51
|
+
if (!existenceResult || existenceResult.status === "exists") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (existenceResult.status === "missing") {
|
|
55
|
+
context.report({
|
|
56
|
+
node: commentNode,
|
|
57
|
+
messageId: "fileMissing",
|
|
58
|
+
data: { path: storyPath },
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (existenceResult.status === "fs-error") {
|
|
63
|
+
const rawError = existenceResult.error;
|
|
64
|
+
let errorMessage;
|
|
65
|
+
if (rawError == null) {
|
|
66
|
+
errorMessage = "Unknown filesystem error";
|
|
67
|
+
}
|
|
68
|
+
else if (rawError instanceof Error) {
|
|
69
|
+
errorMessage = rawError.message;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
errorMessage = String(rawError);
|
|
73
|
+
}
|
|
74
|
+
context.report({
|
|
75
|
+
node: commentNode,
|
|
76
|
+
messageId: "fileAccessError",
|
|
77
|
+
data: {
|
|
78
|
+
path: storyPath,
|
|
79
|
+
error: errorMessage,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
38
84
|
/**
|
|
39
85
|
* Process and validate the story path for security, extension, and existence.
|
|
86
|
+
* Filesystem and I/O errors are handled inside the underlying utilities
|
|
87
|
+
* (e.g. storyExists) and surfaced as missing-file or filesystem-error
|
|
88
|
+
* diagnostics where appropriate.
|
|
89
|
+
*
|
|
40
90
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
41
91
|
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
42
92
|
* @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
43
93
|
* @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
94
|
+
* @req REQ-ERROR-HANDLING - Delegate filesystem and I/O error handling to utilities and differentiate error types
|
|
44
95
|
*/
|
|
45
96
|
function processStoryPath(opts) {
|
|
46
97
|
const { storyPath, commentNode, context, cwd, storyDirs, allowAbsolute, requireExt, } = opts;
|
|
@@ -76,14 +127,22 @@ function processStoryPath(opts) {
|
|
|
76
127
|
});
|
|
77
128
|
return;
|
|
78
129
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Existence check:
|
|
132
|
+
* - Distinguish between missing files and filesystem errors.
|
|
133
|
+
* - Filesystem and I/O errors are surfaced with a dedicated diagnostic.
|
|
134
|
+
*
|
|
135
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
136
|
+
* @req REQ-FILE-EXISTENCE - Ensure referenced files exist
|
|
137
|
+
* @req REQ-ERROR-HANDLING - Differentiate missing files from filesystem errors
|
|
138
|
+
*/
|
|
139
|
+
reportExistenceProblems({
|
|
140
|
+
storyPath,
|
|
141
|
+
commentNode,
|
|
142
|
+
context,
|
|
143
|
+
cwd,
|
|
144
|
+
storyDirs,
|
|
145
|
+
});
|
|
87
146
|
}
|
|
88
147
|
/**
|
|
89
148
|
* Handle a single comment node by processing its lines.
|
|
@@ -124,6 +183,11 @@ exports.default = {
|
|
|
124
183
|
fileMissing: "Story file '{{path}}' not found",
|
|
125
184
|
invalidExtension: "Invalid story file extension for '{{path}}', expected '.story.md'",
|
|
126
185
|
invalidPath: "Invalid story path '{{path}}'",
|
|
186
|
+
/**
|
|
187
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
188
|
+
* @req REQ-ERROR-HANDLING - Provide clear diagnostics for filesystem errors
|
|
189
|
+
*/
|
|
190
|
+
fileAccessError: "Could not validate story file '{{path}}' due to a filesystem error: {{error}}. Please check file existence and permissions.",
|
|
127
191
|
},
|
|
128
192
|
schema: [
|
|
129
193
|
{
|
|
@@ -146,10 +210,14 @@ exports.default = {
|
|
|
146
210
|
return {
|
|
147
211
|
/**
|
|
148
212
|
* Program-level handler: iterate comments and validate @story annotations.
|
|
213
|
+
* Filesystem and I/O errors are handled by underlying utilities and
|
|
214
|
+
* surfaced as missing-file or filesystem-error diagnostics where appropriate.
|
|
215
|
+
*
|
|
149
216
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
150
217
|
* @req REQ-ANNOTATION-VALIDATION - Discover and dispatch @story annotations for validation
|
|
151
218
|
* @req REQ-FILE-EXISTENCE - Ensure referenced files exist
|
|
152
219
|
* @req REQ-PATH-RESOLUTION - Resolve using cwd and configured story directories
|
|
220
|
+
* @req REQ-ERROR-HANDLING - Delegate filesystem and I/O error handling to utilities
|
|
153
221
|
*/
|
|
154
222
|
Program() {
|
|
155
223
|
const comments = context.getSourceCode().getAllComments() || [];
|
|
@@ -1,19 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes the possible existence states for a checked path.
|
|
3
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
4
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
5
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
6
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
7
|
+
*/
|
|
8
|
+
export type StoryExistenceStatus = "exists" | "missing" | "fs-error";
|
|
9
|
+
/**
|
|
10
|
+
* Result of checking a single candidate path.
|
|
11
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
12
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
13
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
14
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
15
|
+
*/
|
|
16
|
+
export interface StoryPathCheckResult {
|
|
17
|
+
path: string;
|
|
18
|
+
status: StoryExistenceStatus;
|
|
19
|
+
error?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Aggregated existence result across multiple candidate paths.
|
|
23
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
24
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
25
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
26
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
27
|
+
*/
|
|
28
|
+
export interface StoryExistenceResult {
|
|
29
|
+
candidates: string[];
|
|
30
|
+
status: StoryExistenceStatus;
|
|
31
|
+
matchedPath?: string;
|
|
32
|
+
error?: unknown;
|
|
33
|
+
}
|
|
1
34
|
/**
|
|
2
35
|
* Build candidate file paths for a given story path.
|
|
3
36
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
4
37
|
* @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
5
38
|
*/
|
|
6
39
|
export declare function buildStoryCandidates(storyPath: string, cwd: string, storyDirs: string[]): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Aggregate existence status across multiple candidate paths.
|
|
42
|
+
* Returns the first successful match (`exists`), or, if none exist,
|
|
43
|
+
* the first filesystem error encountered. If there are only missing
|
|
44
|
+
* candidates, returns a missing status.
|
|
45
|
+
*
|
|
46
|
+
* This function never throws and is the preferred richer API for callers
|
|
47
|
+
* that need more than a boolean.
|
|
48
|
+
*
|
|
49
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
50
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
51
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
52
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
53
|
+
*/
|
|
54
|
+
export declare function getStoryExistence(candidates: string[]): StoryExistenceResult;
|
|
55
|
+
/**
|
|
56
|
+
* Check if any of the provided file paths exist.
|
|
57
|
+
* Handles filesystem errors (e.g., EACCES) gracefully by treating them as non-existent
|
|
58
|
+
* and never throwing.
|
|
59
|
+
*
|
|
60
|
+
* Internally delegates to the richer status-based helper while preserving the
|
|
61
|
+
* original boolean-only API for backwards compatibility.
|
|
62
|
+
*
|
|
63
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
64
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
65
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
66
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
67
|
+
*/
|
|
7
68
|
export declare function storyExists(paths: string[]): boolean;
|
|
8
69
|
/**
|
|
9
70
|
* Normalize a story path to candidate absolute paths and check existence.
|
|
71
|
+
* Filesystem errors are handled via the status-aware helper, which suppresses
|
|
72
|
+
* exceptions and treats such cases as non-existent for the boolean flag while
|
|
73
|
+
* still surfacing error details in the status field.
|
|
74
|
+
*
|
|
10
75
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
11
76
|
* @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
12
77
|
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
78
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
79
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
13
80
|
*/
|
|
14
81
|
export declare function normalizeStoryPath(storyPath: string, cwd: string, storyDirs: string[]): {
|
|
15
82
|
candidates: string[];
|
|
16
83
|
exists: boolean;
|
|
84
|
+
existence: StoryExistenceResult;
|
|
17
85
|
};
|
|
18
86
|
/**
|
|
19
87
|
* Check if the provided path is absolute.
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.buildStoryCandidates = buildStoryCandidates;
|
|
7
|
+
exports.getStoryExistence = getStoryExistence;
|
|
7
8
|
exports.storyExists = storyExists;
|
|
8
9
|
exports.normalizeStoryPath = normalizeStoryPath;
|
|
9
10
|
exports.isAbsolutePath = isAbsolutePath;
|
|
@@ -17,6 +18,7 @@ exports.isUnsafeStoryPath = isUnsafeStoryPath;
|
|
|
17
18
|
* @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
18
19
|
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
19
20
|
* @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
21
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
20
22
|
*/
|
|
21
23
|
const fs_1 = __importDefault(require("fs"));
|
|
22
24
|
const path_1 = __importDefault(require("path"));
|
|
@@ -39,34 +41,126 @@ function buildStoryCandidates(storyPath, cwd, storyDirs) {
|
|
|
39
41
|
return candidates;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
|
-
*
|
|
44
|
+
* Cache of filesystem existence checks keyed by absolute path.
|
|
43
45
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
44
46
|
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
47
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
48
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
45
49
|
*/
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const fileExistStatusCache = new Map();
|
|
51
|
+
/**
|
|
52
|
+
* Check a single candidate path, with caching and robust error handling.
|
|
53
|
+
* All filesystem interactions are wrapped in try/catch and never throw.
|
|
54
|
+
*
|
|
55
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
56
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
57
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
58
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
59
|
+
*/
|
|
60
|
+
function checkSingleCandidate(candidate) {
|
|
61
|
+
const cached = fileExistStatusCache.get(candidate);
|
|
62
|
+
if (cached) {
|
|
63
|
+
return cached;
|
|
64
|
+
}
|
|
65
|
+
let result;
|
|
66
|
+
try {
|
|
67
|
+
const exists = fs_1.default.existsSync(candidate);
|
|
68
|
+
if (!exists) {
|
|
69
|
+
result = { path: candidate, status: "missing" };
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const stat = fs_1.default.statSync(candidate);
|
|
73
|
+
if (stat.isFile()) {
|
|
74
|
+
result = { path: candidate, status: "exists" };
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Path exists but is not a file; treat as missing for story purposes.
|
|
78
|
+
result = { path: candidate, status: "missing" };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// Any filesystem error is captured and surfaced as fs-error.
|
|
84
|
+
result = { path: candidate, status: "fs-error", error };
|
|
85
|
+
}
|
|
86
|
+
fileExistStatusCache.set(candidate, result);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Aggregate existence status across multiple candidate paths.
|
|
91
|
+
* Returns the first successful match (`exists`), or, if none exist,
|
|
92
|
+
* the first filesystem error encountered. If there are only missing
|
|
93
|
+
* candidates, returns a missing status.
|
|
94
|
+
*
|
|
95
|
+
* This function never throws and is the preferred richer API for callers
|
|
96
|
+
* that need more than a boolean.
|
|
97
|
+
*
|
|
98
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
99
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
100
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
101
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
102
|
+
*/
|
|
103
|
+
function getStoryExistence(candidates) {
|
|
104
|
+
let firstFsError;
|
|
105
|
+
for (const candidate of candidates) {
|
|
106
|
+
const res = checkSingleCandidate(candidate);
|
|
107
|
+
if (res.status === "exists") {
|
|
108
|
+
return {
|
|
109
|
+
candidates,
|
|
110
|
+
status: "exists",
|
|
111
|
+
matchedPath: res.path,
|
|
112
|
+
};
|
|
53
113
|
}
|
|
54
|
-
if (
|
|
55
|
-
|
|
114
|
+
if (res.status === "fs-error" && !firstFsError) {
|
|
115
|
+
firstFsError = res;
|
|
56
116
|
}
|
|
57
117
|
}
|
|
58
|
-
|
|
118
|
+
if (firstFsError) {
|
|
119
|
+
return {
|
|
120
|
+
candidates,
|
|
121
|
+
status: "fs-error",
|
|
122
|
+
error: firstFsError.error,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
candidates,
|
|
127
|
+
status: "missing",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if any of the provided file paths exist.
|
|
132
|
+
* Handles filesystem errors (e.g., EACCES) gracefully by treating them as non-existent
|
|
133
|
+
* and never throwing.
|
|
134
|
+
*
|
|
135
|
+
* Internally delegates to the richer status-based helper while preserving the
|
|
136
|
+
* original boolean-only API for backwards compatibility.
|
|
137
|
+
*
|
|
138
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
139
|
+
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
140
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
141
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
142
|
+
*/
|
|
143
|
+
function storyExists(paths) {
|
|
144
|
+
const result = getStoryExistence(paths);
|
|
145
|
+
return result.status === "exists";
|
|
59
146
|
}
|
|
60
147
|
/**
|
|
61
148
|
* Normalize a story path to candidate absolute paths and check existence.
|
|
149
|
+
* Filesystem errors are handled via the status-aware helper, which suppresses
|
|
150
|
+
* exceptions and treats such cases as non-existent for the boolean flag while
|
|
151
|
+
* still surfacing error details in the status field.
|
|
152
|
+
*
|
|
62
153
|
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
63
154
|
* @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
64
155
|
* @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
|
|
156
|
+
* @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
|
|
157
|
+
* @req REQ-PERFORMANCE-OPTIMIZATION - Cache filesystem checks to avoid redundant work
|
|
65
158
|
*/
|
|
66
159
|
function normalizeStoryPath(storyPath, cwd, storyDirs) {
|
|
67
160
|
const candidates = buildStoryCandidates(storyPath, cwd, storyDirs);
|
|
68
|
-
const
|
|
69
|
-
|
|
161
|
+
const existence = getStoryExistence(candidates);
|
|
162
|
+
const exists = existence.status === "exists";
|
|
163
|
+
return { candidates, exists, existence };
|
|
70
164
|
}
|
|
71
165
|
/**
|
|
72
166
|
* Check if the provided path is absolute.
|
package/lib/tests/rules/{require-story-core.branches.test.js → require-story-core-edgecases.test.js}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Edge-case tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
5
5
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
6
6
|
* @req REQ-AUTOFIX - Cover additional branch cases in require-story-core (addStoryFixer/reportMissing)
|
|
7
7
|
*/
|
|
8
8
|
const require_story_core_1 = require("../../src/rules/helpers/require-story-core");
|
|
9
9
|
const require_story_helpers_1 = require("../../src/rules/helpers/require-story-helpers");
|
|
10
|
-
describe("Require Story Core (Story 003.0)", () => {
|
|
10
|
+
describe("Require Story Core - edge cases (Story 003.0)", () => {
|
|
11
11
|
test("createAddStoryFix falls back to 0 when target is falsy", () => {
|
|
12
12
|
const fixer = {
|
|
13
13
|
insertTextBeforeRange: jest.fn((r, t) => ({ r, t })),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
3
3
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-
|
|
4
|
+
* @req REQ-HELPERS-EDGE-CASES - Edge-case behavior tests for helpers in require-story-helpers.ts
|
|
5
5
|
*/
|
|
6
6
|
export {};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
4
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
5
|
-
* @req REQ-
|
|
5
|
+
* @req REQ-HELPERS-EDGE-CASES - Edge-case behavior tests for helpers in require-story-helpers.ts
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const require_story_helpers_1 = require("../../src/rules/helpers/require-story-helpers");
|
|
9
|
-
describe("Require Story Helpers -
|
|
9
|
+
describe("Require Story Helpers - edge cases (Story 003.0)", () => {
|
|
10
10
|
test("jsdocHasStory returns false when JSDoc exists but value is not a string", () => {
|
|
11
11
|
const fakeSource = { getJSDocComment: () => ({ value: 123 }) };
|
|
12
12
|
const res = (0, require_story_helpers_1.jsdocHasStory)(fakeSource, {});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
3
3
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-
|
|
4
|
+
* @req REQ-IO-BEHAVIOR-EDGE-CASES - Edge-case behavior tests for IO helpers in require-story-io.ts
|
|
5
5
|
*/
|
|
6
6
|
export {};
|
package/lib/tests/rules/{require-story-io.branches.test.js → require-story-io-behavior.test.js}
RENAMED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
4
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
5
|
-
* @req REQ-
|
|
5
|
+
* @req REQ-IO-BEHAVIOR-EDGE-CASES - Edge-case behavior tests for IO helpers in require-story-io.ts
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const require_story_io_1 = require("../../src/rules/helpers/require-story-io");
|
|
9
|
-
describe("Require Story IO helpers -
|
|
9
|
+
describe("Require Story IO helpers - additional behavior (Story 003.0)", () => {
|
|
10
10
|
test("parentChainHasStory returns false when sourceCode.getCommentsBefore is not a function", () => {
|
|
11
11
|
const fakeSource = {}; // no getCommentsBefore function
|
|
12
12
|
const node = { parent: { parent: null } };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
3
3
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-
|
|
4
|
+
* @req REQ-VISITORS-BEHAVIOR - Behavior tests for visitors in require-story-visitors.ts
|
|
5
5
|
*/
|
|
6
6
|
export {};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
4
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
5
|
-
* @req REQ-
|
|
5
|
+
* @req REQ-VISITORS-BEHAVIOR - Behavior tests for visitors in require-story-visitors.ts
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const require_story_visitors_1 = require("../../src/rules/helpers/require-story-visitors");
|
|
9
|
-
describe("Require Story Visitors -
|
|
9
|
+
describe("Require Story Visitors - behavior (Story 003.0)", () => {
|
|
10
10
|
test("build visitors returns handlers for FunctionDeclaration and ArrowFunctionExpression", () => {
|
|
11
11
|
const fakeContext = { getFilename: () => "file.ts" };
|
|
12
12
|
const fakeSource = { getText: () => "" };
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
*/
|
|
11
11
|
const eslint_1 = require("eslint");
|
|
12
12
|
const valid_story_reference_1 = __importDefault(require("../../src/rules/valid-story-reference"));
|
|
13
|
+
const storyReferenceUtils_1 = require("../../src/utils/storyReferenceUtils");
|
|
13
14
|
const ruleTester = new eslint_1.RuleTester({
|
|
14
15
|
languageOptions: { parserOptions: { ecmaVersion: 2020 } },
|
|
15
16
|
});
|
|
@@ -67,3 +68,81 @@ describe("Valid Story Reference Rule (Story 006.0-DEV-FILE-VALIDATION)", () => {
|
|
|
67
68
|
],
|
|
68
69
|
});
|
|
69
70
|
});
|
|
71
|
+
/**
|
|
72
|
+
* Helper to run the valid-story-reference rule against a single source string
|
|
73
|
+
* and collect reported diagnostics.
|
|
74
|
+
*
|
|
75
|
+
* @req REQ-ERROR-HANDLING - Used to verify fileAccessError reporting behavior
|
|
76
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
77
|
+
*/
|
|
78
|
+
function runRuleOnCode(code) {
|
|
79
|
+
const messages = [];
|
|
80
|
+
const context = {
|
|
81
|
+
report: (descriptor) => {
|
|
82
|
+
messages.push(descriptor);
|
|
83
|
+
},
|
|
84
|
+
getSourceCode: () => ({
|
|
85
|
+
text: code,
|
|
86
|
+
getAllComments: () => [
|
|
87
|
+
{
|
|
88
|
+
type: "Line",
|
|
89
|
+
value: code.replace(/^\/\//, "").trim(),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
}),
|
|
93
|
+
options: [],
|
|
94
|
+
parserOptions: { ecmaVersion: 2020 },
|
|
95
|
+
};
|
|
96
|
+
const listeners = valid_story_reference_1.default.create(context);
|
|
97
|
+
if (typeof listeners.Program === "function") {
|
|
98
|
+
listeners.Program({});
|
|
99
|
+
}
|
|
100
|
+
return messages;
|
|
101
|
+
}
|
|
102
|
+
describe("Valid Story Reference Rule Error Handling (Story 006.0-DEV-FILE-VALIDATION)", () => {
|
|
103
|
+
/**
|
|
104
|
+
* @req REQ-ERROR-HANDLING - Verify storyExists swallows fs errors and returns false
|
|
105
|
+
* instead of throwing when filesystem operations fail.
|
|
106
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
107
|
+
*/
|
|
108
|
+
const fs = require("fs");
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
jest.restoreAllMocks();
|
|
111
|
+
});
|
|
112
|
+
it("[REQ-ERROR-HANDLING] storyExists returns false when fs throws", () => {
|
|
113
|
+
jest.spyOn(fs, "existsSync").mockImplementation(() => {
|
|
114
|
+
const err = new Error("EACCES: permission denied");
|
|
115
|
+
err.code = "EACCES";
|
|
116
|
+
throw err;
|
|
117
|
+
});
|
|
118
|
+
jest.spyOn(fs, "statSync").mockImplementation(() => {
|
|
119
|
+
const err = new Error("EACCES: permission denied");
|
|
120
|
+
err.code = "EACCES";
|
|
121
|
+
throw err;
|
|
122
|
+
});
|
|
123
|
+
expect(() => (0, storyReferenceUtils_1.storyExists)(["docs/stories/permission-denied.story.md"])).not.toThrow();
|
|
124
|
+
expect((0, storyReferenceUtils_1.storyExists)(["docs/stories/permission-denied.story.md"])).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
/**
|
|
127
|
+
* @req REQ-ERROR-HANDLING - Verify rule reports fileAccessError when filesystem operations fail
|
|
128
|
+
* instead of treating it as a missing file.
|
|
129
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
130
|
+
*/
|
|
131
|
+
it("[REQ-ERROR-HANDLING] rule reports fileAccessError when fs throws", () => {
|
|
132
|
+
const accessError = new Error("EACCES: permission denied while accessing");
|
|
133
|
+
accessError.code = "EACCES";
|
|
134
|
+
jest.spyOn(fs, "existsSync").mockImplementation(() => {
|
|
135
|
+
throw accessError;
|
|
136
|
+
});
|
|
137
|
+
jest.spyOn(fs, "statSync").mockImplementation(() => {
|
|
138
|
+
throw accessError;
|
|
139
|
+
});
|
|
140
|
+
const diagnostics = runRuleOnCode(`// @story docs/stories/fs-error.story.md`);
|
|
141
|
+
expect(diagnostics.length).toBeGreaterThan(0);
|
|
142
|
+
const fileAccessDiagnostics = diagnostics.filter((d) => d.messageId === "fileAccessError");
|
|
143
|
+
expect(fileAccessDiagnostics.length).toBeGreaterThan(0);
|
|
144
|
+
const errorData = fileAccessDiagnostics[0].data;
|
|
145
|
+
expect(errorData).toBeDefined();
|
|
146
|
+
expect(String(errorData.error)).toMatch(/EACCES/i);
|
|
147
|
+
});
|
|
148
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.10",
|
|
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",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"lint": "eslint --config eslint.config.js \"src/**/*.{js,ts}\" \"tests/**/*.{js,ts}\" --max-warnings=0",
|
|
23
23
|
"test": "jest --ci --bail",
|
|
24
24
|
"ci-verify": "npm run type-check && npm run lint && npm run format:check && npm run duplication && npm run check:traceability && npm test && npm run audit:ci && npm run safety:deps",
|
|
25
|
+
"ci-verify:full": "npm run check:traceability && npm run safety:deps && npm run audit:ci && npm run build && npm run type-check && npm run lint-plugin-check && npm run lint -- --max-warnings=0 && npm run duplication && npm run test -- --coverage && npm run format:check && npm audit --production --audit-level=high && npm run audit:dev-high",
|
|
25
26
|
"ci-verify:fast": "npm run type-check && npm run check:traceability && npm run duplication && jest --ci --bail --passWithNoTests --testPathPatterns 'tests/(unit|fast)'",
|
|
26
27
|
"format": "prettier --write .",
|
|
27
28
|
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
File without changes
|