@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.
- package/LICENSE +21 -0
- package/README.md +10 -0
- package/_ts4.2/dist/RuleTester.d.ts +46 -0
- package/_ts4.2/dist/TestFramework.d.ts +66 -0
- package/_ts4.2/dist/index.d.ts +5 -0
- package/_ts4.2/dist/noFormat.d.ts +6 -0
- package/_ts4.2/dist/types/DependencyConstraint.d.ts +14 -0
- package/_ts4.2/dist/types/InvalidTestCase.d.ts +68 -0
- package/_ts4.2/dist/types/RuleTesterConfig.d.ts +26 -0
- package/_ts4.2/dist/types/ValidTestCase.d.ts +53 -0
- package/_ts4.2/dist/types/index.d.ts +19 -0
- package/_ts4.2/dist/utils/SourceCodeFixer.d.ts +16 -0
- package/_ts4.2/dist/utils/ajv.d.ts +3 -0
- package/_ts4.2/dist/utils/cloneDeeplyExcludesParent.d.ts +6 -0
- package/_ts4.2/dist/utils/config-schema.d.ts +3 -0
- package/_ts4.2/dist/utils/config-validator.d.ts +12 -0
- package/_ts4.2/dist/utils/dependencyConstraints.d.ts +3 -0
- package/_ts4.2/dist/utils/deprecation-warnings.d.ts +13 -0
- package/_ts4.2/dist/utils/freezeDeeply.d.ts +5 -0
- package/_ts4.2/dist/utils/getRuleOptionsSchema.d.ts +9 -0
- package/_ts4.2/dist/utils/hasOwnProperty.d.ts +4 -0
- package/_ts4.2/dist/utils/interpolate.d.ts +3 -0
- package/_ts4.2/dist/utils/isReadonlyArray.d.ts +2 -0
- package/_ts4.2/dist/utils/validationHelpers.d.ts +43 -0
- package/dist/RuleTester.d.ts +46 -0
- package/dist/RuleTester.d.ts.map +1 -0
- package/dist/RuleTester.js +658 -0
- package/dist/RuleTester.js.map +1 -0
- package/dist/TestFramework.d.ts +66 -0
- package/dist/TestFramework.d.ts.map +1 -0
- package/dist/TestFramework.js +148 -0
- package/dist/TestFramework.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/noFormat.d.ts +6 -0
- package/dist/noFormat.d.ts.map +1 -0
- package/dist/noFormat.js +12 -0
- package/dist/noFormat.js.map +1 -0
- package/dist/types/DependencyConstraint.d.ts +14 -0
- package/dist/types/DependencyConstraint.d.ts.map +1 -0
- package/dist/types/DependencyConstraint.js +3 -0
- package/dist/types/DependencyConstraint.js.map +1 -0
- package/dist/types/InvalidTestCase.d.ts +68 -0
- package/dist/types/InvalidTestCase.d.ts.map +1 -0
- package/dist/types/InvalidTestCase.js +3 -0
- package/dist/types/InvalidTestCase.js.map +1 -0
- package/dist/types/RuleTesterConfig.d.ts +26 -0
- package/dist/types/RuleTesterConfig.d.ts.map +1 -0
- package/dist/types/RuleTesterConfig.js +3 -0
- package/dist/types/RuleTesterConfig.js.map +1 -0
- package/dist/types/ValidTestCase.d.ts +53 -0
- package/dist/types/ValidTestCase.d.ts.map +1 -0
- package/dist/types/ValidTestCase.js +3 -0
- package/dist/types/ValidTestCase.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/SourceCodeFixer.d.ts +16 -0
- package/dist/utils/SourceCodeFixer.d.ts.map +1 -0
- package/dist/utils/SourceCodeFixer.js +93 -0
- package/dist/utils/SourceCodeFixer.js.map +1 -0
- package/dist/utils/ajv.d.ts +3 -0
- package/dist/utils/ajv.d.ts.map +1 -0
- package/dist/utils/ajv.js +26 -0
- package/dist/utils/ajv.js.map +1 -0
- package/dist/utils/cloneDeeplyExcludesParent.d.ts +6 -0
- package/dist/utils/cloneDeeplyExcludesParent.d.ts.map +1 -0
- package/dist/utils/cloneDeeplyExcludesParent.js +24 -0
- package/dist/utils/cloneDeeplyExcludesParent.js.map +1 -0
- package/dist/utils/config-schema.d.ts +3 -0
- package/dist/utils/config-schema.d.ts.map +1 -0
- package/dist/utils/config-schema.js +87 -0
- package/dist/utils/config-schema.js.map +1 -0
- package/dist/utils/config-validator.d.ts +12 -0
- package/dist/utils/config-validator.d.ts.map +1 -0
- package/dist/utils/config-validator.js +208 -0
- package/dist/utils/config-validator.js.map +1 -0
- package/dist/utils/dependencyConstraints.d.ts +3 -0
- package/dist/utils/dependencyConstraints.d.ts.map +1 -0
- package/dist/utils/dependencyConstraints.js +53 -0
- package/dist/utils/dependencyConstraints.js.map +1 -0
- package/dist/utils/deprecation-warnings.d.ts +13 -0
- package/dist/utils/deprecation-warnings.d.ts.map +1 -0
- package/dist/utils/deprecation-warnings.js +32 -0
- package/dist/utils/deprecation-warnings.js.map +1 -0
- package/dist/utils/freezeDeeply.d.ts +5 -0
- package/dist/utils/freezeDeeply.d.ts.map +1 -0
- package/dist/utils/freezeDeeply.js +23 -0
- package/dist/utils/freezeDeeply.js.map +1 -0
- package/dist/utils/getRuleOptionsSchema.d.ts +9 -0
- package/dist/utils/getRuleOptionsSchema.d.ts.map +1 -0
- package/dist/utils/getRuleOptionsSchema.js +33 -0
- package/dist/utils/getRuleOptionsSchema.js.map +1 -0
- package/dist/utils/hasOwnProperty.d.ts +2 -0
- package/dist/utils/hasOwnProperty.d.ts.map +1 -0
- package/dist/utils/hasOwnProperty.js +6 -0
- package/dist/utils/hasOwnProperty.js.map +1 -0
- package/dist/utils/interpolate.d.ts +3 -0
- package/dist/utils/interpolate.d.ts.map +1 -0
- package/dist/utils/interpolate.js +20 -0
- package/dist/utils/interpolate.js.map +1 -0
- package/dist/utils/isReadonlyArray.d.ts +2 -0
- package/dist/utils/isReadonlyArray.d.ts.map +1 -0
- package/dist/utils/isReadonlyArray.js +9 -0
- package/dist/utils/isReadonlyArray.js.map +1 -0
- package/dist/utils/validationHelpers.d.ts +29 -0
- package/dist/utils/validationHelpers.d.ts.map +1 -0
- package/dist/utils/validationHelpers.js +158 -0
- package/dist/utils/validationHelpers.js.map +1 -0
- 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
|