Skip to content

Commit

Permalink
feat: add snapshots for report location (#733)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimitri POSTOLOV authored Oct 30, 2021
1 parent 7fab078 commit bb4a9ab
Show file tree
Hide file tree
Showing 37 changed files with 32,474 additions and 242 deletions.
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ module.exports = {
...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
'@eslint/eslintrc/universal': '@eslint/eslintrc/dist/eslintrc-universal.cjs',
},
snapshotSerializers: ['jest-snapshot-serializer-raw/always'],
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"eslint-plugin-unicorn": "37.0.1",
"husky": "7.0.4",
"jest": "27.3.1",
"jest-snapshot-serializer-raw": "^1.2.0",
"json-schema-to-markdown": "1.1.1",
"lint-staged": "11.2.6",
"patch-package": "6.4.7",
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"lodash.lowercase": "^4.3.0"
},
"devDependencies": {
"@babel/code-frame": "^7.15.8",
"@types/babel__code-frame": "^7.0.3",
"@types/eslint": "7.28.2",
"@types/graphql-depth-limit": "1.1.3",
"@types/lodash.lowercase": "4.3.6",
Expand All @@ -48,6 +50,8 @@
"buildOptions": {
"input": "./src/index.ts",
"external": [
"@babel/code-frame",
"eslint",
"graphql",
"graphql/validation/rules/ExecutableDefinitionsRule",
"graphql/validation/rules/FieldsOnCorrectTypeRule",
Expand Down
6 changes: 4 additions & 2 deletions packages/plugin/src/rules/match-document-filename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ const rule: GraphQLESLintRule<MatchDocumentFilenameRuleConfig> = {
Document(documentNode) {
if (options.fileExtension && options.fileExtension !== fileExtension) {
context.report({
node: documentNode,
// Report on first character
loc: { column: 0, line: 1 },
messageId: MATCH_EXTENSION,
data: {
fileExtension,
Expand Down Expand Up @@ -215,7 +216,8 @@ const rule: GraphQLESLintRule<MatchDocumentFilenameRuleConfig> = {

if (expectedFilename !== filenameWithExtension) {
context.report({
node: documentNode,
// Report on first character
loc: { column: 0, line: 1 },
messageId: MATCH_STYLE,
data: {
expectedFilename,
Expand Down
106 changes: 98 additions & 8 deletions packages/plugin/src/testkit.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { RuleTester } from 'eslint';
import { readFileSync } from 'fs';
import { ASTKindToNode } from 'graphql';
import { resolve } from 'path';
import { RuleTester, AST, Linter, Rule } from 'eslint';
import { ASTKindToNode } from 'graphql';
import { codeFrameColumns } from '@babel/code-frame';
import { GraphQLESTreeNode } from './estree-parser';
import { GraphQLESLintRule, ParserOptions } from './types';

export type GraphQLESLintRuleListener<WithTypeInfo extends boolean = false> = {
[K in keyof ASTKindToNode]?: (node: GraphQLESTreeNode<ASTKindToNode[K], WithTypeInfo>) => void;
} &
Record<string, any>;
} & Record<string, any>;

export type GraphQLValidTestCase<Options> = Omit<RuleTester.ValidTestCase, 'options' | 'parserOptions'> & {
options?: Options;
Expand All @@ -20,15 +20,22 @@ export type GraphQLInvalidTestCase<T> = GraphQLValidTestCase<T> & {
output?: string | null;
};

export class GraphQLRuleTester extends require('eslint').RuleTester {
export class GraphQLRuleTester extends RuleTester {
config: {
parser: string;
parserOptions: ParserOptions;
};

constructor(parserOptions: ParserOptions = {}) {
super({
const config = {
parser: require.resolve('@graphql-eslint/eslint-plugin'),
parserOptions: {
...parserOptions,
skipGraphQLConfig: true,
},
});
};
super(config);
this.config = config;
}

fromMockFile(path: string): string {
Expand All @@ -43,6 +50,89 @@ export class GraphQLRuleTester extends require('eslint').RuleTester {
invalid: GraphQLInvalidTestCase<Config>[];
}
): void {
super.run(name, rule, tests);
super.run(name, rule as Rule.RuleModule, tests);

// Skip snapshot testing if `expect` variable is not defined
if (typeof expect === 'undefined') {
return;
}

const linter = new Linter();
linter.defineRule(name, rule as Rule.RuleModule);

for (const testCase of tests.invalid) {
const verifyConfig = getVerifyConfig(name, this.config, testCase);
defineParser(linter, verifyConfig.parser);

const { code, filename } = testCase;

const messages = linter.verify(code, verifyConfig, { filename });

for (const message of messages) {
if (message.fatal) {
throw new Error(message.message);
}

const messageForSnapshot = visualizeEslintMessage(code, message);
// eslint-disable-next-line no-undef
expect(messageForSnapshot).toMatchSnapshot();
}
}
}
}

function getVerifyConfig(ruleId: string, testerConfig, testCase) {
const { options, parserOptions, parser = testerConfig.parser } = testCase;

return {
...testerConfig,
parser,
parserOptions: {
...testerConfig.parserOptions,
...parserOptions,
},
rules: {
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
},
};
}

const parsers = new WeakMap();

function defineParser(linter: Linter, parser: string): void {
if (!parser) {
return;
}
if (!parsers.has(linter)) {
parsers.set(linter, new Set());
}

const defined = parsers.get(linter);
if (!defined.has(parser)) {
defined.add(parser);
linter.defineParser(parser, require(parser));
}
}

function visualizeEslintMessage(text: string, result: Linter.LintMessage): string {
const { line, column, endLine, endColumn, message } = result;
const location: Partial<AST.SourceLocation> = {
start: {
line,
column,
},
};

if (typeof endLine === 'number' && typeof endColumn === 'number') {
location.end = {
line: endLine,
column: endColumn,
};
}

return codeFrameColumns(text, location as AST.SourceLocation, {
linesAbove: Number.POSITIVE_INFINITY,
linesBelow: Number.POSITIVE_INFINITY,
message,
});
}
2 changes: 1 addition & 1 deletion packages/plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export type RuleDocsInfo<T> = Rule.RuleMetaData & {
usage?: T;
}[];
optionsForConfig?: T;
graphQLJSRuleName?: string
graphQLJSRuleName?: string;
};
};

Expand Down
Loading

0 comments on commit bb4a9ab

Please sign in to comment.