Test internal eslint rules (#9929)

This commit is contained in:
fisker Cheung authored and GitHub committed 2020-12-24 19:44:17 +08:00
1 parent f1574a9e7a
commit c55f899b32
6 files changed
+238 -3

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
name: Internal_ESLint_Rules_Test
on:
push:
paths:
- "scripts/tools/eslint-plugin-prettier-internal-rules"
- ".github/workflows/eslint-rules.yml"
pull_request:
paths:
- "scripts/tools/eslint-plugin-prettier-internal-rules"
- ".github/workflows/eslint-rules.yml"
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
- name: Setup Node.js
uses: actions/setup-node@v2.1.4
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Test
run: cd scripts/tools/eslint-plugin-prettier-internal-rules && yarn test-coverage
+2
View File
@@ -21,3 +21,5 @@ package-lock.json
!.yarn/sdks !.yarn/sdks
!.yarn/versions !.yarn/versions
.pnp.* .pnp.*
.nyc_output
/scripts/tools/eslint-plugin-prettier-internal-rules/node_modules
@@ -53,7 +53,7 @@ module.exports = {
}, },
messages: { messages: {
[MESSAGE_ID_PREFER_NAME_CHECK]: [MESSAGE_ID_PREFER_NAME_CHECK]:
"Prefer `name {{operator}} {{property}}` over `parent.{{property}} {{operator}} node`", "Prefer `name {{operator}} {{propertyText}}` over `parent.{{property}} {{operator}} node`.",
[MESSAGE_ID_NAME_CHECK_FIRST]: [MESSAGE_ID_NAME_CHECK_FIRST]:
"`name` comparison should be on left side.", "`name` comparison should be on left side.",
}, },
@@ -79,7 +79,11 @@ module.exports = {
context.report({ context.report({
node, node,
messageId: MESSAGE_ID_PREFER_NAME_CHECK, messageId: MESSAGE_ID_PREFER_NAME_CHECK,
data: { property: propertyText, operator }, data: {
property: sourceCode.getText(property),
propertyText,
operator,
},
fix: (fixer) => fix: (fixer) =>
fixer.replaceText(node, `name ${operator} ${propertyText}`), fixer.replaceText(node, `name ${operator} ${propertyText}`),
}); });
@@ -50,8 +50,14 @@ module.exports = {
]; ];
}) })
); );
// avoid report on `const {comments} = node` twice
const reported = new Set();
return { return {
[selector](node) { [selector](node) {
if (reported.has(node)) {
return;
}
if (ignored.has(fileName)) { if (ignored.has(fileName)) {
const functionNames = ignored.get(fileName); const functionNames = ignored.get(fileName);
if (functionNames === true) { if (functionNames === true) {
@@ -75,6 +81,7 @@ module.exports = {
return; return;
} }
} }
reported.add(node);
context.report({ context.report({
node, node,
messageId, messageId,
@@ -5,5 +5,9 @@
"private": true, "private": true,
"author": "fisker", "author": "fisker",
"main": "./index.js", "main": "./index.js",
"license": "MIT" "license": "MIT",
"scripts": {
"test": "node test.js",
"test-coverage": "npx nyc node test.js"
}
} }
@@ -0,0 +1,190 @@
/* eslint-disable import/no-extraneous-dependencies */
"use strict";
const path = require("path");
const { outdent } = require("outdent");
const { RuleTester } = require("eslint");
const { rules } = require(".");
const test = (ruleId, tests) => {
new RuleTester({ parserOptions: { ecmaVersion: 2021 } }).run(
ruleId,
rules[ruleId],
tests
);
};
test("better-parent-property-check-in-needs-parens", {
valid: ["function needsParens() {return parent.test === node;}"],
invalid: [
{
code: 'return parent.type === "MemberExpression" && name === "object";',
errors: [{ message: "`name` comparison should be on left side." }],
},
{
code: "return parent.test === node;",
output: 'return name === "test";',
errors: [
{ message: 'Prefer `name === "test"` over `parent.test === node`.' },
],
},
{
code: "return parent.test !== node;",
output: 'return name !== "test";',
errors: [
{ message: 'Prefer `name !== "test"` over `parent.test !== node`.' },
],
},
{
code: 'return parent["property"] === node;',
output: 'return name === "property";',
errors: [
{
message:
'Prefer `name === "property"` over `parent."property" === node`.',
},
],
},
].map((testCase) => ({
...testCase,
code: `function needsParens() {${testCase.code}}`,
output: `function needsParens() {${testCase.output || testCase.code}}`,
filename: "needs-parens.js",
})),
});
test("directly-loc-start-end", {
valid: [],
invalid: [
{
code: "options.locStart(node)",
output: "locStart(node)",
errors: [
{ message: "Please import `locStart` function and use it directly." },
],
},
{
code: "options.locEnd(node)",
output: "locEnd(node)",
errors: [
{ message: "Please import `locEnd` function and use it directly." },
],
},
],
});
test("jsx-identifier-case", {
valid: [
{
code: "const isJSXNode = true",
options: ["isJSXNode"],
},
],
invalid: [
{
code: "function isJSXNode(){}",
output: "function isJsxNode(){}",
errors: [{ message: "Please rename 'isJSXNode' to 'isJsxNode'." }],
},
{
code: "const isJSXNode = true",
output: "const isJsxNode = true",
errors: [{ message: "Please rename 'isJSXNode' to 'isJsxNode'." }],
},
],
});
test("no-node-comments", {
valid: [
"const comments = node.notComments",
{
code: "function functionName() {return node.comments;}",
filename: path.join(__dirname, "../../..", "a.js"),
options: ["a.js"],
},
{
code: "function functionName() {return node.comments;}",
filename: path.join(__dirname, "../../..", "a.js"),
options: [{ file: "a.js", functions: ["functionName"] }],
},
],
invalid: [
...[
"function functionName() {return node.comments;}",
"const {comments} = node",
"const {comments: nodeComments} = node",
].map((code) => ({
code,
output: code,
errors: [{ message: "Do not access node.comments." }],
})),
{
code: "function notFunctionName() {return node.comments;}",
output: "function notFunctionName() {return node.comments;}",
filename: path.join(__dirname, "../../..", "a.js"),
options: [{ file: "a.js", functions: ["functionName"] }],
errors: [{ message: "Do not access node.comments." }],
},
],
});
test("prefer-fast-path-each", {
valid: ["const foo = path.map()"],
invalid: [
{
code: "path.map()",
output: "path.each()",
errors: 1,
},
],
});
test("prefer-is-non-empty-array", {
valid: [
// `isNonEmptyArray` self is ignored
outdent`
function isNonEmptyArray(object){
return Array.isArray(object) && object.length;
}
`,
"a.b && a.c.length",
"a.b || !a.b.length",
'!a["b"] || !a.b.length',
],
invalid: [
...[
"a && a.b && a.b.length",
"a && a.b && a.b.length !== 0",
"a && a.b && a.b.length > 0",
"a && Array.isArray(a.b) && a.b.length",
"a && Array.isArray(a.b) && a.b.length !== 0",
"a && Array.isArray(a.b) && a.b.length > 0",
].map((code) => ({
code,
output: "a && isNonEmptyArray(a.b)",
errors: 1,
})),
...[
"!a || !a.b || !a.b.length",
"!a || !a.b || a.b.length === 0",
"!a || !Array.isArray(a.b) || !a.b.length",
"!a || !Array.isArray(a.b) || a.b.length === 0",
].map((code) => ({
code,
output: "!a || !isNonEmptyArray(a.b)",
errors: 1,
})),
],
});
test("require-json-extensions", {
valid: ['require("./not-exists")', 'require("./index")'],
invalid: [
{
code: 'require("./package")',
filename: __filename,
output: 'require("./package.json")',
errors: [{ message: 'Missing file extension ".json" for "./package".' }],
},
],
});