@typescript-eslint/rule-tester 6.0.0-alpha.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -0
  3. package/_ts4.2/dist/RuleTester.d.ts +46 -0
  4. package/_ts4.2/dist/TestFramework.d.ts +66 -0
  5. package/_ts4.2/dist/index.d.ts +5 -0
  6. package/_ts4.2/dist/noFormat.d.ts +6 -0
  7. package/_ts4.2/dist/types/DependencyConstraint.d.ts +14 -0
  8. package/_ts4.2/dist/types/InvalidTestCase.d.ts +68 -0
  9. package/_ts4.2/dist/types/RuleTesterConfig.d.ts +26 -0
  10. package/_ts4.2/dist/types/ValidTestCase.d.ts +53 -0
  11. package/_ts4.2/dist/types/index.d.ts +19 -0
  12. package/_ts4.2/dist/utils/SourceCodeFixer.d.ts +16 -0
  13. package/_ts4.2/dist/utils/ajv.d.ts +3 -0
  14. package/_ts4.2/dist/utils/cloneDeeplyExcludesParent.d.ts +6 -0
  15. package/_ts4.2/dist/utils/config-schema.d.ts +3 -0
  16. package/_ts4.2/dist/utils/config-validator.d.ts +12 -0
  17. package/_ts4.2/dist/utils/dependencyConstraints.d.ts +3 -0
  18. package/_ts4.2/dist/utils/deprecation-warnings.d.ts +13 -0
  19. package/_ts4.2/dist/utils/freezeDeeply.d.ts +5 -0
  20. package/_ts4.2/dist/utils/getRuleOptionsSchema.d.ts +9 -0
  21. package/_ts4.2/dist/utils/hasOwnProperty.d.ts +4 -0
  22. package/_ts4.2/dist/utils/interpolate.d.ts +3 -0
  23. package/_ts4.2/dist/utils/isReadonlyArray.d.ts +2 -0
  24. package/_ts4.2/dist/utils/validationHelpers.d.ts +43 -0
  25. package/dist/RuleTester.d.ts +46 -0
  26. package/dist/RuleTester.d.ts.map +1 -0
  27. package/dist/RuleTester.js +658 -0
  28. package/dist/RuleTester.js.map +1 -0
  29. package/dist/TestFramework.d.ts +66 -0
  30. package/dist/TestFramework.d.ts.map +1 -0
  31. package/dist/TestFramework.js +148 -0
  32. package/dist/TestFramework.js.map +1 -0
  33. package/dist/index.d.ts +5 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +8 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/noFormat.d.ts +6 -0
  38. package/dist/noFormat.d.ts.map +1 -0
  39. package/dist/noFormat.js +12 -0
  40. package/dist/noFormat.js.map +1 -0
  41. package/dist/types/DependencyConstraint.d.ts +14 -0
  42. package/dist/types/DependencyConstraint.d.ts.map +1 -0
  43. package/dist/types/DependencyConstraint.js +3 -0
  44. package/dist/types/DependencyConstraint.js.map +1 -0
  45. package/dist/types/InvalidTestCase.d.ts +68 -0
  46. package/dist/types/InvalidTestCase.d.ts.map +1 -0
  47. package/dist/types/InvalidTestCase.js +3 -0
  48. package/dist/types/InvalidTestCase.js.map +1 -0
  49. package/dist/types/RuleTesterConfig.d.ts +26 -0
  50. package/dist/types/RuleTesterConfig.d.ts.map +1 -0
  51. package/dist/types/RuleTesterConfig.js +3 -0
  52. package/dist/types/RuleTesterConfig.js.map +1 -0
  53. package/dist/types/ValidTestCase.d.ts +53 -0
  54. package/dist/types/ValidTestCase.d.ts.map +1 -0
  55. package/dist/types/ValidTestCase.js +3 -0
  56. package/dist/types/ValidTestCase.js.map +1 -0
  57. package/dist/types/index.d.ts +19 -0
  58. package/dist/types/index.d.ts.map +1 -0
  59. package/dist/types/index.js +3 -0
  60. package/dist/types/index.js.map +1 -0
  61. package/dist/utils/SourceCodeFixer.d.ts +16 -0
  62. package/dist/utils/SourceCodeFixer.d.ts.map +1 -0
  63. package/dist/utils/SourceCodeFixer.js +93 -0
  64. package/dist/utils/SourceCodeFixer.js.map +1 -0
  65. package/dist/utils/ajv.d.ts +3 -0
  66. package/dist/utils/ajv.d.ts.map +1 -0
  67. package/dist/utils/ajv.js +26 -0
  68. package/dist/utils/ajv.js.map +1 -0
  69. package/dist/utils/cloneDeeplyExcludesParent.d.ts +6 -0
  70. package/dist/utils/cloneDeeplyExcludesParent.d.ts.map +1 -0
  71. package/dist/utils/cloneDeeplyExcludesParent.js +24 -0
  72. package/dist/utils/cloneDeeplyExcludesParent.js.map +1 -0
  73. package/dist/utils/config-schema.d.ts +3 -0
  74. package/dist/utils/config-schema.d.ts.map +1 -0
  75. package/dist/utils/config-schema.js +87 -0
  76. package/dist/utils/config-schema.js.map +1 -0
  77. package/dist/utils/config-validator.d.ts +12 -0
  78. package/dist/utils/config-validator.d.ts.map +1 -0
  79. package/dist/utils/config-validator.js +208 -0
  80. package/dist/utils/config-validator.js.map +1 -0
  81. package/dist/utils/dependencyConstraints.d.ts +3 -0
  82. package/dist/utils/dependencyConstraints.d.ts.map +1 -0
  83. package/dist/utils/dependencyConstraints.js +53 -0
  84. package/dist/utils/dependencyConstraints.js.map +1 -0
  85. package/dist/utils/deprecation-warnings.d.ts +13 -0
  86. package/dist/utils/deprecation-warnings.d.ts.map +1 -0
  87. package/dist/utils/deprecation-warnings.js +32 -0
  88. package/dist/utils/deprecation-warnings.js.map +1 -0
  89. package/dist/utils/freezeDeeply.d.ts +5 -0
  90. package/dist/utils/freezeDeeply.d.ts.map +1 -0
  91. package/dist/utils/freezeDeeply.js +23 -0
  92. package/dist/utils/freezeDeeply.js.map +1 -0
  93. package/dist/utils/getRuleOptionsSchema.d.ts +9 -0
  94. package/dist/utils/getRuleOptionsSchema.d.ts.map +1 -0
  95. package/dist/utils/getRuleOptionsSchema.js +33 -0
  96. package/dist/utils/getRuleOptionsSchema.js.map +1 -0
  97. package/dist/utils/hasOwnProperty.d.ts +2 -0
  98. package/dist/utils/hasOwnProperty.d.ts.map +1 -0
  99. package/dist/utils/hasOwnProperty.js +6 -0
  100. package/dist/utils/hasOwnProperty.js.map +1 -0
  101. package/dist/utils/interpolate.d.ts +3 -0
  102. package/dist/utils/interpolate.d.ts.map +1 -0
  103. package/dist/utils/interpolate.js +20 -0
  104. package/dist/utils/interpolate.js.map +1 -0
  105. package/dist/utils/isReadonlyArray.d.ts +2 -0
  106. package/dist/utils/isReadonlyArray.d.ts.map +1 -0
  107. package/dist/utils/isReadonlyArray.js +9 -0
  108. package/dist/utils/isReadonlyArray.js.map +1 -0
  109. package/dist/utils/validationHelpers.d.ts +29 -0
  110. package/dist/utils/validationHelpers.d.ts.map +1 -0
  111. package/dist/utils/validationHelpers.js +158 -0
  112. package/dist/utils/validationHelpers.js.map +1 -0
  113. package/package.json +83 -0
