Skip to content

test(eslint-plugin): migrate validation tools to jest test cases #1403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,10 @@ jobs:
yarn lint
displayName: 'Run linting'

- script: |
yarn check:docs
displayName: 'Validate documentation'

- script: |
yarn check:spelling
displayName: 'Validate documentation spelling'

- script: |
yarn check:configs
displayName: 'Validate plugin configs'

- script: |
yarn test
displayName: 'Run unit tests'
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"main": "dist/index.js",
"scripts": {
"build": "tsc -b tsconfig.build.json",
"check:docs": "../../node_modules/.bin/ts-node --files --transpile-only ./tools/validate-docs/index.ts",
"check:configs": "../../node_modules/.bin/ts-node --files --transpile-only ./tools/validate-configs/index.ts",
"check:docs": "jest tests/docs.test.ts --runTestsByPath --silent --runInBand",
"check:configs": "jest tests/configs.test.ts --runTestsByPath --silent --runInBand",
"clean": "tsc -b tsconfig.build.json --clean",
"format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore",
"generate:configs": "../../node_modules/.bin/ts-node --files --transpile-only tools/generate-configs.ts",
Expand Down
71 changes: 71 additions & 0 deletions packages/eslint-plugin/tests/configs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import rules from '../src/rules';
import plugin from '../src/index';

function entriesToObject<T = unknown>(value: [string, T][]): Record<string, T> {
return value.reduce<Record<string, T>>((accum, [k, v]) => {
accum[k] = v;
return accum;
}, {});
}

const notDeprecatedRules = Object.entries(rules).filter(
([, rule]) => !rule.meta.deprecated,
);

function filterRules(values: Record<string, string>): [string, string][] {
return Object.entries(values).filter(([name]) =>
name.startsWith(RULE_NAME_PREFIX),
);
}

const RULE_NAME_PREFIX = '@typescript-eslint/';

describe('all.json config', () => {
const configRules = filterRules(plugin.configs.all.rules);
const ruleConfigs = notDeprecatedRules.map<[string, string]>(([name]) => [
`${RULE_NAME_PREFIX}${name}`,
'error',
]);

it('contains all of the rules, excluding the deprecated ones', () => {
expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules));
});
});

describe('recommended.json config', () => {
const configRules = filterRules(plugin.configs.recommended.rules);
const ruleConfigs = notDeprecatedRules
.filter(
([, rule]) =>
rule.meta.docs.recommended !== false &&
rule.meta.docs.requiresTypeChecking !== true,
)
.map<[string, string]>(([name, rule]) => [
`${RULE_NAME_PREFIX}${name}`,
rule.meta.docs.recommended || 'off',
]);

it("contains all recommended rules that don't require typechecking, excluding the deprecated ones", () => {
expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules));
});
});

describe('recommended-requiring-type-checking.json config', () => {
const configRules = filterRules(
plugin.configs['recommended-requiring-type-checking'].rules,
);
const ruleConfigs = notDeprecatedRules
.filter(
([, rule]) =>
rule.meta.docs.recommended !== false &&
rule.meta.docs.requiresTypeChecking === true,
)
.map<[string, string]>(([name, rule]) => [
`${RULE_NAME_PREFIX}${name}`,
rule.meta.docs.recommended || 'off',
]);

it('contains all recommended rules that require type checking, excluding the deprecated ones', () => {
expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules));
});
});
42 changes: 0 additions & 42 deletions packages/eslint-plugin/tests/configs/all.test.ts

This file was deleted.

141 changes: 141 additions & 0 deletions packages/eslint-plugin/tests/docs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import fs from 'fs';
import path from 'path';

import marked from 'marked';
import rules from '../src/rules';

const docsRoot = path.resolve(__dirname, '../docs/rules');
const rulesData = Object.entries(rules);

function createRuleLink(ruleName: string): string {
return `[\`@typescript-eslint/${ruleName}\`](./docs/rules/${ruleName}.md)`;
}

