mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
bc1408c28b
Remove the now-redundant fixer eslint_compat option now that ESLint-compatible adjacent-fix handling is the only behavior. follow on from #22071
4038 lines
110 KiB
TypeScript
4038 lines
110 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { RuleTester } from "../src-js/package/rule_tester.ts";
|
|
|
|
import type { Rule } from "../src-js/plugins.ts";
|
|
|
|
/**
|
|
* Test case.
|
|
*/
|
|
interface Case {
|
|
path: string[];
|
|
fn: () => void;
|
|
only: boolean;
|
|
}
|
|
|
|
// Current test cases
|
|
const cases: Case[] = [];
|
|
|
|
// Current test case stack
|
|
const caseStack: string[] = [];
|
|
|
|
// Set up `RuleTester` to use these `describe` and `it` functions
|
|
function describeHook(name: string, fn: () => void): void {
|
|
caseStack.push(name);
|
|
try {
|
|
return fn();
|
|
} finally {
|
|
caseStack.pop();
|
|
}
|
|
}
|
|
RuleTester.describe = describeHook;
|
|
|
|
function itHook(name: string, fn: () => void): void {
|
|
cases.push({ path: caseStack.concat([name]), fn, only: false });
|
|
}
|
|
itHook.only = function itOnlyHook(name: string, fn: () => void): void {
|
|
cases.push({ path: caseStack.concat([name]), fn, only: true });
|
|
};
|
|
RuleTester.it = itHook;
|
|
|
|
/**
|
|
* Run all current test cases.
|
|
* @returns Array containing errors for each test case
|
|
*/
|
|
function runCases(): (Error | null)[] {
|
|
const errors: (Error | null)[] = [];
|
|
for (const testCase of cases) {
|
|
let error = null;
|
|
try {
|
|
testCase.fn();
|
|
} catch (err) {
|
|
error = err as Error;
|
|
}
|
|
errors.push(error);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
// Simple rule which flags identifiers named "foo"
|
|
const simpleRule: Rule = {
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name === "foo") context.report({ message: "No foo!", node });
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
// Rule which flags all identifiers, and uses message IDs to report messages
|
|
const messageIdsRule: Rule = {
|
|
meta: {
|
|
messages: {
|
|
noFoo: "No foo!",
|
|
noBar: "No bar! {{name}}",
|
|
noIdentifiers: "No identifiers! {{name}}",
|
|
},
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name === "foo") {
|
|
context.report({ messageId: "noFoo", node });
|
|
} else if (node.name === "bar") {
|
|
context.report({ messageId: "noBar", node });
|
|
} else {
|
|
context.report({ messageId: "noIdentifiers", data: { name: node.name }, node });
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
// Tests
|
|
describe("RuleTester", () => {
|
|
beforeEach(() => {
|
|
RuleTester.resetDefaultConfig();
|
|
cases.length = 0;
|
|
caseStack.length = 0;
|
|
});
|
|
|
|
describe("can be constructed", () => {
|
|
it("with no config", () => {
|
|
expect(() => new RuleTester()).not.toThrow();
|
|
});
|
|
|
|
it("with config", () => {
|
|
expect(() => new RuleTester({})).not.toThrow();
|
|
});
|
|
});
|
|
|
|
it("generates test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("my-rule", simpleRule, {
|
|
valid: [
|
|
"valid code string",
|
|
{
|
|
code: "valid code from object",
|
|
},
|
|
{
|
|
name: "valid case name",
|
|
code: "let x = 1;",
|
|
},
|
|
],
|
|
invalid: [
|
|
{
|
|
code: "invalid code from object",
|
|
errors: 1,
|
|
},
|
|
{
|
|
name: "invalid case name",
|
|
code: "let x = 1;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(cases).toEqual([
|
|
{ path: ["my-rule", "valid", "valid code string"], fn: expect.any(Function), only: false },
|
|
{
|
|
path: ["my-rule", "valid", "valid code from object"],
|
|
fn: expect.any(Function),
|
|
only: false,
|
|
},
|
|
{ path: ["my-rule", "valid", "valid case name"], fn: expect.any(Function), only: false },
|
|
{
|
|
path: ["my-rule", "invalid", "invalid code from object"],
|
|
fn: expect.any(Function),
|
|
only: false,
|
|
},
|
|
{ path: ["my-rule", "invalid", "invalid case name"], fn: expect.any(Function), only: false },
|
|
]);
|
|
});
|
|
|
|
describe("config", () => {
|
|
it("can be set globally", () => {
|
|
const config = { eslintCompat: true };
|
|
RuleTester.setDefaultConfig(config);
|
|
expect(RuleTester.getDefaultConfig()).toBe(config);
|
|
});
|
|
|
|
it("is reset to default by `resetDefaultConfig`", () => {
|
|
RuleTester.setDefaultConfig({ eslintCompat: true });
|
|
expect(RuleTester.getDefaultConfig()).toHaveProperty("eslintCompat", true);
|
|
|
|
RuleTester.resetDefaultConfig();
|
|
expect(RuleTester.getDefaultConfig()).not.toHaveProperty("eslintCompat");
|
|
});
|
|
|
|
it("cannot permanently change default config", () => {
|
|
const defaultConfig = RuleTester.getDefaultConfig();
|
|
defaultConfig.eslintCompat = true;
|
|
expect(RuleTester.getDefaultConfig()).toBe(defaultConfig);
|
|
|
|
RuleTester.resetDefaultConfig();
|
|
expect(RuleTester.getDefaultConfig()).not.toHaveProperty("eslintCompat");
|
|
});
|
|
});
|
|
|
|
describe("tests valid cases", () => {
|
|
it("which are correct", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "let y;"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("which are incorrect", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let foo;", "foo.foo;", "let foo;", "let foo;"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Should have no errors but had 1: [
|
|
{
|
|
ruleId: 'rule-to-test/no-foo',
|
|
message: 'No foo!',
|
|
messageId: null,
|
|
severity: 1,
|
|
nodeType: 'Identifier',
|
|
line: 1,
|
|
column: 4,
|
|
endLine: 1,
|
|
endColumn: 7,
|
|
fixes: null,
|
|
suggestions: null
|
|
}
|
|
]
|
|
|
|
1 !== 0
|
|
],
|
|
[AssertionError: Should have no errors but had 2: [
|
|
{
|
|
ruleId: 'rule-to-test/no-foo',
|
|
message: 'No foo!',
|
|
messageId: null,
|
|
severity: 1,
|
|
nodeType: 'Identifier',
|
|
line: 1,
|
|
column: 0,
|
|
endLine: 1,
|
|
endColumn: 3,
|
|
fixes: null,
|
|
suggestions: null
|
|
},
|
|
{
|
|
ruleId: 'rule-to-test/no-foo',
|
|
message: 'No foo!',
|
|
messageId: null,
|
|
severity: 1,
|
|
nodeType: 'Identifier',
|
|
line: 1,
|
|
column: 4,
|
|
endLine: 1,
|
|
endColumn: 7,
|
|
fixes: null,
|
|
suggestions: null
|
|
}
|
|
]
|
|
|
|
2 !== 0
|
|
],
|
|
[AssertionError: Detected duplicate test case],
|
|
[AssertionError: Detected duplicate test case],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("tests invalid cases", () => {
|
|
describe("which are correct", () => {
|
|
it("with error count", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{ code: "let foo;", errors: 1 },
|
|
{ code: "foo.foo;", errors: 2 },
|
|
],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("with error messages", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: ["No foo!"],
|
|
},
|
|
{
|
|
code: "foo.foo;",
|
|
errors: ["No foo!", "No foo!"],
|
|
},
|
|
{
|
|
code: "let foo = 1;",
|
|
errors: ["No foo!"],
|
|
},
|
|
{
|
|
code: "foo.foo = 2;",
|
|
errors: [/^No foo!$/, /o [fg]o/],
|
|
},
|
|
{
|
|
code: "let foo = 3;",
|
|
errors: [{ message: "No foo!" }],
|
|
},
|
|
{
|
|
code: "foo.foo = 4;",
|
|
errors: [{ message: "No foo!" }, { message: "No foo!" }],
|
|
},
|
|
{
|
|
code: "let foo = 5;",
|
|
errors: [{ message: "No foo!" }],
|
|
},
|
|
{
|
|
code: "foo.foo = 6;",
|
|
errors: [{ message: /^No foo!$/ }, { message: /o [fg]o/ }],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null, null, null, null, null, null, null]);
|
|
});
|
|
|
|
it("with message IDs and data", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", messageIdsRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
messageId: "noFoo",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "qux.bing;",
|
|
errors: [
|
|
// Without data
|
|
{
|
|
messageId: "noIdentifiers",
|
|
},
|
|
// With data
|
|
{
|
|
messageId: "noIdentifiers",
|
|
data: { name: "bing" },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("with location", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo = 1;\nfoo = 2;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
},
|
|
{
|
|
message: "No foo!",
|
|
line: 2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 1;\nfoo = 3;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
column: 4,
|
|
},
|
|
{
|
|
message: "No foo!",
|
|
column: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 1;\nfoo = 4;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
endLine: 1,
|
|
},
|
|
{
|
|
message: "No foo!",
|
|
endLine: 2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 1;\nfoo = 5;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
endColumn: 7,
|
|
},
|
|
{
|
|
message: "No foo!",
|
|
endColumn: 3,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 1;\nfoo = 6;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 4,
|
|
endLine: 1,
|
|
endColumn: 7,
|
|
},
|
|
{
|
|
message: "No foo!",
|
|
line: 2,
|
|
column: 0,
|
|
endLine: 2,
|
|
endColumn: 3,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null, null, null, null]);
|
|
});
|
|
});
|
|
|
|
describe("which are incorrect", () => {
|
|
it("with error count", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{ code: "let x;", errors: 1 },
|
|
{ code: "let foo;", errors: 2 },
|
|
],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Should have 1 error but had 0: []
|
|
|
|
0 !== 1
|
|
],
|
|
[AssertionError: Should have 2 errors but had 1: [
|
|
{
|
|
ruleId: 'rule-to-test/no-foo',
|
|
message: 'No foo!',
|
|
messageId: null,
|
|
severity: 1,
|
|
nodeType: 'Identifier',
|
|
line: 1,
|
|
column: 4,
|
|
endLine: 1,
|
|
endColumn: 7,
|
|
fixes: null,
|
|
suggestions: null
|
|
}
|
|
]
|
|
|
|
1 !== 2
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with error messages", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: ["wrong message"],
|
|
},
|
|
{
|
|
code: "foo.foo;",
|
|
errors: ["again wrong", "so very wrong"],
|
|
},
|
|
{
|
|
code: "let foo = 1;",
|
|
errors: [/^NO foo!$/],
|
|
},
|
|
{
|
|
code: "foo.foo = 2;",
|
|
errors: [/wrong/, /so very wrong/],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Expected values to be strictly equal:
|
|
+ actual - expected
|
|
|
|
+ 'No foo!'
|
|
- 'wrong message'
|
|
],
|
|
[AssertionError: Expected values to be strictly equal:
|
|
+ actual - expected
|
|
|
|
+ 'No foo!'
|
|
- 'again wrong'
|
|
],
|
|
[AssertionError: Expected 'No foo!' to match /^NO foo!$/],
|
|
[AssertionError: Expected 'No foo!' to match /wrong/],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with error messages in objects", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [{ message: "wrong message" }],
|
|
},
|
|
{
|
|
code: "foo.foo;",
|
|
errors: [{ message: "again wrong" }, { message: "so very wrong" }],
|
|
},
|
|
{
|
|
code: "let foo = 1;",
|
|
errors: [{ message: /^NO foo!$/ }],
|
|
},
|
|
{
|
|
code: "foo.foo = 2;",
|
|
errors: [{ message: /wrong/ }, { message: /so very wrong/ }],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Expected values to be strictly equal:
|
|
+ actual - expected
|
|
|
|
+ 'No foo!'
|
|
- 'wrong message'
|
|
],
|
|
[AssertionError: Expected values to be strictly equal:
|
|
+ actual - expected
|
|
|
|
+ 'No foo!'
|
|
- 'again wrong'
|
|
],
|
|
[AssertionError: Expected 'No foo!' to match /^NO foo!$/],
|
|
[AssertionError: Expected 'No foo!' to match /wrong/],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with message IDs and data", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", messageIdsRule, {
|
|
valid: [],
|
|
invalid: [
|
|
// Wrong message ID
|
|
{
|
|
code: "foo",
|
|
errors: [
|
|
{
|
|
messageId: "noIdentifiers",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "bar",
|
|
errors: [
|
|
{
|
|
messageId: "noFoo",
|
|
},
|
|
],
|
|
},
|
|
// Wrong data
|
|
{
|
|
code: "qux",
|
|
errors: [
|
|
{
|
|
messageId: "noIdentifiers",
|
|
data: { name: "not qux" },
|
|
},
|
|
],
|
|
},
|
|
// Missing data
|
|
{
|
|
code: "whoosh",
|
|
errors: [
|
|
{
|
|
messageId: "noIdentifiers",
|
|
data: { x: "whoosh" },
|
|
},
|
|
],
|
|
},
|
|
// Missing placeholder
|
|
{
|
|
code: "let bar",
|
|
errors: [
|
|
{
|
|
messageId: "noBar",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "bar = 1",
|
|
errors: [
|
|
{
|
|
messageId: "noBar",
|
|
data: { name: "bar" },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: messageId 'noFoo' does not match expected messageId 'noIdentifiers'
|
|
+ actual - expected
|
|
|
|
+ 'noFoo'
|
|
- 'noIdentifiers'
|
|
^
|
|
],
|
|
[AssertionError: messageId 'noBar' does not match expected messageId 'noFoo'
|
|
|
|
'noBar' !== 'noFoo'
|
|
],
|
|
[AssertionError: Hydrated message "No identifiers! not qux" does not match "No identifiers! qux"
|
|
+ actual - expected
|
|
|
|
+ 'No identifiers! qux'
|
|
- 'No identifiers! not qux'
|
|
^
|
|
],
|
|
[AssertionError: Hydrated message "No identifiers! {{name}}" does not match "No identifiers! whoosh"
|
|
+ actual - expected
|
|
|
|
+ 'No identifiers! whoosh'
|
|
- 'No identifiers! {{name}}'
|
|
^
|
|
],
|
|
[AssertionError: The reported message has an unsubstituted placeholder 'name'. Please provide the missing value via the \`data\` property.],
|
|
[AssertionError: Hydrated message "No bar! bar" does not match "No bar! {{name}}"
|
|
+ actual - expected
|
|
|
|
+ 'No bar! {{name}}'
|
|
- 'No bar! bar'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with location", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo = 1;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 2;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
column: 2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 3;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
endLine: 2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 4;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
endColumn: 4,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "let foo = 5;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 2,
|
|
column: 2,
|
|
endLine: 3,
|
|
endColumn: 4,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Actual error location does not match expected error location.
|
|
+ actual - expected
|
|
|
|
{
|
|
+ line: 1
|
|
- line: 2
|
|
}
|
|
],
|
|
[AssertionError: Actual error location does not match expected error location.
|
|
+ actual - expected
|
|
|
|
{
|
|
+ column: 4
|
|
- column: 2
|
|
}
|
|
],
|
|
[AssertionError: Actual error location does not match expected error location.
|
|
+ actual - expected
|
|
|
|
{
|
|
+ endLine: 1
|
|
- endLine: 2
|
|
}
|
|
],
|
|
[AssertionError: Actual error location does not match expected error location.
|
|
+ actual - expected
|
|
|
|
{
|
|
+ endColumn: 7
|
|
- endColumn: 4
|
|
}
|
|
],
|
|
[AssertionError: Actual error location does not match expected error location.
|
|
+ actual - expected
|
|
|
|
{
|
|
+ column: 4,
|
|
+ endColumn: 7,
|
|
+ endLine: 1,
|
|
+ line: 1
|
|
- column: 2,
|
|
- endColumn: 4,
|
|
- endLine: 3,
|
|
- line: 2
|
|
}
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("duplicate code", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: 1,
|
|
},
|
|
{
|
|
code: "let foo;",
|
|
errors: 1,
|
|
},
|
|
{
|
|
code: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[AssertionError: Detected duplicate test case],
|
|
[AssertionError: Detected duplicate test case],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("fixes", () => {
|
|
// Rule which flags identifiers named "foo", and provides a fix to rename them to "bar"
|
|
const fixRule: Rule = {
|
|
meta: {
|
|
fixable: "code",
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name !== "foo") return;
|
|
|
|
context.report({
|
|
message: "No foo!",
|
|
node,
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, "bar");
|
|
},
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
describe("which are correct", () => {
|
|
it("with `output: null` when no fixes", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: null,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("without `output` when no fixes", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `output` matching fixed code", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", fixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: "let bar;",
|
|
errors: 1,
|
|
},
|
|
{
|
|
code: "let foo = foo;",
|
|
output: "let bar = bar;",
|
|
errors: 2,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
});
|
|
|
|
describe("which are incorrect", () => {
|
|
it("with `output` not matching fixed code", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", fixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: "let qux;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Output is incorrect
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let qux;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with `output: null` when there are fixes", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", fixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: null,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Expected no autofixes to be suggested
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let foo;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("without `output` when there are fixes", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", fixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: The rule fixed the code. Please add \`output\` property.
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let foo;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with `output` same as `code` (fixable rule)", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", fixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Output is incorrect
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let foo;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("with `output` same as `code` (non-fixable rule)", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
output: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Test property \`output\` matches \`code\`. If no autofix is expected, set output to \`null\`.],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("recursive", () => {
|
|
// Rule which requires multiple fix passes. It removes trailing `_` from identifiers one at a time.
|
|
// e.g. `foo__` -> `foo_` -> `foo`
|
|
const multiPassFixRule: Rule = {
|
|
meta: {
|
|
fixable: "code",
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (!node.name.endsWith("_")) return;
|
|
|
|
context.report({
|
|
message: "No trailing underscore!",
|
|
node,
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, node.name.slice(0, -1));
|
|
},
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("without `recursive` applies only one fix pass", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo__;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `recursive: false`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo__;",
|
|
recursive: false,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `recursive: 0`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo__;",
|
|
recursive: 0,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `recursive: 1` applies two fix passes total", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo_;",
|
|
recursive: 1,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `recursive: 2` applies three fix passes total", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo;",
|
|
recursive: 2,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `recursive: true` applies all fix passes", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo;",
|
|
recursive: true,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({ recursive: true });
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({ recursive: true });
|
|
const tester = new RuleTester();
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo;",
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("test case overrides `RuleTester` options", () => {
|
|
const tester = new RuleTester({ recursive: true });
|
|
tester.run("no-trailing-underscore", multiPassFixRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo___;",
|
|
output: "let foo__;",
|
|
recursive: false,
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
});
|
|
|
|
describe("adjacent fixes", () => {
|
|
// Rule which produces two diagnostics with adjacent fix ranges for identifier `ab`.
|
|
// Fix 1 replaces `a` (first char), fix 2 replaces `b` (second char).
|
|
// End of fix 1's range === start of fix 2's range.
|
|
//
|
|
// Adjacent fixes are considered overlapping, matching ESLint, so only the first fix applies.
|
|
const adjacentFixesRule: Rule = {
|
|
meta: {
|
|
fixable: "code",
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name !== "ab") return;
|
|
|
|
const start = node.range[0];
|
|
const mid = start + 1;
|
|
const end = node.range[1];
|
|
context.report({
|
|
message: "Fix a",
|
|
node,
|
|
fix(fixer) {
|
|
return fixer.replaceTextRange([start, mid], "x");
|
|
},
|
|
});
|
|
context.report({
|
|
message: "Fix b",
|
|
node,
|
|
fix(fixer) {
|
|
return fixer.replaceTextRange([mid, end], "y");
|
|
},
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("only first adjacent fix applied", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("adjacent-fixes", adjacentFixesRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let ab;",
|
|
output: "let xb;",
|
|
errors: 2,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("suggestions", () => {
|
|
// Rule which suggests renaming `foo` to `bar` or `qux`
|
|
const suggestRule: Rule = {
|
|
meta: {
|
|
hasSuggestions: true,
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name !== "foo") return;
|
|
|
|
context.report({
|
|
message: "No foo!",
|
|
node,
|
|
suggest: [
|
|
{
|
|
desc: "Rename to bar",
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, "bar");
|
|
},
|
|
},
|
|
{
|
|
desc: "Rename to qux",
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, "qux");
|
|
},
|
|
},
|
|
],
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
// Same as above rule, but using `messageId`s
|
|
const suggestMessageIdRule: Rule = {
|
|
meta: {
|
|
hasSuggestions: true,
|
|
messages: {
|
|
noFoo: "No foo!",
|
|
renameToBar: "Rename to bar",
|
|
renameTo: "Rename to {{name}}",
|
|
},
|
|
},
|
|
create(context) {
|
|
return {
|
|
Identifier(node) {
|
|
if (node.name !== "foo") return;
|
|
|
|
context.report({
|
|
messageId: "noFoo",
|
|
node,
|
|
suggest: [
|
|
{
|
|
messageId: "renameToBar",
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, "bar");
|
|
},
|
|
},
|
|
{
|
|
messageId: "renameTo",
|
|
data: { name: "qux" },
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, "qux");
|
|
},
|
|
},
|
|
],
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
describe("which are correct", () => {
|
|
it("with `desc`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: [
|
|
{ desc: "Rename to bar", output: "let bar;" },
|
|
{ desc: "Rename to qux", output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `messageId`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestMessageIdRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
messageId: "noFoo",
|
|
suggestions: [
|
|
{ messageId: "renameToBar", output: "let bar;" },
|
|
{ messageId: "renameTo", data: { name: "qux" }, output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("with `suggestions: null` when no suggestions", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: null,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("without `suggestions` (not checked)", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [{ message: "No foo!" }],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
});
|
|
|
|
describe("which are incorrect", () => {
|
|
it("wrong suggestion count", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: [{ desc: "Rename to bar", output: "let bar;" }],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Error should have 1 suggestion. Instead found 2 suggestions.
|
|
|
|
2 !== 1
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("wrong `desc`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: [
|
|
{ desc: "Wrong description", output: "let bar;" },
|
|
{ desc: "Rename to qux", output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Suggestion at index 0: \`desc\` should be "Wrong description" but got "Rename to bar" instead
|
|
+ actual - expected
|
|
|
|
+ 'Rename to bar'
|
|
- 'Wrong description'
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("wrong `output`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: [
|
|
{ desc: "Rename to bar", output: "let qux;" },
|
|
{ desc: "Rename to qux", output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Suggestion at index 0: Expected the applied suggestion fix to match the test suggestion output
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let qux;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("`output` same as original source code", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: [
|
|
{ desc: "Rename to bar", output: "let foo;" },
|
|
{ desc: "Rename to qux", output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Suggestion at index 0: Expected the applied suggestion fix to match the test suggestion output
|
|
+ actual - expected
|
|
|
|
+ 'let bar;'
|
|
- 'let foo;'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("wrong `messageId`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestMessageIdRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
messageId: "noFoo",
|
|
suggestions: [
|
|
{ messageId: "renameTo", output: "let bar;" },
|
|
{ messageId: "renameTo", data: { name: "qux" }, output: "let qux;" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Suggestion at index 0: messageId 'renameToBar' does not match expected messageId 'renameTo'
|
|
+ actual - expected
|
|
|
|
+ 'renameToBar'
|
|
- 'renameTo'
|
|
^
|
|
],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("`suggestions: null` when rule has suggestions", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", suggestRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "let foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
suggestions: null,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[AssertionError: Rule produced suggestions],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("errors when parsing failure", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let"],
|
|
invalid: [{ code: "let", errors: 1 }],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("runs `before` and `after` hooks", () => {
|
|
const validTests = [
|
|
// Passes
|
|
{
|
|
code: "let x;",
|
|
before: vi.fn(),
|
|
after: vi.fn(),
|
|
},
|
|
// Fails
|
|
{
|
|
code: "let foo;",
|
|
before: vi.fn(),
|
|
after: vi.fn(),
|
|
},
|
|
];
|
|
|
|
const invalidTests = [
|
|
// Passes
|
|
{
|
|
code: "let foo;",
|
|
before: vi.fn(),
|
|
after: vi.fn(),
|
|
errors: 1,
|
|
},
|
|
// Fails
|
|
{
|
|
code: "let x;",
|
|
before: vi.fn(),
|
|
after: vi.fn(),
|
|
errors: 2,
|
|
},
|
|
];
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: validTests,
|
|
invalid: invalidTests,
|
|
});
|
|
runCases();
|
|
|
|
for (const test of [...validTests, ...invalidTests]) {
|
|
expect(test.before).toHaveBeenCalledExactlyOnceWith();
|
|
expect(test.before.mock.contexts[0]).toBe(test);
|
|
expect(test.after).toHaveBeenCalledExactlyOnceWith();
|
|
expect(test.after.mock.contexts[0]).toBe(test);
|
|
}
|
|
});
|
|
|
|
describe("ESLint compat mode", () => {
|
|
it("enabled globally", () => {
|
|
RuleTester.setDefaultConfig({ eslintCompat: true });
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 1,
|
|
endLine: 1,
|
|
endColumn: 4,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("enabled in `RuleTester` options", () => {
|
|
const tester = new RuleTester({ eslintCompat: true });
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "foo;",
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 1,
|
|
endLine: 1,
|
|
endColumn: 4,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("enabled in in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "foo = 1;",
|
|
// Default: eslintCompat: false
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 0,
|
|
endLine: 1,
|
|
endColumn: 3,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "foo = 1;",
|
|
eslintCompat: false,
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 0,
|
|
endLine: 1,
|
|
endColumn: 3,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "foo = 1;",
|
|
eslintCompat: true,
|
|
errors: [
|
|
{
|
|
message: "No foo!",
|
|
line: 1,
|
|
column: 1, // 1 not 0
|
|
endLine: 1,
|
|
endColumn: 4, // 4 not 3
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null, null]);
|
|
});
|
|
});
|
|
|
|
describe("parsing options", () => {
|
|
describe("sourceType", () => {
|
|
describe("default", () => {
|
|
const reportSourceTypeRule: Rule = {
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report({
|
|
message: `sourceType: ${context.languageOptions.sourceType}`,
|
|
node,
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("unambiguous without ESLint compatibility mode", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("source-type", reportSourceTypeRule, {
|
|
valid: [],
|
|
invalid: [
|
|
// No ESM syntax, parsed as script, so `with` is allowed
|
|
{
|
|
code: "with (obj) {}",
|
|
errors: ["sourceType: script"],
|
|
},
|
|
// Has ESM syntax, parsed as module
|
|
{
|
|
code: "import x from 'foo';",
|
|
errors: ["sourceType: module"],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("module with ESLint compatibility mode", () => {
|
|
const tester = new RuleTester({ eslintCompat: true });
|
|
tester.run("source-type", reportSourceTypeRule, {
|
|
// Parsed as module, `with` is not allowed, so parse error
|
|
valid: ["with (obj) {}"],
|
|
// Has ESM syntax, successfully parsed as module
|
|
invalid: [
|
|
{
|
|
code: "import x from 'foo';",
|
|
errors: ["sourceType: module"],
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("module", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "script" },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "script" },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "script" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("script", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "script" },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "script" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "script" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "script" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("commonjs", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "commonjs" },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "return 123;", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "commonjs" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "return 123;", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "commonjs" },
|
|
},
|
|
{
|
|
code: "return 123;",
|
|
languageOptions: { sourceType: "commonjs" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "commonjs" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("unambiguous", () => {
|
|
describe("with `eslintCompat` option", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
eslintCompat: true,
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
eslintCompat: true,
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
eslintCompat: true,
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
eslintCompat: true,
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
[Error: 'unambiguous' source type is not supported in ESLint compatibility mode.
|
|
Disable ESLint compatibility mode by setting \`eslintCompat\` to \`false\` in the config / test case.],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("without `eslintCompat` option", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["with (obj) {}", "import x from 'foo';"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("mixed across test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
// Default = unambiguous
|
|
"with (obj) {}",
|
|
"import x from 'foo';",
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "script" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "module" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "script" },
|
|
},
|
|
{
|
|
code: "with (obj) {}",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
},
|
|
{
|
|
code: "import x from 'foo';",
|
|
languageOptions: { sourceType: "unambiguous" },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("lang", () => {
|
|
it("default (js)", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
describe("js", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("jsx", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("ts", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("tsx", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("dts", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
it("mixed across test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
// Default (js)
|
|
"let x;",
|
|
"<div/>",
|
|
"let x: number;",
|
|
"let x: T = <div/>;",
|
|
"class C { f(): void }",
|
|
// JS
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
// JSX
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
},
|
|
// TS
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
// TSX
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "tsx" } },
|
|
},
|
|
// DTS
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "let x: number;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "let x: T = <div/>;",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
{
|
|
code: "class C { f(): void }",
|
|
languageOptions: { parserOptions: { lang: "dts" } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("is overridden by `filename`", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x: number;",
|
|
filename: "foo.ts",
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
},
|
|
{
|
|
code: "<div />",
|
|
filename: "foo.jsx",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
},
|
|
],
|
|
invalid: [
|
|
{
|
|
code: "let x: number;",
|
|
filename: "foo.jsx",
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
errors: 1,
|
|
},
|
|
{
|
|
code: "<div />",
|
|
filename: "foo.ts",
|
|
languageOptions: { parserOptions: { lang: "jsx" } },
|
|
errors: 1,
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
null,
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("ecmaFeatures.jsx", () => {
|
|
it("default (false)", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
describe("false", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("true", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
});
|
|
|
|
it("mixed across test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"let x;",
|
|
"<div/>",
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: false } } },
|
|
},
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
[Error: Parsing failed],
|
|
null,
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
|
|
describe("does not take priority over `lang` option", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { lang: "js", ecmaFeatures: { jsx: true } } },
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "js", ecmaFeatures: { jsx: true } } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["let x;", "<div/>"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { lang: "js", ecmaFeatures: { jsx: true } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { lang: "js", ecmaFeatures: { jsx: true } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases with `lang` defined in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "js" } },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "let x;",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
{
|
|
code: "<div/>",
|
|
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
null,
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ignoreNonFatalErrors", () => {
|
|
it("default (off)", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
describe("disabled", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in `RuleTester` options, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: true },
|
|
},
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: true },
|
|
},
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("set in individual test cases, overriding `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: true },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: {
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe("enabled", () => {
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: true } },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: true },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: false } },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: true },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: ["function f(x, x) {}"],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in individual test cases", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: true } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in individual test cases, overriding global setting", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: false } },
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: true } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in individual test cases, overriding `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
sourceType: "module",
|
|
parserOptions: { ignoreNonFatalErrors: false },
|
|
},
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: true } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
});
|
|
|
|
it("mixed across test cases", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: { sourceType: "module" },
|
|
});
|
|
tester.run("no-foo", simpleRule, {
|
|
valid: [
|
|
"function f(x, x) {}",
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: false } },
|
|
},
|
|
{
|
|
code: "function f(x, x) {}",
|
|
languageOptions: { parserOptions: { ignoreNonFatalErrors: true } },
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: Parsing failed],
|
|
[Error: Parsing failed],
|
|
null,
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("globals", () => {
|
|
const globalReporterRule: Rule = {
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report({
|
|
message: `globals: ${JSON.stringify(context.languageOptions.globals)}`,
|
|
node,
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("is empty object if no globals defined", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: "globals: {}",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
describe("set", () => {
|
|
it("globally", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: {
|
|
globals: {
|
|
read: "readonly",
|
|
write: "writable",
|
|
disabled: "off",
|
|
},
|
|
},
|
|
});
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: 'globals: {"read":"readonly","write":"writable","disabled":"off"}',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("in `RuleTester` options", () => {
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
globals: {
|
|
read: "readonly",
|
|
write: "writable",
|
|
disabled: "off",
|
|
},
|
|
},
|
|
});
|
|
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: 'globals: {"read":"readonly","write":"writable","disabled":"off"}',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("in test case", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
languageOptions: {
|
|
globals: {},
|
|
},
|
|
errors: [
|
|
{
|
|
message: "globals: {}",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "",
|
|
languageOptions: {
|
|
globals: {
|
|
read: "readonly",
|
|
write: "writable",
|
|
disabled: "off",
|
|
},
|
|
},
|
|
errors: [
|
|
{
|
|
message: 'globals: {"read":"readonly","write":"writable","disabled":"off"}',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
});
|
|
|
|
it("merged between global config, config, and test case", () => {
|
|
RuleTester.setDefaultConfig({
|
|
languageOptions: {
|
|
globals: {
|
|
globalConfig: "readonly",
|
|
globalConfigOverriddenByConfig: "readonly",
|
|
globalConfigOverriddenByTestCase: "readonly",
|
|
globalConfigOverriddenByBoth: "readonly",
|
|
},
|
|
},
|
|
});
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: {
|
|
globals: {
|
|
config: "writable",
|
|
globalConfigOverriddenByConfig: "writable",
|
|
globalConfigOverriddenByBoth: "writable",
|
|
configOverriddenByTestCase: "writable",
|
|
},
|
|
},
|
|
});
|
|
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
languageOptions: {
|
|
globals: {
|
|
testCase: "off",
|
|
globalConfigOverriddenByTestCase: "off",
|
|
globalConfigOverriddenByBoth: "off",
|
|
configOverriddenByTestCase: "off",
|
|
},
|
|
},
|
|
errors: [
|
|
{
|
|
message: `globals: ${JSON.stringify({
|
|
globalConfig: "readonly",
|
|
globalConfigOverriddenByConfig: "writable",
|
|
globalConfigOverriddenByTestCase: "off",
|
|
globalConfigOverriddenByBoth: "off",
|
|
config: "writable",
|
|
configOverriddenByTestCase: "off",
|
|
testCase: "off",
|
|
})}`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("normalizes values", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
eslintCompat: true,
|
|
languageOptions: {
|
|
globals: {
|
|
writable: "writable",
|
|
writeable: "writeable",
|
|
true: true,
|
|
trueStr: "true",
|
|
readonly: "readonly",
|
|
readable: "readable",
|
|
false: false,
|
|
falseStr: "false",
|
|
null: null,
|
|
off: "off",
|
|
},
|
|
},
|
|
errors: [
|
|
{
|
|
message: `globals: ${JSON.stringify({
|
|
writable: "writable",
|
|
writeable: "writable",
|
|
true: "writable",
|
|
trueStr: "writable",
|
|
readonly: "readonly",
|
|
readable: "readonly",
|
|
false: "readonly",
|
|
falseStr: "readonly",
|
|
null: "readonly",
|
|
off: "off",
|
|
})}`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
describe("throws on invalid values", () => {
|
|
it("other string", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [
|
|
{
|
|
code: "",
|
|
languageOptions: {
|
|
globals: {
|
|
// @ts-expect-error - intentionally invalid value
|
|
invalid: "invalid",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: 'invalid' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')],
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("`null` when not in ESLint compatibility mode", () => {
|
|
// Note: `null` being accepted in ESLint compatibility mode is tested above
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", globalReporterRule, {
|
|
valid: [
|
|
{
|
|
code: "",
|
|
languageOptions: {
|
|
globals: {
|
|
null: null,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
invalid: [],
|
|
});
|
|
expect(runCases()).toMatchInlineSnapshot(`
|
|
[
|
|
[Error: 'null' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')],
|
|
]
|
|
`);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("settings", () => {
|
|
const settingsReporterRule: Rule = {
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report({
|
|
message: `settings: ${JSON.stringify(context.settings)}`,
|
|
node,
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("is empty object if no settings defined", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", settingsReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: "settings: {}",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("reflects defined settings", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("no-foo", settingsReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
settings: { foo: 123, nested: { bar: true, qux: null } },
|
|
errors: [
|
|
{
|
|
message: 'settings: {"foo":123,"nested":{"bar":true,"qux":null}}',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: "",
|
|
settings: { x: "y" },
|
|
errors: [
|
|
{
|
|
message: 'settings: {"x":"y"}',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null, null]);
|
|
});
|
|
});
|
|
|
|
describe("`cwd` option", () => {
|
|
const cwdReporterRule: Rule = {
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report({
|
|
message: `cwd: ${context.cwd}`,
|
|
node,
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
it("set globally", () => {
|
|
RuleTester.setDefaultConfig({ cwd: "/a/b/c" });
|
|
|
|
const tester = new RuleTester();
|
|
tester.run("cwd", cwdReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: "cwd: /a/b/c",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in `RuleTester` options", () => {
|
|
const tester = new RuleTester({ cwd: "/a/b/c" });
|
|
tester.run("cwd", cwdReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
errors: [
|
|
{
|
|
message: "cwd: /a/b/c",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
|
|
it("set in test case", () => {
|
|
const tester = new RuleTester();
|
|
tester.run("cwd", cwdReporterRule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
code: "",
|
|
cwd: "/a/b/c",
|
|
errors: [
|
|
{
|
|
message: "cwd: /a/b/c",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
expect(runCases()).toEqual([null]);
|
|
});
|
|
});
|
|
});
|