eslint-plugin-traceability 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -2
- package/lib/src/rules/helpers/require-story-helpers.d.ts +5 -11
- package/lib/src/rules/helpers/require-story-helpers.js +7 -56
- package/lib/src/rules/helpers/test-callback-exclusion.d.ts +39 -0
- package/lib/src/rules/helpers/test-callback-exclusion.js +109 -0
- package/lib/src/rules/require-story-annotation.js +14 -1
- package/lib/tests/rules/require-story-annotation.test.js +47 -0
- package/lib/tests/rules/require-story-helpers.test.js +261 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# [1.
|
|
1
|
+
# [1.17.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.16.1...v1.17.0) (2025-12-09)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* allow configuring additional excluded test helper callbacks ([d266197](https://github.com/voder-ai/eslint-plugin-traceability/commit/d26619721b1826fe97d01a63647f07505e35c846))
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
@@ -11,21 +11,17 @@ import type { Rule } from "eslint";
|
|
|
11
11
|
import { linesBeforeHasStory, parentChainHasStory, fallbackTextBeforeHasStory } from "./require-story-io";
|
|
12
12
|
import { getNodeName } from "./require-story-utils";
|
|
13
13
|
import { DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES, STORY_PATH } from "./require-story-core";
|
|
14
|
+
import { type CallbackExclusionOptions } from "./test-callback-exclusion";
|
|
14
15
|
/**
|
|
15
16
|
* Shared configuration helpers
|
|
16
17
|
*/
|
|
17
|
-
interface ReportOptions {
|
|
18
|
+
interface ReportOptions extends CallbackExclusionOptions {
|
|
18
19
|
annotationTemplateOverride?: string;
|
|
19
20
|
autoFixToggle?: boolean;
|
|
20
|
-
excludeTestCallbacks?: boolean;
|
|
21
21
|
}
|
|
22
22
|
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
23
|
-
declare function getAnnotationTemplate(override?: string, _options?:
|
|
24
|
-
|
|
25
|
-
}): string;
|
|
26
|
-
declare function shouldApplyAutoFix(autoFix: boolean | undefined, _options?: {
|
|
27
|
-
excludeTestCallbacks?: boolean;
|
|
28
|
-
}): boolean;
|
|
23
|
+
declare function getAnnotationTemplate(override?: string, _options?: CallbackExclusionOptions): string;
|
|
24
|
+
declare function shouldApplyAutoFix(autoFix: boolean | undefined, _options?: CallbackExclusionOptions): boolean;
|
|
29
25
|
/**
|
|
30
26
|
* Determine if a node is in an export declaration
|
|
31
27
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -70,9 +66,7 @@ declare function resolveTargetNode(sourceCode: any, node: any): any;
|
|
|
70
66
|
*/
|
|
71
67
|
declare function extractName(node: any): string;
|
|
72
68
|
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
73
|
-
declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string, options?:
|
|
74
|
-
excludeTestCallbacks?: boolean;
|
|
75
|
-
}): boolean;
|
|
69
|
+
declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string, options?: CallbackExclusionOptions): boolean;
|
|
76
70
|
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
77
71
|
declare function reportMissing(context: Rule.RuleContext, sourceCode: any, config: {
|
|
78
72
|
node: any;
|
|
@@ -23,23 +23,7 @@ const require_story_core_1 = require("./require-story-core");
|
|
|
23
23
|
Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
|
|
24
24
|
Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
|
|
25
25
|
Object.defineProperty(exports, "STORY_PATH", { enumerable: true, get: function () { return require_story_core_1.STORY_PATH; } });
|
|
26
|
-
|
|
27
|
-
* Known test framework function names and variants.
|
|
28
|
-
* Includes Jest, Mocha, Vitest and their focused/skipped/concurrent variants.
|
|
29
|
-
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
30
|
-
*/
|
|
31
|
-
const TEST_FUNCTION_NAMES = new Set([
|
|
32
|
-
"it",
|
|
33
|
-
"test",
|
|
34
|
-
"describe",
|
|
35
|
-
"fit",
|
|
36
|
-
"xit",
|
|
37
|
-
"ftest",
|
|
38
|
-
"xtest",
|
|
39
|
-
"fdescribe",
|
|
40
|
-
"xdescribe",
|
|
41
|
-
]);
|
|
42
|
-
const TEST_FUNCTION_CONCURRENT_PROP = "concurrent";
|
|
26
|
+
const test_callback_exclusion_1 = require("./test-callback-exclusion");
|
|
43
27
|
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
44
28
|
function getAnnotationTemplate(override, _options) {
|
|
45
29
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
@@ -59,9 +43,13 @@ function shouldApplyAutoFix(autoFix, _options) {
|
|
|
59
43
|
*/
|
|
60
44
|
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
61
45
|
function buildTemplateConfig(options) {
|
|
62
|
-
const effectiveTemplate = getAnnotationTemplate(options?.annotationTemplateOverride, {
|
|
46
|
+
const effectiveTemplate = getAnnotationTemplate(options?.annotationTemplateOverride, {
|
|
47
|
+
excludeTestCallbacks: options?.excludeTestCallbacks,
|
|
48
|
+
additionalTestHelperNames: options?.additionalTestHelperNames,
|
|
49
|
+
});
|
|
63
50
|
const allowFix = shouldApplyAutoFix(options?.autoFixToggle, {
|
|
64
51
|
excludeTestCallbacks: options?.excludeTestCallbacks,
|
|
52
|
+
additionalTestHelperNames: options?.additionalTestHelperNames,
|
|
65
53
|
});
|
|
66
54
|
return { effectiveTemplate, allowFix };
|
|
67
55
|
}
|
|
@@ -108,43 +96,6 @@ function isEffectivelyAnonymousFunction(node) {
|
|
|
108
96
|
}
|
|
109
97
|
return true;
|
|
110
98
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Determine whether a node represents a callback passed to a known test
|
|
113
|
-
* framework function (Jest, Mocha, Vitest, etc).
|
|
114
|
-
*
|
|
115
|
-
* Supports:
|
|
116
|
-
* - it(), test(), describe(), fit(), xit(), ftest(), xtest(), fdescribe(), xdescribe()
|
|
117
|
-
* - their .concurrent variants (e.g., it.concurrent(), test.concurrent())
|
|
118
|
-
*
|
|
119
|
-
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
120
|
-
*/
|
|
121
|
-
function isTestFrameworkCallback(node, options) {
|
|
122
|
-
if (options?.excludeTestCallbacks === false) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
if (!node || node.type !== "ArrowFunctionExpression") {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
const parent = node.parent;
|
|
129
|
-
if (!parent || parent.type !== "CallExpression") {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
const callee = parent.callee;
|
|
133
|
-
if (callee.type === "Identifier") {
|
|
134
|
-
return TEST_FUNCTION_NAMES.has(callee.name);
|
|
135
|
-
}
|
|
136
|
-
if (callee.type === "MemberExpression" &&
|
|
137
|
-
!callee.computed &&
|
|
138
|
-
callee.property &&
|
|
139
|
-
callee.property.type === "Identifier" &&
|
|
140
|
-
callee.property.name === TEST_FUNCTION_CONCURRENT_PROP) {
|
|
141
|
-
const obj = callee.object;
|
|
142
|
-
if (obj && obj.type === "Identifier") {
|
|
143
|
-
return TEST_FUNCTION_NAMES.has(obj.name);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
99
|
/**
|
|
149
100
|
* Determine whether a function node is required to carry its own annotation
|
|
150
101
|
* according to Story 004.0-DEV-BRANCH-ANNOTATIONS rules.
|
|
@@ -159,7 +110,7 @@ function isTestFrameworkCallback(node, options) {
|
|
|
159
110
|
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED REQ-NESTED-FUNCTION-INHERITANCE
|
|
160
111
|
*/
|
|
161
112
|
function requiresOwnFunctionAnnotation(node, options) {
|
|
162
|
-
if (isTestFrameworkCallback(node, options)) {
|
|
113
|
+
if ((0, test_callback_exclusion_1.isTestFrameworkCallback)(node, options)) {
|
|
163
114
|
return false;
|
|
164
115
|
}
|
|
165
116
|
// Anonymous arrow functions used as callbacks are excluded from function-level
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for determining whether a function-like node should be
|
|
3
|
+
* treated as a test framework callback that may be excluded from
|
|
4
|
+
* function-level annotation requirements.
|
|
5
|
+
*
|
|
6
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
7
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
8
|
+
* @story docs/stories/013-exclude-test-framework-callbacks.proposed.md
|
|
9
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION - Provide reusable test callback exclusion logic
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Options controlling how test callbacks are treated by the helpers.
|
|
13
|
+
*
|
|
14
|
+
* - excludeTestCallbacks: when false, no callbacks are excluded and all
|
|
15
|
+
* function-like nodes are treated as regular functions.
|
|
16
|
+
* - additionalTestHelperNames: optional array of additional helper names that
|
|
17
|
+
* should be treated like built-in test functions when excludeTestCallbacks
|
|
18
|
+
* is enabled.
|
|
19
|
+
*/
|
|
20
|
+
interface CallbackExclusionOptions {
|
|
21
|
+
excludeTestCallbacks?: boolean;
|
|
22
|
+
additionalTestHelperNames?: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Determine whether a node represents a callback passed to a known test
|
|
26
|
+
* framework function (Jest, Mocha, Vitest, etc).
|
|
27
|
+
*
|
|
28
|
+
* Supports:
|
|
29
|
+
* - it(), test(), describe(), suite(), context(), specify()
|
|
30
|
+
* - lifecycle hooks: before(), after(), beforeEach(), afterEach(), beforeAll(), afterAll()
|
|
31
|
+
* - focused variants: fit(), ftest(), fdescribe(), fsuite()
|
|
32
|
+
* - skipped variants and helpers: xit(), xtest(), xdescribe(), xsuite()
|
|
33
|
+
* - their .concurrent variants (e.g., it.concurrent(), test.concurrent())
|
|
34
|
+
*
|
|
35
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
36
|
+
*/
|
|
37
|
+
declare function isTestFrameworkCallback(node: any, options?: CallbackExclusionOptions): boolean;
|
|
38
|
+
export type { CallbackExclusionOptions };
|
|
39
|
+
export { isTestFrameworkCallback };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared helpers for determining whether a function-like node should be
|
|
4
|
+
* treated as a test framework callback that may be excluded from
|
|
5
|
+
* function-level annotation requirements.
|
|
6
|
+
*
|
|
7
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
8
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
9
|
+
* @story docs/stories/013-exclude-test-framework-callbacks.proposed.md
|
|
10
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION - Provide reusable test callback exclusion logic
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isTestFrameworkCallback = isTestFrameworkCallback;
|
|
14
|
+
/**
|
|
15
|
+
* Known test framework function names and variants.
|
|
16
|
+
* Includes Jest, Mocha, Vitest and their focused/skipped/concurrent variants.
|
|
17
|
+
*
|
|
18
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
19
|
+
*/
|
|
20
|
+
const TEST_FUNCTION_NAMES = new Set([
|
|
21
|
+
// Core test/describe-style functions (Jest, Mocha, Vitest share many of these)
|
|
22
|
+
"it",
|
|
23
|
+
"test",
|
|
24
|
+
"describe",
|
|
25
|
+
"suite",
|
|
26
|
+
// Focused variants
|
|
27
|
+
"fit",
|
|
28
|
+
"ftest",
|
|
29
|
+
"fdescribe",
|
|
30
|
+
"fsuite",
|
|
31
|
+
// Skipped variants
|
|
32
|
+
"xit",
|
|
33
|
+
"xtest",
|
|
34
|
+
"xdescribe",
|
|
35
|
+
"xsuite",
|
|
36
|
+
// Additional common aliases
|
|
37
|
+
"context",
|
|
38
|
+
"specify",
|
|
39
|
+
"before",
|
|
40
|
+
"after",
|
|
41
|
+
"beforeEach",
|
|
42
|
+
"afterEach",
|
|
43
|
+
"beforeAll",
|
|
44
|
+
"afterAll",
|
|
45
|
+
]);
|
|
46
|
+
const TEST_FUNCTION_CONCURRENT_PROP = "concurrent";
|
|
47
|
+
/**
|
|
48
|
+
* Determine if a function name should be treated as a recognized test helper,
|
|
49
|
+
* including core test functions and any configured additional helper names.
|
|
50
|
+
*
|
|
51
|
+
* Vitest's `bench` is explicitly never treated as an excluded test callback,
|
|
52
|
+
* even if it appears in additionalTestHelperNames, to preserve the story
|
|
53
|
+
* requirement that bench callbacks always require annotations.
|
|
54
|
+
*
|
|
55
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
56
|
+
*/
|
|
57
|
+
function isRecognizedTestHelperName(name, options) {
|
|
58
|
+
if (name === "bench") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (TEST_FUNCTION_NAMES.has(name)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (options?.additionalTestHelperNames &&
|
|
65
|
+
Array.isArray(options.additionalTestHelperNames)) {
|
|
66
|
+
return options.additionalTestHelperNames.includes(name);
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Determine whether a node represents a callback passed to a known test
|
|
72
|
+
* framework function (Jest, Mocha, Vitest, etc).
|
|
73
|
+
*
|
|
74
|
+
* Supports:
|
|
75
|
+
* - it(), test(), describe(), suite(), context(), specify()
|
|
76
|
+
* - lifecycle hooks: before(), after(), beforeEach(), afterEach(), beforeAll(), afterAll()
|
|
77
|
+
* - focused variants: fit(), ftest(), fdescribe(), fsuite()
|
|
78
|
+
* - skipped variants and helpers: xit(), xtest(), xdescribe(), xsuite()
|
|
79
|
+
* - their .concurrent variants (e.g., it.concurrent(), test.concurrent())
|
|
80
|
+
*
|
|
81
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION
|
|
82
|
+
*/
|
|
83
|
+
function isTestFrameworkCallback(node, options) {
|
|
84
|
+
if (options?.excludeTestCallbacks === false) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (!node || node.type !== "ArrowFunctionExpression") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const parent = node.parent;
|
|
91
|
+
if (!parent || parent.type !== "CallExpression") {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const callee = parent.callee;
|
|
95
|
+
if (callee.type === "Identifier") {
|
|
96
|
+
return isRecognizedTestHelperName(callee.name, options);
|
|
97
|
+
}
|
|
98
|
+
if (callee.type === "MemberExpression" &&
|
|
99
|
+
!callee.computed &&
|
|
100
|
+
callee.property &&
|
|
101
|
+
callee.property.type === "Identifier" &&
|
|
102
|
+
callee.property.name === TEST_FUNCTION_CONCURRENT_PROP) {
|
|
103
|
+
const obj = callee.object;
|
|
104
|
+
if (obj && obj.type === "Identifier") {
|
|
105
|
+
return isRecognizedTestHelperName(obj.name, options);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
@@ -49,6 +49,11 @@ const rule = {
|
|
|
49
49
|
methodAnnotationTemplate: { type: "string" },
|
|
50
50
|
autoFix: { type: "boolean" },
|
|
51
51
|
excludeTestCallbacks: { type: "boolean" },
|
|
52
|
+
additionalTestHelperNames: {
|
|
53
|
+
type: "array",
|
|
54
|
+
items: { type: "string" },
|
|
55
|
+
uniqueItems: true,
|
|
56
|
+
},
|
|
52
57
|
},
|
|
53
58
|
additionalProperties: false,
|
|
54
59
|
},
|
|
@@ -79,6 +84,10 @@ const rule = {
|
|
|
79
84
|
const excludeTestCallbacks = typeof opts.excludeTestCallbacks === "boolean"
|
|
80
85
|
? opts.excludeTestCallbacks
|
|
81
86
|
: true;
|
|
87
|
+
const additionalTestHelperNames = Array.isArray(opts.additionalTestHelperNames) &&
|
|
88
|
+
opts.additionalTestHelperNames.every((name) => typeof name === "string")
|
|
89
|
+
? opts.additionalTestHelperNames
|
|
90
|
+
: undefined;
|
|
82
91
|
/**
|
|
83
92
|
* Optional debug logging for troubleshooting this rule.
|
|
84
93
|
* Developers can temporarily uncomment the block below to log when the rule
|
|
@@ -94,7 +103,10 @@ const rule = {
|
|
|
94
103
|
// : "<unknown>",
|
|
95
104
|
// );
|
|
96
105
|
// Local closure that binds configured scope and export priority to the helper.
|
|
97
|
-
const should = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority, {
|
|
106
|
+
const should = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority, {
|
|
107
|
+
excludeTestCallbacks,
|
|
108
|
+
additionalTestHelperNames,
|
|
109
|
+
});
|
|
98
110
|
// Delegate visitor construction to helper to keep this file concise.
|
|
99
111
|
return (0, require_story_visitors_1.buildVisitors)(context, sourceCode, {
|
|
100
112
|
shouldProcessNode: should,
|
|
@@ -104,6 +116,7 @@ const rule = {
|
|
|
104
116
|
methodAnnotationTemplate,
|
|
105
117
|
autoFix,
|
|
106
118
|
excludeTestCallbacks,
|
|
119
|
+
additionalTestHelperNames,
|
|
107
120
|
});
|
|
108
121
|
},
|
|
109
122
|
};
|
|
@@ -72,8 +72,39 @@ declare function tsDecl(): void;`,
|
|
|
72
72
|
*/
|
|
73
73
|
describe('Feature X', () => {
|
|
74
74
|
it('does something', () => {});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Mocha-style suite/context/specify examples
|
|
78
|
+
suite('Mocha suite', () => {
|
|
79
|
+
beforeEach(() => {});
|
|
80
|
+
afterEach(() => {});
|
|
81
|
+
before(() => {});
|
|
82
|
+
after(() => {});
|
|
83
|
+
|
|
84
|
+
test('Mocha test', () => {});
|
|
85
|
+
specify('Mocha specify', () => {});
|
|
86
|
+
context('Mocha context', () => {
|
|
87
|
+
it('nested it', () => {});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Vitest-style APIs including hooks and bench
|
|
92
|
+
describe('Vitest suite', () => {
|
|
93
|
+
beforeEach(() => {});
|
|
94
|
+
afterEach(() => {});
|
|
95
|
+
beforeAll(() => {});
|
|
96
|
+
afterAll(() => {});
|
|
97
|
+
|
|
98
|
+
it('Vitest it', () => {});
|
|
99
|
+
test('Vitest test', () => {});
|
|
100
|
+
bench('Vitest bench', () => {});
|
|
75
101
|
});`,
|
|
76
102
|
},
|
|
103
|
+
{
|
|
104
|
+
name: "[REQ-TEST-CALLBACK-EXCLUSION][Story 003.0] additionalTestHelperNames excludes configured helper callbacks when excludeTestCallbacks=true",
|
|
105
|
+
code: `withTestCase("does something", () => {});`,
|
|
106
|
+
options: [{ additionalTestHelperNames: ["withTestCase"] }],
|
|
107
|
+
},
|
|
77
108
|
],
|
|
78
109
|
invalid: [
|
|
79
110
|
{
|
|
@@ -190,6 +221,22 @@ describe('Feature X', () => {
|
|
|
190
221
|
},
|
|
191
222
|
],
|
|
192
223
|
},
|
|
224
|
+
{
|
|
225
|
+
name: "[REQ-TEST-CALLBACK-EXCLUSION][Story 003.0] bench callback still reported even when included in additionalTestHelperNames",
|
|
226
|
+
code: `bench("bench case", () => {});`,
|
|
227
|
+
options: [{ additionalTestHelperNames: ["bench"], autoFix: false }],
|
|
228
|
+
errors: [
|
|
229
|
+
{
|
|
230
|
+
messageId: "missingStory",
|
|
231
|
+
suggestions: [
|
|
232
|
+
{
|
|
233
|
+
desc: `Add traceability annotation for function '(anonymous)' using @supports (preferred) or @story (legacy), for example: /** @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */`,
|
|
234
|
+
output: `bench("bench case", /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n() => {});`,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
193
240
|
],
|
|
194
241
|
});
|
|
195
242
|
ruleTester.run("require-story-annotation with exportPriority option", require_story_annotation_1.default, {
|
|
@@ -192,6 +192,94 @@ describe("Require Story Helpers (Story 003.0)", () => {
|
|
|
192
192
|
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
193
193
|
expect(result).toBeFalsy();
|
|
194
194
|
});
|
|
195
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as beforeEach callback is excluded by default", () => {
|
|
196
|
+
const node = {
|
|
197
|
+
type: "ArrowFunctionExpression",
|
|
198
|
+
parent: {
|
|
199
|
+
type: "CallExpression",
|
|
200
|
+
callee: { type: "Identifier", name: "beforeEach" },
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
204
|
+
expect(result).toBeFalsy();
|
|
205
|
+
});
|
|
206
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as afterEach callback is excluded by default", () => {
|
|
207
|
+
const node = {
|
|
208
|
+
type: "ArrowFunctionExpression",
|
|
209
|
+
parent: {
|
|
210
|
+
type: "CallExpression",
|
|
211
|
+
callee: { type: "Identifier", name: "afterEach" },
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
215
|
+
expect(result).toBeFalsy();
|
|
216
|
+
});
|
|
217
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as beforeAll callback is excluded by default", () => {
|
|
218
|
+
const node = {
|
|
219
|
+
type: "ArrowFunctionExpression",
|
|
220
|
+
parent: {
|
|
221
|
+
type: "CallExpression",
|
|
222
|
+
callee: { type: "Identifier", name: "beforeAll" },
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
226
|
+
expect(result).toBeFalsy();
|
|
227
|
+
});
|
|
228
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as afterAll callback is excluded by default", () => {
|
|
229
|
+
const node = {
|
|
230
|
+
type: "ArrowFunctionExpression",
|
|
231
|
+
parent: {
|
|
232
|
+
type: "CallExpression",
|
|
233
|
+
callee: { type: "Identifier", name: "afterAll" },
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
237
|
+
expect(result).toBeFalsy();
|
|
238
|
+
});
|
|
239
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as suite callback is excluded by default", () => {
|
|
240
|
+
const node = {
|
|
241
|
+
type: "ArrowFunctionExpression",
|
|
242
|
+
parent: {
|
|
243
|
+
type: "CallExpression",
|
|
244
|
+
callee: { type: "Identifier", name: "suite" },
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
248
|
+
expect(result).toBeFalsy();
|
|
249
|
+
});
|
|
250
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as context callback is excluded by default", () => {
|
|
251
|
+
const node = {
|
|
252
|
+
type: "ArrowFunctionExpression",
|
|
253
|
+
parent: {
|
|
254
|
+
type: "CallExpression",
|
|
255
|
+
callee: { type: "Identifier", name: "context" },
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
259
|
+
expect(result).toBeFalsy();
|
|
260
|
+
});
|
|
261
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as specify callback is excluded by default", () => {
|
|
262
|
+
const node = {
|
|
263
|
+
type: "ArrowFunctionExpression",
|
|
264
|
+
parent: {
|
|
265
|
+
type: "CallExpression",
|
|
266
|
+
callee: { type: "Identifier", name: "specify" },
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
270
|
+
expect(result).toBeFalsy();
|
|
271
|
+
});
|
|
272
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow function used as bench callback is checked by default", () => {
|
|
273
|
+
const node = {
|
|
274
|
+
type: "ArrowFunctionExpression",
|
|
275
|
+
parent: {
|
|
276
|
+
type: "CallExpression",
|
|
277
|
+
callee: { type: "Identifier", name: "bench" },
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
281
|
+
expect(result).toBeTruthy();
|
|
282
|
+
});
|
|
195
283
|
/**
|
|
196
284
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
197
285
|
* @req REQ-TEST-CALLBACK-EXCLUSION - Verify arrow function test callbacks are checked when exclusion is disabled
|
|
@@ -209,4 +297,177 @@ describe("Require Story Helpers (Story 003.0)", () => {
|
|
|
209
297
|
});
|
|
210
298
|
expect(result).toBeTruthy();
|
|
211
299
|
});
|
|
300
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] beforeEach arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
301
|
+
const node = {
|
|
302
|
+
type: "ArrowFunctionExpression",
|
|
303
|
+
parent: {
|
|
304
|
+
type: "CallExpression",
|
|
305
|
+
callee: { type: "Identifier", name: "beforeEach" },
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
309
|
+
excludeTestCallbacks: false,
|
|
310
|
+
});
|
|
311
|
+
expect(result).toBeTruthy();
|
|
312
|
+
});
|
|
313
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] afterEach arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
314
|
+
const node = {
|
|
315
|
+
type: "ArrowFunctionExpression",
|
|
316
|
+
parent: {
|
|
317
|
+
type: "CallExpression",
|
|
318
|
+
callee: { type: "Identifier", name: "afterEach" },
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
322
|
+
excludeTestCallbacks: false,
|
|
323
|
+
});
|
|
324
|
+
expect(result).toBeTruthy();
|
|
325
|
+
});
|
|
326
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] beforeAll arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
327
|
+
const node = {
|
|
328
|
+
type: "ArrowFunctionExpression",
|
|
329
|
+
parent: {
|
|
330
|
+
type: "CallExpression",
|
|
331
|
+
callee: { type: "Identifier", name: "beforeAll" },
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
335
|
+
excludeTestCallbacks: false,
|
|
336
|
+
});
|
|
337
|
+
expect(result).toBeTruthy();
|
|
338
|
+
});
|
|
339
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] afterAll arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
340
|
+
const node = {
|
|
341
|
+
type: "ArrowFunctionExpression",
|
|
342
|
+
parent: {
|
|
343
|
+
type: "CallExpression",
|
|
344
|
+
callee: { type: "Identifier", name: "afterAll" },
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
348
|
+
excludeTestCallbacks: false,
|
|
349
|
+
});
|
|
350
|
+
expect(result).toBeTruthy();
|
|
351
|
+
});
|
|
352
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] suite arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
353
|
+
const node = {
|
|
354
|
+
type: "ArrowFunctionExpression",
|
|
355
|
+
parent: {
|
|
356
|
+
type: "CallExpression",
|
|
357
|
+
callee: { type: "Identifier", name: "suite" },
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
361
|
+
excludeTestCallbacks: false,
|
|
362
|
+
});
|
|
363
|
+
expect(result).toBeTruthy();
|
|
364
|
+
});
|
|
365
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] context arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
366
|
+
const node = {
|
|
367
|
+
type: "ArrowFunctionExpression",
|
|
368
|
+
parent: {
|
|
369
|
+
type: "CallExpression",
|
|
370
|
+
callee: { type: "Identifier", name: "context" },
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
374
|
+
excludeTestCallbacks: false,
|
|
375
|
+
});
|
|
376
|
+
expect(result).toBeTruthy();
|
|
377
|
+
});
|
|
378
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] specify arrow function callback is checked when excludeTestCallbacks is false", () => {
|
|
379
|
+
const node = {
|
|
380
|
+
type: "ArrowFunctionExpression",
|
|
381
|
+
parent: {
|
|
382
|
+
type: "CallExpression",
|
|
383
|
+
callee: { type: "Identifier", name: "specify" },
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
387
|
+
excludeTestCallbacks: false,
|
|
388
|
+
});
|
|
389
|
+
expect(result).toBeTruthy();
|
|
390
|
+
});
|
|
391
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] bench arrow function callback is always checked (also when excludeTestCallbacks is false)", () => {
|
|
392
|
+
const node = {
|
|
393
|
+
type: "ArrowFunctionExpression",
|
|
394
|
+
parent: {
|
|
395
|
+
type: "CallExpression",
|
|
396
|
+
callee: { type: "Identifier", name: "bench" },
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
400
|
+
excludeTestCallbacks: false,
|
|
401
|
+
});
|
|
402
|
+
expect(result).toBeTruthy();
|
|
403
|
+
});
|
|
404
|
+
/**
|
|
405
|
+
* Additional coverage for nested and helper-wrapped test callbacks.
|
|
406
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
407
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION - Document how nested and wrapper-based callbacks interact with exclusion logic
|
|
408
|
+
*/
|
|
409
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Nested anonymous arrow inside it() callback is excluded via nested-function inheritance", () => {
|
|
410
|
+
const outerCallback = {
|
|
411
|
+
type: "ArrowFunctionExpression",
|
|
412
|
+
parent: {
|
|
413
|
+
type: "CallExpression",
|
|
414
|
+
callee: { type: "Identifier", name: "it" },
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
const innerCallback = {
|
|
418
|
+
type: "ArrowFunctionExpression",
|
|
419
|
+
parent: {
|
|
420
|
+
type: "BlockStatement",
|
|
421
|
+
parent: outerCallback,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
// Outer callback is treated as a test framework callback and excluded.
|
|
425
|
+
const outerResult = (0, require_story_helpers_1.shouldProcessNode)(outerCallback, require_story_helpers_1.DEFAULT_SCOPE);
|
|
426
|
+
// Inner anonymous arrow inherits from its nested parent and is also excluded.
|
|
427
|
+
const innerResult = (0, require_story_helpers_1.shouldProcessNode)(innerCallback, require_story_helpers_1.DEFAULT_SCOPE);
|
|
428
|
+
expect(outerResult).toBeFalsy();
|
|
429
|
+
expect(innerResult).toBeFalsy();
|
|
430
|
+
});
|
|
431
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow callback passed to local wrapper around describe() is not treated as a test callback", () => {
|
|
432
|
+
const node = {
|
|
433
|
+
type: "ArrowFunctionExpression",
|
|
434
|
+
parent: {
|
|
435
|
+
type: "CallExpression",
|
|
436
|
+
callee: { type: "Identifier", name: "withDescribe" },
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE);
|
|
440
|
+
expect(result).toBeTruthy();
|
|
441
|
+
});
|
|
442
|
+
/**
|
|
443
|
+
* Additional coverage for configurable test helper names.
|
|
444
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
445
|
+
* @req REQ-TEST-CALLBACK-EXCLUSION - Verify additionalTestHelperNames interacts correctly with exclusion logic
|
|
446
|
+
*/
|
|
447
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] Arrow callback passed to configured additionalTestHelperNames helper is excluded by default", () => {
|
|
448
|
+
const node = {
|
|
449
|
+
type: "ArrowFunctionExpression",
|
|
450
|
+
parent: {
|
|
451
|
+
type: "CallExpression",
|
|
452
|
+
callee: { type: "Identifier", name: "withTest" },
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
456
|
+
additionalTestHelperNames: ["withTest"],
|
|
457
|
+
});
|
|
458
|
+
expect(result).toBeFalsy();
|
|
459
|
+
});
|
|
460
|
+
test("[REQ-TEST-CALLBACK-EXCLUSION] bench callback is never excluded even when included in additionalTestHelperNames", () => {
|
|
461
|
+
const node = {
|
|
462
|
+
type: "ArrowFunctionExpression",
|
|
463
|
+
parent: {
|
|
464
|
+
type: "CallExpression",
|
|
465
|
+
callee: { type: "Identifier", name: "bench" },
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
const result = (0, require_story_helpers_1.shouldProcessNode)(node, require_story_helpers_1.DEFAULT_SCOPE, "all", {
|
|
469
|
+
additionalTestHelperNames: ["bench"],
|
|
470
|
+
});
|
|
471
|
+
expect(result).toBeTruthy();
|
|
472
|
+
});
|
|
212
473
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "A customizable ESLint plugin that enforces traceability annotations in your code, ensuring each implementation is linked to its requirement or test case.",
|
|
5
5
|
"main": "lib/src/index.js",
|
|
6
6
|
"types": "lib/src/index.d.ts",
|