function parseReadme(): marked.Tokens.Table {
const readmeRaw = fs.readFileSync(
path.resolve(__dirname, '../README.md'),
'utf8',
);
const readme = marked.lexer(readmeRaw, {
gfm: true,
silent: false,
});

// find the table
const rulesTable = readme.find(
(token): token is marked.Tokens.Table => token.type === 'table',
);
if (!rulesTable) {
throw Error('Could not find the rules table in README.md');
}

return rulesTable;
}

describe('Validating rule docs', () => {
it('All rules must have a corresponding rule doc', () => {
const files = fs.readdirSync(docsRoot);
const ruleFiles = Object.keys(rules)
.map(rule => `${rule}.md`)
.sort();

expect(files.sort()).toEqual(ruleFiles);
});

for (const [ruleName, rule] of rulesData) {
const filePath = path.join(docsRoot, `${ruleName}.md`);
it(`Description of ${ruleName}.md must match`, () => {
// validate if description of rule is same as in docs
const file = fs.readFileSync(filePath, 'utf-8');
const tokens = marked.lexer(file, {
gfm: true,
silent: false,
});

// Rule title not found.
// Rule title does not match the rule metadata.
expect(tokens[0]).toEqual({
type: 'heading',
depth: 1,
text: `${rule.meta.docs.description} (\`${ruleName}\`)`,
});
});
}
});

describe('Validating rule metadata', () => {
for (const [ruleName, rule] of rulesData) {
describe(`${ruleName}`, () => {
it('`name` field in rule must match the filename', () => {
// validate if rule name is same as url
// there is no way to access this field but its used only in generation of docs url
expect(
rule.meta.docs.url.endsWith(`rules/${ruleName}.md`),
).toBeTruthy();
});

it('`requiresTypeChecking` should be set if the rule uses type information', () => {
// quick-and-dirty check to see if it uses parserServices
// not perfect but should be good enough
const ruleFileContents = fs.readFileSync(
path.resolve(__dirname, `../src/rules/${ruleName}.ts`),
);

expect(ruleFileContents.includes('getParserServices')).toEqual(
rule.meta.docs.requiresTypeChecking ?? false,
);
});
});
}
});

describe('Validating README.md', () => {
const rulesTable = parseReadme().cells;
const notDeprecated = rulesData.filter(
([, rule]) => rule.meta.deprecated !== true,
);

it('All non-deprecated rules should have a row in the table, and the table should be ordered alphabetically', () => {
const ruleNames = notDeprecated
.map(([ruleName]) => ruleName)
.sort()
.map(createRuleLink);

expect(rulesTable.map(row => row[0])).toStrictEqual(ruleNames);
});

for (const [ruleName, rule] of notDeprecated) {
describe(`Checking rule ${ruleName}`, () => {
const ruleRow =
rulesTable.find(row => row[0].includes(`/${ruleName}.md`)) ?? [];

it('Link column should be correct', () => {
expect(ruleRow[0]).toEqual(createRuleLink(ruleName));
});

it('Description column should be correct', () => {
expect(ruleRow[1]).toEqual(rule.meta.docs.description);
});

it('Recommended column should be correct', () => {
expect(ruleRow[2]).toEqual(
rule.meta.docs.recommended ? ':heavy_check_mark:' : '',
);
});

it('Fixable column should be correct', () => {
expect(ruleRow[3]).toEqual(
rule.meta.fixable !== undefined ? ':wrench:' : '',
);
});

it('Requiring type information column should be correct', () => {
expect(ruleRow[4]).toEqual(
rule.meta.docs.requiresTypeChecking === true
? ':thought_balloon:'
: '',
);
});
});
}
});
35 changes: 0 additions & 35 deletions packages/eslint-plugin/tools/validate-configs/checkConfigAll.ts

This file was deleted.

This file was deleted.

Loading