@@ -0,0 +1,658 @@
1
+ "use strict";
2
+ // Forked from https://github.com/eslint/eslint/blob/ad9dd6a933fd098a0d99c6a9aa059850535c23ee/lib/rule-tester/rule-tester.js
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
27
+ if (kind === "m") throw new TypeError("Private method is not writable");
28
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
29
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
30
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
31
+ };
32
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
33
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
34
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
35
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
36
+ };
37
+ var __importDefault = (this && this.__importDefault) || function (mod) {
38
+ return (mod && mod.__esModule) ? mod : { "default": mod };
39
+ };
40
+ var _RuleTester_instances, _RuleTester_testerConfig, _RuleTester_rules, _RuleTester_linter, _RuleTester_normalizeTests, _RuleTester_testValidTemplate, _RuleTester_testInvalidTemplate;
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.RuleTester = void 0;
43
+ const node_assert_1 = __importDefault(require("node:assert"));
44
+ const node_path_1 = __importDefault(require("node:path"));
45
+ const node_util_1 = __importDefault(require("node:util"));
46
+ const eslint_utils_1 = require("@typescript-eslint/utils/eslint-utils");
47
+ const ts_eslint_1 = require("@typescript-eslint/utils/ts-eslint");
48
+ // we intentionally import from eslint here because we need to use the same class
49
+ // that ESLint uses, not our custom override typed version
50
+ const eslint_1 = require("eslint");
51
+ const lodash_merge_1 = __importDefault(require("lodash.merge"));
52
+ const TestFramework_1 = require("./TestFramework");
53
+ const ajv_1 = require("./utils/ajv");
54
+ const cloneDeeplyExcludesParent_1 = require("./utils/cloneDeeplyExcludesParent");
55
+ const config_validator_1 = require("./utils/config-validator");
56
+ const dependencyConstraints_1 = require("./utils/dependencyConstraints");
57
+ const freezeDeeply_1 = require("./utils/freezeDeeply");
58
+ const getRuleOptionsSchema_1 = require("./utils/getRuleOptionsSchema");
59
+ const hasOwnProperty_1 = require("./utils/hasOwnProperty");
60
+ const interpolate_1 = require("./utils/interpolate");
61
+ const isReadonlyArray_1 = require("./utils/isReadonlyArray");
62
+ const SourceCodeFixer = __importStar(require("./utils/SourceCodeFixer"));
63
+ const validationHelpers_1 = require("./utils/validationHelpers");
64
+ const ajv = (0, ajv_1.ajvBuilder)({ strictDefaults: true });
65
+ const TYPESCRIPT_ESLINT_PARSER = '@typescript-eslint/parser';
66
+ const DUPLICATE_PARSER_ERROR_MESSAGE = `Do not set the parser at the test level unless you want to use a parser other than "${TYPESCRIPT_ESLINT_PARSER}"`;
67
+ /*
68
+ * testerDefaultConfig must not be modified as it allows to reset the tester to
69
+ * the initial default configuration
70
+ */
71
+ const testerDefaultConfig = {
72
+ parser: TYPESCRIPT_ESLINT_PARSER,
73
+ rules: {},
74
+ defaultFilenames: { ts: 'file.ts', tsx: 'react.tsx' },
75
+ };
76
+ let defaultConfig = (0, eslint_utils_1.deepMerge)({}, testerDefaultConfig);
77
+ class RuleTester extends TestFramework_1.TestFramework {
78
+ /**
79
+ * Creates a new instance of RuleTester.
80
+ */
81
+ constructor(testerConfig) {
82
+ super();
83
+ _RuleTester_instances.add(this);
84
+ _RuleTester_testerConfig.set(this, void 0);
85
+ _RuleTester_rules.set(this, {});
86
+ _RuleTester_linter.set(this, new ts_eslint_1.Linter());
87
+ /**
88
+ * The configuration to use for this tester. Combination of the tester
89
+ * configuration and the default configuration.
90
+ */
91
+ __classPrivateFieldSet(this, _RuleTester_testerConfig, (0, lodash_merge_1.default)({}, defaultConfig, testerConfig, {
92
+ rules: { 'rule-tester/validate-ast': 'error' },
93
+ // as of eslint 6 you have to provide an absolute path to the parser
94
+ // but that's not as clean to type, this saves us trying to manually enforce
95
+ // that contributors require.resolve everything
96
+ parser: require.resolve((testerConfig ?? defaultConfig).parser),
97
+ }), "f");
98
+ // make sure that the parser doesn't hold onto file handles between tests
99
+ // on linux (i.e. our CI env), there can be very a limited number of watch handles available
100
+ const constructor = this.constructor;
101
+ constructor.afterAll(() => {
102
+ try {
103
+ // instead of creating a hard dependency, just use a soft require
104
+ // a bit weird, but if they're using this tooling, it'll be installed
105
+ const parser = require(TYPESCRIPT_ESLINT_PARSER);
106
+ parser.clearCaches();
107
+ }
108
+ catch {
109
+ // ignored on purpose
110
+ }
111
+ });
112
+ }
113
+ /**
114
+ * Set the configuration to use for all future tests
115
+ */
116
+ static setDefaultConfig(config) {
117
+ if (typeof config !== 'object' || config == null) {
118
+ throw new TypeError('RuleTester.setDefaultConfig: config must be an object');
119
+ }
120
+ // Make sure the rules object exists since it is assumed to exist later
121
+ defaultConfig = (0, eslint_utils_1.deepMerge)(defaultConfig,
122
+ // @ts-expect-error -- no index signature
123
+ config);
124
+ }
125
+ /**
126
+ * Get the current configuration used for all tests
127
+ */
128
+ static getDefaultConfig() {
129
+ return defaultConfig;
130
+ }
131
+ /**
132
+ * Reset the configuration to the initial configuration of the tester removing
133
+ * any changes made until now.
134
+ */
135
+ static resetDefaultConfig() {
136
+ defaultConfig = (0, lodash_merge_1.default)({}, testerDefaultConfig);
137
+ }
138
+ static only(item) {
139
+ if (typeof item === 'string') {
140
+ return { code: item, only: true };
141
+ }
142
+ return { ...item, only: true };
143
+ }
144
+ /**
145
+ * Define a rule for one particular run of tests.
146
+ */
147
+ defineRule(name, rule) {
148
+ __classPrivateFieldGet(this, _RuleTester_rules, "f")[name] = rule;
149
+ }
150
+ /**
151
+ * Adds a new rule test to execute.
152
+ */
153
+ run(ruleName, rule, test) {
154
+ const constructor = this.constructor;
155
+ if (__classPrivateFieldGet(this, _RuleTester_testerConfig, "f").dependencyConstraints &&
156
+ !(0, dependencyConstraints_1.satisfiesAllDependencyConstraints)(__classPrivateFieldGet(this, _RuleTester_testerConfig, "f").dependencyConstraints)) {
157
+ // for frameworks like mocha or jest that have a "skip" version of their function
158
+ // we can provide a nice skipped test!
159
+ constructor.describeSkip(ruleName, () => {
160
+ constructor.it('All tests skipped due to unsatisfied constructor dependency constraints', () => {
161
+ // some frameworks error if there are no assertions
162
+ node_assert_1.default.equal(true, true);
163
+ });
164
+ });
165
+ // don't run any tests because we don't match the base constraint
166
+ return;
167
+ }
168
+ if (!test || typeof test !== 'object') {
169
+ throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
170
+ }
171
+ const scenarioErrors = [];
172
+ validationHelpers_1.REQUIRED_SCENARIOS.forEach(scenarioType => {
173
+ if (!test[scenarioType]) {
174
+ scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`);
175
+ }
176
+ });
177
+ if (scenarioErrors.length > 0) {
178
+ throw new Error([
179
+ `Test Scenarios for rule ${ruleName} is invalid:`,
180
+ ...scenarioErrors,
181
+ ].join('\n'));
182
+ }
183
+ if (typeof rule === 'function') {
184
+ (0, validationHelpers_1.emitLegacyRuleAPIWarning)(ruleName);
185
+ }
186
+ __classPrivateFieldGet(this, _RuleTester_linter, "f").defineRule(ruleName, Object.assign({}, rule, {
187
+ // Create a wrapper rule that freezes the `context` properties.
188
+ create(context) {
189
+ (0, freezeDeeply_1.freezeDeeply)(context.options);
190
+ (0, freezeDeeply_1.freezeDeeply)(context.settings);
191
+ (0, freezeDeeply_1.freezeDeeply)(context.parserOptions);
192
+ return (typeof rule === 'function' ? rule : rule.create)(context);
193
+ },
194
+ }));
195
+ __classPrivateFieldGet(this, _RuleTester_linter, "f").defineRules(__classPrivateFieldGet(this, _RuleTester_rules, "f"));
196
+ const normalizedTests = __classPrivateFieldGet(this, _RuleTester_instances, "m", _RuleTester_normalizeTests).call(this, test);
197
+ function getTestMethod(test) {
198
+ if (test.skip) {
199
+ return 'itSkip';
200
+ }
201
+ if (test.only) {
202
+ return 'itOnly';
203
+ }
204
+ return 'it';
205
+ }
206
+ /*
207
+ * This creates a test suite and pipes all supplied info through
208
+ * one of the templates above.
209
+ */
210
+ constructor.describe(ruleName, () => {
211
+ constructor.describe('valid', () => {
212
+ normalizedTests.valid.forEach(valid => {
213
+ const testName = (() => {
214
+ if (valid.name == null || valid.name.length === 0) {
215
+ return valid.code;
216
+ }
217
+ return valid.name;
218
+ })();
219
+ constructor[getTestMethod(valid)]((0, validationHelpers_1.sanitize)(testName), () => {
220
+ __classPrivateFieldGet(this, _RuleTester_instances, "m", _RuleTester_testValidTemplate).call(this, ruleName, rule, valid);
221
+ });
222
+ });
223
+ });
224
+ constructor.describe('invalid', () => {
225
+ normalizedTests.invalid.forEach(invalid => {
226
+ const name = (() => {
227
+ if (invalid.name == null || invalid.name.length === 0) {
228
+ return invalid.code;
229
+ }
230
+ return invalid.name;
231
+ })();
232
+ constructor[getTestMethod(invalid)]((0, validationHelpers_1.sanitize)(name), () => {
233
+ __classPrivateFieldGet(this, _RuleTester_instances, "m", _RuleTester_testInvalidTemplate).call(this, ruleName, rule, invalid);
234
+ });
235
+ });
236
+ });
237
+ });
238
+ }
239
+ /**
240
+ * Run the rule for the given item
241
+ * @throws {Error} If an invalid schema.
242
+ * Use @private instead of #private to expose it for testing purposes
243
+ */
244
+ runRuleForItem(ruleName, rule, item) {
245
+ let config = (0, lodash_merge_1.default)({}, __classPrivateFieldGet(this, _RuleTester_testerConfig, "f"));
246
+ let code;
247
+ let filename;
248
+ let output;
249
+ let beforeAST;
250
+ let afterAST;
251
+ if (typeof item === 'string') {
252
+ code = item;
253
+ }
254
+ else {
255
+ code = item.code;
256
+ /*
257
+ * Assumes everything on the item is a config except for the
258
+ * parameters used by this tester
259
+ */
260
+ const itemConfig = { ...item };
261
+ for (const parameter of validationHelpers_1.RULE_TESTER_PARAMETERS) {
262
+ delete itemConfig[parameter];
263
+ }
264
+ /*
265
+ * Create the config object from the tester config and this item
266
+ * specific configurations.
267
+ */
268
+ config = (0, lodash_merge_1.default)(config, itemConfig);
269
+ }
270
+ if (item.filename) {
271
+ filename = item.filename;
272
+ }
273
+ if ((0, hasOwnProperty_1.hasOwnProperty)(item, 'options')) {
274
+ (0, node_assert_1.default)(Array.isArray(item.options), 'options must be an array');
275
+ if (item.options.length > 0 &&
276
+ typeof rule === 'object' &&
277
+ (!rule.meta || (rule.meta && rule.meta.schema == null))) {
278
+ (0, validationHelpers_1.emitMissingSchemaWarning)(ruleName);
279
+ }
280
+ config.rules[ruleName] = ['error', ...item.options];
281
+ }
282
+ else {
283
+ config.rules[ruleName] = 'error';
284
+ }
285
+ const schema = (0, getRuleOptionsSchema_1.getRuleOptionsSchema)(rule);
286
+ /*
287
+ * Setup AST getters.
288
+ * The goal is to check whether or not AST was modified when
289
+ * running the rule under test.
290
+ */
291
+ __classPrivateFieldGet(this, _RuleTester_linter, "f").defineRule('rule-tester/validate-ast', {
292
+ create() {
293
+ return {
294
+ Program(node) {
295
+ beforeAST = (0, cloneDeeplyExcludesParent_1.cloneDeeplyExcludesParent)(node);
296
+ },
297
+ 'Program:exit'(node) {
298
+ afterAST = node;
299
+ },
300
+ };
301
+ },
302
+ });
303
+ if (typeof config.parser === 'string') {
304
+ (0, node_assert_1.default)(node_path_1.default.isAbsolute(config.parser), 'Parsers provided as strings to RuleTester must be absolute paths');
305
+ }
306
+ else {
307
+ config.parser = require.resolve(TYPESCRIPT_ESLINT_PARSER);
308
+ }
309
+ __classPrivateFieldGet(this, _RuleTester_linter, "f").defineParser(config.parser, (0, validationHelpers_1.wrapParser)(require(config.parser)));
310
+ if (schema) {
311
+ ajv.validateSchema(schema);
312
+ if (ajv.errors) {
313
+ const errors = ajv.errors
314
+ .map(error => {
315
+ const field = error.dataPath[0] === '.'
316
+ ? error.dataPath.slice(1)
317
+ : error.dataPath;
318
+ return `\t${field}: ${error.message}`;
319
+ })
320
+ .join('\n');
321
+ throw new Error([`Schema for rule ${ruleName} is invalid:`, errors].join(
322
+ // no space after comma to match eslint core
323
+ ','));
324
+ }
325
+ /*
326
+ * `ajv.validateSchema` checks for errors in the structure of the schema (by comparing the schema against a "meta-schema"),
327
+ * and it reports those errors individually. However, there are other types of schema errors that only occur when compiling
328
+ * the schema (e.g. using invalid defaults in a schema), and only one of these errors can be reported at a time. As a result,
329
+ * the schema is compiled here separately from checking for `validateSchema` errors.
330
+ */
331
+ try {
332
+ ajv.compile(schema);
333
+ }
334
+ catch (err) {
335
+ throw new Error(`Schema for rule ${ruleName} is invalid: ${err.message}`);
336
+ }
337
+ }
338
+ (0, config_validator_1.validate)(config, 'rule-tester', id => (id === ruleName ? rule : null));
339
+ // Verify the code.
340
+ // @ts-expect-error -- we don't define deprecated members on our types
341
+ const { getComments } = eslint_1.SourceCode.prototype;
342
+ let messages;
343
+ try {
344
+ // @ts-expect-error -- we don't define deprecated members on our types
345
+ eslint_1.SourceCode.prototype.getComments = validationHelpers_1.getCommentsDeprecation;
346
+ messages = __classPrivateFieldGet(this, _RuleTester_linter, "f").verify(code, config, filename);
347
+ }
348
+ finally {
349
+ // @ts-expect-error -- we don't define deprecated members on our types
350
+ eslint_1.SourceCode.prototype.getComments = getComments;
351
+ }
352
+ const fatalErrorMessage = messages.find(m => m.fatal);
353
+ (0, node_assert_1.default)(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage?.message}`);
354
+ // Verify if autofix makes a syntax error or not.
355
+ if (messages.some(m => m.fix)) {
356
+ output = SourceCodeFixer.applyFixes(code, messages).output;
357
+ const errorMessageInFix = __classPrivateFieldGet(this, _RuleTester_linter, "f")
358
+ .verify(output, config, filename)
359
+ .find(m => m.fatal);
360
+ (0, node_assert_1.default)(!errorMessageInFix, [
361
+ 'A fatal parsing error occurred in autofix.',
362
+ `Error: ${errorMessageInFix?.message}`,
363
+ 'Autofix output:',
364
+ output,
365
+ ].join('\n'));
366
+ }
367
+ else {
368
+ output = code;
369
+ }
370
+ return {
371
+ messages,
372
+ output,
373
+ // is definitely assigned within the `rule-tester/validate-ast` rule
374
+ beforeAST: beforeAST,
375
+ // is definitely assigned within the `rule-tester/validate-ast` rule
376
+ afterAST: (0, cloneDeeplyExcludesParent_1.cloneDeeplyExcludesParent)(afterAST),
377
+ };
378
+ }
379
+ }
380
+ exports.RuleTester = RuleTester;
381
+ _RuleTester_testerConfig = new WeakMap(), _RuleTester_rules = new WeakMap(), _RuleTester_linter = new WeakMap(), _RuleTester_instances = new WeakSet(), _RuleTester_normalizeTests = function _RuleTester_normalizeTests(rawTests) {
382
+ /*
383
+ Automatically add a filename to the tests to enable type-aware tests to "just work".
384
+ This saves users having to verbosely and manually add the filename to every
385
+ single test case.
386
+ Hugely helps with the string-based valid test cases as it means they don't
387
+ need to be made objects!
388
+ */
389
+ const getFilename = (testOptions) => {
390
+ const resolvedOptions = (0, eslint_utils_1.deepMerge)(__classPrivateFieldGet(this, _RuleTester_testerConfig, "f").parserOptions, testOptions);
391
+ const filename = resolvedOptions.ecmaFeatures?.jsx
392
+ ? __classPrivateFieldGet(this, _RuleTester_testerConfig, "f").defaultFilenames.tsx
393
+ : __classPrivateFieldGet(this, _RuleTester_testerConfig, "f").defaultFilenames.ts;
394
+ if (resolvedOptions.project) {
395
+ return node_path_1.default.join(resolvedOptions.tsconfigRootDir ?? process.cwd(), filename);
396
+ }
397
+ return filename;
398
+ };
399
+ const normalizeTest = (test) => {
400
+ if (test.parser === TYPESCRIPT_ESLINT_PARSER) {
401
+ throw new Error(DUPLICATE_PARSER_ERROR_MESSAGE);
402
+ }
403
+ if (!test.filename) {
404
+ return {
405
+ ...test,
406
+ filename: getFilename(test.parserOptions),
407
+ };
408
+ }
409
+ return test;
410
+ };
411
+ const normalizedTests = {
412
+ valid: rawTests.valid
413
+ .map(test => {
414
+ if (typeof test === 'string') {
415
+ return { code: test };
416
+ }
417
+ return test;
418
+ })
419
+ .map(normalizeTest),
420
+ invalid: rawTests.invalid.map(normalizeTest),
421
+ };
422
+ // convenience iterator to make it easy to loop all tests without a concat
423
+ const allTestsIterator = {
424
+ *[Symbol.iterator]() {
425
+ for (const testCase of normalizedTests.valid) {
426
+ yield testCase;
427
+ }
428
+ for (const testCase of normalizedTests.invalid) {
429
+ yield testCase;
430
+ }
431
+ },
432
+ };
433
+ const hasOnly = (() => {
434
+ for (const test of allTestsIterator) {
435
+ if (test.only) {
436
+ return true;
437
+ }
438
+ }
439
+ return false;
440
+ })();
441
+ if (hasOnly) {
442
+ // if there is an `only: true` - don't try apply constraints - assume that
443
+ // we are in "local development" mode rather than "CI validation" mode
444
+ return normalizedTests;
445
+ }
446
+ const hasConstraints = (() => {
447
+ for (const test of allTestsIterator) {
448
+ if (test.dependencyConstraints &&
449
+ Object.keys(test.dependencyConstraints).length > 0) {
450
+ return true;
451
+ }
452
+ }
453
+ return false;
454
+ })();
455
+ if (!hasConstraints) {
456
+ return normalizedTests;
457
+ }
458
+ /*
459
+ Mark all unsatisfactory tests as `skip: true`.
460
+ We do this instead of just omitting the tests entirely because it gives the
461
+ test framework the opportunity to log the test as skipped rather than the test
462
+ just disappearing without a trace.
463
+ */
464
+ const maybeMarkAsOnly = (test) => {
465
+ return {
466
+ ...test,
467
+ skip: !(0, dependencyConstraints_1.satisfiesAllDependencyConstraints)(test.dependencyConstraints),
468
+ };
469
+ };
470
+ normalizedTests.valid = normalizedTests.valid.map(maybeMarkAsOnly);
471
+ normalizedTests.invalid = normalizedTests.invalid.map(maybeMarkAsOnly);
472
+ return normalizedTests;
473
+ }, _RuleTester_testValidTemplate = function _RuleTester_testValidTemplate(ruleName, rule, itemIn) {
474
+ const item = typeof itemIn === 'object' ? itemIn : { code: itemIn };
475
+ node_assert_1.default.ok(typeof item.code === 'string', "Test case must specify a string value for 'code'");
476
+ if (item.name) {
477
+ node_assert_1.default.ok(typeof item.name === 'string', "Optional test case property 'name' must be a string");
478
+ }
479
+ const result = this.runRuleForItem(ruleName, rule, item);
480
+ const messages = result.messages;
481
+ node_assert_1.default.strictEqual(messages.length, 0, node_util_1.default.format('Should have no errors but had %d: %s', messages.length, node_util_1.default.inspect(messages)));
482
+ assertASTDidntChange(result.beforeAST, result.afterAST);
483
+ }, _RuleTester_testInvalidTemplate = function _RuleTester_testInvalidTemplate(ruleName, rule, item) {
484
+ node_assert_1.default.ok(typeof item.code === 'string', "Test case must specify a string value for 'code'");
485
+ if (item.name) {
486
+ node_assert_1.default.ok(typeof item.name === 'string', "Optional test case property 'name' must be a string");
487
+ }
488
+ node_assert_1.default.ok(item.errors || item.errors === 0, `Did not specify errors for an invalid test of ${ruleName}`);
489
+ if (Array.isArray(item.errors) && item.errors.length === 0) {
490
+ node_assert_1.default.fail('Invalid cases must have at least one error');
491
+ }
492
+ const ruleHasMetaMessages = (0, hasOwnProperty_1.hasOwnProperty)(rule, 'meta') && (0, hasOwnProperty_1.hasOwnProperty)(rule.meta, 'messages');
493
+ const friendlyIDList = ruleHasMetaMessages
494
+ ? `[${Object.keys(rule.meta.messages)
495
+ .map(key => `'${key}'`)
496
+ .join(', ')}]`
497
+ : null;
498
+ const result = this.runRuleForItem(ruleName, rule, item);
499
+ const messages = result.messages;
500
+ if (typeof item.errors === 'number') {
501
+ if (item.errors === 0) {
502
+ node_assert_1.default.fail("Invalid cases must have 'error' value greater than 0");
503
+ }
504
+ node_assert_1.default.strictEqual(messages.length, item.errors, node_util_1.default.format('Should have %d error%s but had %d: %s', item.errors, item.errors === 1 ? '' : 's', messages.length, node_util_1.default.inspect(messages)));
505
+ }
506
+ else {
507
+ node_assert_1.default.strictEqual(messages.length, item.errors.length, node_util_1.default.format('Should have %d error%s but had %d: %s', item.errors.length, item.errors.length === 1 ? '' : 's', messages.length, node_util_1.default.inspect(messages)));
508
+ const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleName);
509
+ for (let i = 0, l = item.errors.length; i < l; i++) {
510
+ const error = item.errors[i];
511
+ const message = messages[i];
512
+ (0, node_assert_1.default)(hasMessageOfThisRule, 'Error rule name should be the same as the name of the rule being tested');
513
+ if (typeof error === 'string' || error instanceof RegExp) {
514
+ // Just an error message.
515
+ assertMessageMatches(message.message, error);
516
+ }
517
+ else if (typeof error === 'object' && error != null) {
518
+ /*
519
+ * Error object.
520
+ * This may have a message, messageId, data, node type, line, and/or
521
+ * column.
522
+ */
523
+ Object.keys(error).forEach(propertyName => {
524
+ node_assert_1.default.ok(validationHelpers_1.ERROR_OBJECT_PARAMETERS.has(propertyName), `Invalid error property name '${propertyName}'. Expected one of ${validationHelpers_1.FRIENDLY_ERROR_OBJECT_PARAMETER_LIST}.`);
525
+ });
526
+ // @ts-expect-error -- we purposely don't define `message` on our types as the current standard is `messageId`
527
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'message')) {
528
+ node_assert_1.default.ok(!(0, hasOwnProperty_1.hasOwnProperty)(error, 'messageId'), "Error should not specify both 'message' and a 'messageId'.");
529
+ node_assert_1.default.ok(!(0, hasOwnProperty_1.hasOwnProperty)(error, 'data'), "Error should not specify both 'data' and 'message'.");
530
+ assertMessageMatches(message.message,
531
+ // @ts-expect-error -- we purposely don't define `message` on our types as the current standard is `messageId`
532
+ error.message);
533
+ }
534
+ else if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'messageId')) {
535
+ node_assert_1.default.ok(ruleHasMetaMessages, "Error can not use 'messageId' if rule under test doesn't define 'meta.messages'.");
536
+ if (!(0, hasOwnProperty_1.hasOwnProperty)(rule.meta.messages, error.messageId)) {
537
+ (0, node_assert_1.default)(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
538
+ }
539
+ node_assert_1.default.strictEqual(message.messageId, error.messageId, `messageId '${message.messageId}' does not match expected messageId '${error.messageId}'.`);
540
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'data')) {
541
+ /*
542
+ * if data was provided, then directly compare the returned message to a synthetic
543
+ * interpolated message using the same message ID and data provided in the test.
544
+ * See https://github.com/eslint/eslint/issues/9890 for context.
545
+ */
546
+ const unformattedOriginalMessage = rule.meta.messages[error.messageId];
547
+ const rehydratedMessage = (0, interpolate_1.interpolate)(unformattedOriginalMessage, error.data);
548
+ node_assert_1.default.strictEqual(message.message, rehydratedMessage, `Hydrated message "${rehydratedMessage}" does not match "${message.message}"`);
549
+ }
550
+ }
551
+ node_assert_1.default.ok((0, hasOwnProperty_1.hasOwnProperty)(error, 'data')
552
+ ? (0, hasOwnProperty_1.hasOwnProperty)(error, 'messageId')
553
+ : true, "Error must specify 'messageId' if 'data' is used.");
554
+ if (error.type) {
555
+ node_assert_1.default.strictEqual(message.nodeType, error.type, `Error type should be ${error.type}, found ${message.nodeType}`);
556
+ }
557
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'line')) {
558
+ node_assert_1.default.strictEqual(message.line, error.line, `Error line should be ${error.line}`);
559
+ }
560
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'column')) {
561
+ node_assert_1.default.strictEqual(message.column, error.column, `Error column should be ${error.column}`);
562
+ }
563
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'endLine')) {
564
+ node_assert_1.default.strictEqual(message.endLine, error.endLine, `Error endLine should be ${error.endLine}`);
565
+ }
566
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'endColumn')) {
567
+ node_assert_1.default.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`);
568
+ }
569
+ if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'suggestions')) {
570
+ // Support asserting there are no suggestions
571
+ if (!error.suggestions ||
572
+ ((0, isReadonlyArray_1.isReadonlyArray)(error.suggestions) &&
573
+ error.suggestions.length === 0)) {
574
+ if (Array.isArray(message.suggestions) &&
575
+ message.suggestions.length > 0) {
576
+ node_assert_1.default.fail(`Error should have no suggestions on error with message: "${message.message}"`);
577
+ }
578
+ }
579
+ else {
580
+ (0, node_assert_1.default)(Array.isArray(message.suggestions), `Error should have an array of suggestions. Instead received "${String(message.suggestions)}" on error with message: "${message.message}"`);
581
+ const messageSuggestions = message.suggestions;
582
+ node_assert_1.default.strictEqual(messageSuggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${messageSuggestions.length} suggestions`);
583
+ error.suggestions.forEach((expectedSuggestion, index) => {
584
+ node_assert_1.default.ok(typeof expectedSuggestion === 'object' &&
585
+ expectedSuggestion != null, "Test suggestion in 'suggestions' array must be an object.");
586
+ Object.keys(expectedSuggestion).forEach(propertyName => {
587
+ node_assert_1.default.ok(validationHelpers_1.SUGGESTION_OBJECT_PARAMETERS.has(propertyName), `Invalid suggestion property name '${propertyName}'. Expected one of ${validationHelpers_1.FRIENDLY_SUGGESTION_OBJECT_PARAMETER_LIST}.`);
588
+ });
589
+ const actualSuggestion = messageSuggestions[index];
590
+ const suggestionPrefix = `Error Suggestion at index ${index} :`;
591
+ // @ts-expect-error -- we purposely don't define `desc` on our types as the current standard is `messageId`
592
+ if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'desc')) {
593
+ node_assert_1.default.ok(!(0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'data'), `${suggestionPrefix} Test should not specify both 'desc' and 'data'.`);
594
+ // @ts-expect-error -- we purposely don't define `desc` on our types as the current standard is `messageId`
595
+ const expectedDesc = expectedSuggestion.desc;
596
+ node_assert_1.default.strictEqual(actualSuggestion.desc, expectedDesc, `${suggestionPrefix} desc should be "${expectedDesc}" but got "${actualSuggestion.desc}" instead.`);
597
+ }
598
+ if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'messageId')) {
599
+ node_assert_1.default.ok(ruleHasMetaMessages, `${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`);
600
+ node_assert_1.default.ok((0, hasOwnProperty_1.hasOwnProperty)(rule.meta.messages, expectedSuggestion.messageId), `${suggestionPrefix} Test has invalid messageId '${expectedSuggestion.messageId}', the rule under test allows only one of ${friendlyIDList}.`);
601
+ node_assert_1.default.strictEqual(actualSuggestion.messageId, expectedSuggestion.messageId, `${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`);
602
+ if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'data')) {
603
+ const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
604
+ const rehydratedDesc = (0, interpolate_1.interpolate)(unformattedMetaMessage, expectedSuggestion.data);
605
+ node_assert_1.default.strictEqual(actualSuggestion.desc, rehydratedDesc, `${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`);
606
+ }
607
+ }
608
+ else {
609
+ node_assert_1.default.ok(!(0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'data'), `${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`);
610
+ }
611
+ if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'output')) {
612
+ const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output;
613
+ node_assert_1.default.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`);
614
+ }
615
+ });
616
+ }
617
+ }
618
+ }
619
+ else {
620
+ // Message was an unexpected type
621
+ node_assert_1.default.fail(`Error should be a string, object, or RegExp, but found (${node_util_1.default.inspect(message)})`);
622
+ }
623
+ }
624
+ }
625
+ if ((0, hasOwnProperty_1.hasOwnProperty)(item, 'output')) {
626
+ if (item.output == null) {
627
+ node_assert_1.default.strictEqual(result.output, item.code, 'Expected no autofixes to be suggested');
628
+ }
629
+ else {
630
+ node_assert_1.default.strictEqual(result.output, item.output, 'Output is incorrect.');
631
+ }
632
+ }
633
+ else {
634
+ node_assert_1.default.strictEqual(result.output, item.code, "The rule fixed the code. Please add 'output' property.");
635
+ }
636
+ assertASTDidntChange(result.beforeAST, result.afterAST);
637
+ };
638
+ /**
639
+ * Check if the AST was changed
640
+ */
641
+ function assertASTDidntChange(beforeAST, afterAST) {
642
+ node_assert_1.default.deepStrictEqual(beforeAST, afterAST, 'Rule should not modify AST.');
643
+ }
644
+ /**
645
+ * Asserts that the message matches its expected value. If the expected
646
+ * value is a regular expression, it is checked against the actual
647
+ * value.
648
+ */
649
+ function assertMessageMatches(actual, expected) {
650
+ if (expected instanceof RegExp) {
651
+ // assert.js doesn't have a built-in RegExp match function
652
+ node_assert_1.default.ok(expected.test(actual), `Expected '${actual}' to match ${expected}`);
653
+ }
654
+ else {
655
+ node_assert_1.default.strictEqual(actual, expected);
656
+ }
657
+ }
658
+ //# sourceMappingURL=RuleTester.js.map