-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjest.setupTests.js
More file actions
212 lines (182 loc) · 6.73 KB
/
jest.setupTests.js
File metadata and controls
212 lines (182 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import path from 'path';
import { fileURLToPath } from 'url';
import { ESLint } from 'eslint';
/**
* Rule Filtering Policy
*
* The following rules may be filtered from test snapshots:
*
* 1. Universal noise:
* - no-undef: Already disabled in ESLint config for 'module is not defined' errors
*
* 2. Documentation rules:
* - jsdoc/*: Only when not specifically testing JSDoc functionality
*
* 3. Style rules:
* - Only when they conflict with the specific rule being tested
* - Must be explicitly justified in test case
*
* All filtered rules are tracked and reported in snapshots for transparency.
*/
/**
* Centralized definition of filterable rules.
*
* This is used to define and store rules that can be filtered for various test operations.
*
* @namespace FILTERABLE_RULES
* @global
* @type {{universal: string[], documentation: string[], style: string[], common: string[], all: (function(): string[])}}
*/
global.FILTERABLE_RULES = {
// Rules that are universally noisy and safe to filter
universal: ['no-undef'], // Already disabled in ESLint config, listed for tracking
// Documentation-related rules that may add noise
documentation: [
'jsdoc/require-jsdoc',
'jsdoc/tag-lines',
'jsdoc/require-param',
'jsdoc/require-returns'
],
// Style rules that may conflict with test purposes
style: [
'arrow-body-style',
'@stylistic/max-statements-per-line',
'@stylistic/padding-line-between-statements'
],
// Convenience combinations
common: ['jsdoc/require-jsdoc', 'arrow-body-style'],
// Get all filterable rules
all: () => [
...global.FILTERABLE_RULES.universal,
...global.FILTERABLE_RULES.documentation,
...global.FILTERABLE_RULES.style
]
};
/**
* Directory name of the current module.
*
* @type {string}
*/
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/**
* Run ESLint on a file and return the results
*
* @param {string} fileName - File to lint
* @returns {Promise<Object>} - ESLint results
*/
global.lintFile = async fileName => {
const filePath = path.join(__dirname, 'tests', '__fixtures__', fileName);
const eslint = new ESLint({
overrideConfigFile: path.join(__dirname, 'tests', 'eslint.config.js')
});
const results = await eslint.lintFiles([filePath]);
return results[0] || {};
};
/**
* Count errors and warnings in ESLint results
* A global variable that holds the count of issues.
* This property is used to track the total number of issues globally in the context of the application.
* It can be updated to reflect changes in the total issue count and accessed wherever global variables are available.
*
* @param {Array} messages - ESLint messages array
* @returns {{errors: *, warnings: *, errorCount, warningCount, totalIssues: *}} - Object containing error and warning counts
*/
global.countIssues = messages => {
const errors = messages.filter(msg => msg.severity === 2);
const warnings = messages.filter(msg => msg.severity === 1);
return {
errors,
warnings,
errorCount: errors.length,
warningCount: warnings.length,
totalIssues: errors.length + warnings.length
};
};
/**
* Validate filtered rules against policy
*
* @param {string} fileName - File being linted
* @param {string} [testRule] - The rule being tested (if applicable)
* @param {string[]} disableRules - Rules to be filtered
* @returns {string[]} - Validated list of rules to filter
* @throws {Error} - If filtering policy is violated
*/
global.validateFilterRules = (fileName, testRule, disableRules = []) => {
if (!disableRules.length) return disableRules;
const allAllowed = global.FILTERABLE_RULES.all();
// Check for invalid rules
const invalid = disableRules.filter(r => !allAllowed.includes(r));
if (invalid.length > 0) {
throw new Error(`Invalid filter rules: ${invalid.join(', ')}. Only rules defined in FILTERABLE_RULES can be filtered.`);
}
// Don't allow filtering the rule being tested
if (testRule && disableRules.includes(testRule)) {
throw new Error(`Cannot filter the rule being tested: ${testRule}`);
}
// For JSDoc test files, don't allow filtering JSDoc rules
if (fileName.includes('jsdoc') && disableRules.some(r => r.startsWith('jsdoc/'))) {
throw new Error('Cannot filter JSDoc rules in JSDoc test files');
}
return disableRules;
};
/**
* Process ESLint results into a structured format for snapshot testing
*
* @param {string} fileName - File to lint
* @param {Object} options - Options for processing
* @param {string[]} [options.disableRules] - Array of rule IDs to filter out from results
* @param {string} [options.testRule] - The rule being tested (for validation)
* @returns {Promise<Object>} - Processed ESLint results
*/
global.lintAndProcessFile = async (fileName, options = {}) => {
const result = await global.lintFile(fileName);
// Apply filtering based on options
let filteredMessages = result.messages;
let appliedFilters = [...global.FILTERABLE_RULES.universal]; // Always track universal filters
if (options?.disableRules?.length > 0) {
// Validate filters against policy
const validatedRules = global.validateFilterRules(fileName, options.testRule, options.disableRules);
// Add custom filters
appliedFilters = [...appliedFilters, ...validatedRules];
// Filter messages
filteredMessages = result.messages.filter(msg => !appliedFilters.includes(msg.ruleId));
}
// Count filtered issues
const filteredIssues = result.messages.filter(msg =>
!filteredMessages.some(fm => fm.ruleId === msg.ruleId && fm.line === msg.line && fm.column === msg.column));
// Group filtered issues by rule
const filteredCounts = {};
// Count occurrences of each rule
filteredIssues.forEach(msg => {
filteredCounts[msg.ruleId] ??= 0;
filteredCounts[msg.ruleId] += 1;
});
const { errorCount, warningCount, totalIssues } = global.countIssues(filteredMessages);
const projectRoot = path.resolve(__dirname);
// Log filtered rules if any were applied (for debugging)
if (filteredIssues.length > 0 && process.env.DEBUG) {
console.log(`\nFiltered ${filteredIssues.length} issues from ${fileName}:`);
Object.entries(filteredCounts).forEach(([rule, count]) => {
console.log(` ${rule}: ${count} issues`);
});
}
return {
filePath: path.basename(result.filePath),
errorCount,
warningCount,
totalIssues,
messages: filteredMessages.map(({ ruleId, severity, message, line, column }) => ({
ruleId,
severity: severity === 2 ? 'error' : 'warning',
message: message?.replace(projectRoot, '<PROJECT_ROOT>'),
line,
column
})),
// Include filtering information in a snapshot
filtering: {
appliedFilters,
totalFiltered: filteredIssues.length,
ruleBreakdown: filteredCounts
}
};
};