@typescript-eslint/rule-tester 8.59.5-alpha.5 → 8.59.5-alpha.6
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/dist/RuleTester.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export declare class RuleTester extends TestFramework {
|
|
|
30
30
|
*/
|
|
31
31
|
static only<MessageIds extends string, Options extends readonly unknown[]>(item: InvalidTestCase<MessageIds, Options>): InvalidTestCase<MessageIds, Options>;
|
|
32
32
|
defineRule(name: string, rule: AnyRuleModule): void;
|
|
33
|
+
/**
|
|
34
|
+
* Verifies that the assertion options are valid
|
|
35
|
+
* @throws {Error} if the options aren't the correct type
|
|
36
|
+
*/
|
|
37
|
+
private verifyAssertionOptions;
|
|
33
38
|
/**
|
|
34
39
|
* Adds a new rule test to execute.
|
|
35
40
|
*/
|
package/dist/RuleTester.js
CHANGED
|
@@ -346,10 +346,31 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
346
346
|
item[prop]();
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Verifies that the assertion options are valid
|
|
351
|
+
* @throws {Error} if the options aren't the correct type
|
|
352
|
+
*/
|
|
353
|
+
verifyAssertionOptions(assertionOption) {
|
|
354
|
+
if (assertionOption == null) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const dataOption = assertionOption.requireData;
|
|
358
|
+
if (dataOption != null &&
|
|
359
|
+
typeof dataOption !== 'boolean' &&
|
|
360
|
+
dataOption !== 'error' &&
|
|
361
|
+
dataOption !== 'suggestion') {
|
|
362
|
+
throw new Error("The assertion option `requireData` should be of type boolean | 'error' | 'suggestion'");
|
|
363
|
+
}
|
|
364
|
+
const locationOption = assertionOption.requireLocation;
|
|
365
|
+
if (locationOption != null && typeof locationOption !== 'boolean') {
|
|
366
|
+
throw new Error('The assertion option `requireLocation` should be of type boolean');
|
|
367
|
+
}
|
|
368
|
+
}
|
|
349
369
|
/**
|
|
350
370
|
* Adds a new rule test to execute.
|
|
351
371
|
*/
|
|
352
372
|
run(ruleName, rule, test) {
|
|
373
|
+
this.verifyAssertionOptions(test.assertionOptions);
|
|
353
374
|
const constructor = this.constructor;
|
|
354
375
|
if (this.#testerConfig.dependencyConstraints &&
|
|
355
376
|
!(0, dependencyConstraints_1.satisfiesAllDependencyConstraints)(this.#testerConfig.dependencyConstraints)) {
|
|
@@ -431,7 +452,7 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
431
452
|
this.#runHook(invalid, 'before');
|
|
432
453
|
this.#testInvalidTemplate(ruleName, rule,
|
|
433
454
|
// no need to pass no infer type parameter down to private methods
|
|
434
|
-
invalid, seenInvalidTestCases);
|
|
455
|
+
invalid, seenInvalidTestCases, test.assertionOptions);
|
|
435
456
|
}
|
|
436
457
|
finally {
|
|
437
458
|
this.#runHook(invalid, 'after');
|
|
@@ -644,7 +665,7 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
644
665
|
* Check if the template is invalid or not
|
|
645
666
|
* all invalid cases go through this.
|
|
646
667
|
*/
|
|
647
|
-
#testInvalidTemplate(ruleName, rule, item, seenInvalidTestCases) {
|
|
668
|
+
#testInvalidTemplate(ruleName, rule, item, seenInvalidTestCases, assertionOptions = {}) {
|
|
648
669
|
node_assert_1.default.ok(typeof item.code === 'string', "Test case must specify a string value for 'code'");
|
|
649
670
|
if (item.name) {
|
|
650
671
|
node_assert_1.default.ok(typeof item.name === 'string', "Optional test case property 'name' must be a string");
|
|
@@ -689,6 +710,7 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
689
710
|
else {
|
|
690
711
|
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)));
|
|
691
712
|
const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleName);
|
|
713
|
+
const { requireData = false, requireLocation = false } = assertionOptions;
|
|
692
714
|
// console.log({ messages });
|
|
693
715
|
for (let i = 0, l = item.errors.length; i < l; i++) {
|
|
694
716
|
const error = item.errors[i];
|
|
@@ -734,6 +756,12 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
734
756
|
const rehydratedMessage = (0, interpolate_1.interpolate)(unformattedOriginalMessage, error.data);
|
|
735
757
|
node_assert_1.default.strictEqual(message.message, rehydratedMessage, `Hydrated message "${rehydratedMessage}" does not match "${message.message}"`);
|
|
736
758
|
}
|
|
759
|
+
else {
|
|
760
|
+
const requiresDataProperty = requireData === true || requireData === 'error';
|
|
761
|
+
const hasPlaceholders = getMessagePlaceholders(rule.meta.messages[error.messageId])
|
|
762
|
+
.length > 0;
|
|
763
|
+
node_assert_1.default.ok(!requiresDataProperty || !hasPlaceholders, `Error should specify the 'data' property as the referenced message has placeholders.`);
|
|
764
|
+
}
|
|
737
765
|
}
|
|
738
766
|
else {
|
|
739
767
|
node_assert_1.default.fail("Test error must specify either a 'messageId' or 'message'.");
|
|
@@ -750,6 +778,11 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
750
778
|
if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'endColumn')) {
|
|
751
779
|
node_assert_1.default.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`);
|
|
752
780
|
}
|
|
781
|
+
if (requireLocation) {
|
|
782
|
+
const locationProperties = ['line', 'column', 'endLine', 'endColumn'];
|
|
783
|
+
const missingKeys = locationProperties.filter(key => !(0, hasOwnProperty_1.hasOwnProperty)(error, key) && (0, hasOwnProperty_1.hasOwnProperty)(message, key));
|
|
784
|
+
node_assert_1.default.ok(missingKeys.length === 0, `Error is missing expected location properties: ${missingKeys.join(', ')}`);
|
|
785
|
+
}
|
|
753
786
|
node_assert_1.default.ok(!message.suggestions || (0, hasOwnProperty_1.hasOwnProperty)(error, 'suggestions'), `Error at index ${i} has suggestions. Please specify 'suggestions' property on the test error object.`);
|
|
754
787
|
if ((0, hasOwnProperty_1.hasOwnProperty)(error, 'suggestions')) {
|
|
755
788
|
// Support asserting there are no suggestions
|
|
@@ -792,13 +825,20 @@ class RuleTester extends TestFramework_1.TestFramework {
|
|
|
792
825
|
node_assert_1.default.ok(ruleHasMetaMessages, `${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`);
|
|
793
826
|
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}.`);
|
|
794
827
|
node_assert_1.default.strictEqual(actualSuggestion.messageId, expectedSuggestion.messageId, `${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`);
|
|
795
|
-
const
|
|
828
|
+
const rawSuggestionMessage = rule.meta.messages[expectedSuggestion.messageId];
|
|
829
|
+
const unsubstitutedPlaceholders = getUnsubstitutedMessagePlaceholders(actualSuggestion.desc, rawSuggestionMessage, expectedSuggestion.data);
|
|
796
830
|
node_assert_1.default.ok(unsubstitutedPlaceholders.length === 0, `The message of the suggestion has ${unsubstitutedPlaceholders.length > 1 ? `unsubstituted placeholders: ${unsubstitutedPlaceholders.map(name => `'${name}'`).join(', ')}` : `an unsubstituted placeholder '${unsubstitutedPlaceholders[0]}'`}. Please provide the missing ${unsubstitutedPlaceholders.length > 1 ? 'values' : 'value'} via the 'data' property for the suggestion in the context.report() call.`);
|
|
797
831
|
if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'data')) {
|
|
798
832
|
const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
|
|
799
833
|
const rehydratedDesc = (0, interpolate_1.interpolate)(unformattedMetaMessage, expectedSuggestion.data);
|
|
800
834
|
node_assert_1.default.strictEqual(actualSuggestion.desc, rehydratedDesc, `${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`);
|
|
801
835
|
}
|
|
836
|
+
else {
|
|
837
|
+
const requiresDataProperty = requireData === true || requireData === 'suggestion';
|
|
838
|
+
const hasPlaceholders = getMessagePlaceholders(rawSuggestionMessage).length >
|
|
839
|
+
0;
|
|
840
|
+
node_assert_1.default.ok(!requiresDataProperty || !hasPlaceholders, `${suggestionPrefix} Suggestion should specify the 'data' property as the referenced message has placeholders.`);
|
|
841
|
+
}
|
|
802
842
|
}
|
|
803
843
|
else if ((0, hasOwnProperty_1.hasOwnProperty)(expectedSuggestion, 'data')) {
|
|
804
844
|
node_assert_1.default.fail(`${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AssertionOptions {
|
|
2
|
+
/**
|
|
3
|
+
* - If `true`, each error object and each suggestion object must also specify `data` if the message referenced by `messageId` has placeholders.
|
|
4
|
+
* - If `'error'`, each error object must also specify `data` if the message referenced by `messageId` has placeholders.
|
|
5
|
+
* - If `'suggestion'`, each suggestion object must also specify `data` if the message referenced by `messageId` has placeholders.
|
|
6
|
+
* @default `false`
|
|
7
|
+
*/
|
|
8
|
+
readonly requireData?: boolean | 'error' | 'suggestion';
|
|
9
|
+
/**
|
|
10
|
+
* If `true`, each `errors` block must contain location properties `line`, `column`, `endLine`, and `endColumn`. Properties `endLine` and `endColumn` may be omitted if the actual error does not contain them.
|
|
11
|
+
* @default `false`
|
|
12
|
+
*/
|
|
13
|
+
readonly requireLocation?: boolean;
|
|
14
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AssertionOptions } from './AssertionOptions';
|
|
1
2
|
import type { InvalidTestCase } from './InvalidTestCase';
|
|
2
3
|
import type { RuleTesterConfig } from './RuleTesterConfig';
|
|
3
4
|
import type { ValidTestCase } from './ValidTestCase';
|
|
@@ -6,10 +7,12 @@ type Mutable<T> = {
|
|
|
6
7
|
};
|
|
7
8
|
export type TesterConfigWithDefaults = Mutable<Required<Pick<RuleTesterConfig, 'defaultFilenames' | 'languageOptions' | 'rules'>> & RuleTesterConfig>;
|
|
8
9
|
export interface RunTests<MessageIds extends string, Options extends readonly unknown[]> {
|
|
10
|
+
readonly assertionOptions?: AssertionOptions;
|
|
9
11
|
readonly invalid: readonly InvalidTestCase<MessageIds, Options>[];
|
|
10
12
|
readonly valid: readonly (string | ValidTestCase<Options>)[];
|
|
11
13
|
}
|
|
12
14
|
export interface NormalizedRunTests<MessageIds extends string, Options extends readonly unknown[]> {
|
|
15
|
+
readonly assertionOptions?: AssertionOptions;
|
|
13
16
|
readonly invalid: readonly InvalidTestCase<MessageIds, Options>[];
|
|
14
17
|
readonly valid: readonly ValidTestCase<Options>[];
|
|
15
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/rule-tester",
|
|
3
|
-
"version": "8.59.5-alpha.
|
|
3
|
+
"version": "8.59.5-alpha.6",
|
|
4
4
|
"description": "Tooling to test ESLint rules",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
|
41
41
|
"lodash.merge": "4.6.2",
|
|
42
42
|
"semver": "^7.7.3",
|
|
43
|
-
"@typescript-eslint/parser": "8.59.5-alpha.
|
|
44
|
-
"@typescript-eslint/
|
|
45
|
-
"@typescript-eslint/
|
|
43
|
+
"@typescript-eslint/parser": "8.59.5-alpha.6",
|
|
44
|
+
"@typescript-eslint/typescript-estree": "8.59.5-alpha.6",
|
|
45
|
+
"@typescript-eslint/utils": "8.59.5-alpha.6"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|