diff --git a/.changeset/long-rockets-cheat.md b/.changeset/long-rockets-cheat.md new file mode 100644 index 00000000000..d6b4b5ebc37 --- /dev/null +++ b/.changeset/long-rockets-cheat.md @@ -0,0 +1,5 @@ +--- +'@graphql-eslint/eslint-plugin': patch +--- + +fix error report for `alphabetize` rule diff --git a/packages/plugin/src/rules/alphabetize.ts b/packages/plugin/src/rules/alphabetize.ts index e6c15778c8a..bad5acd3473 100644 --- a/packages/plugin/src/rules/alphabetize.ts +++ b/packages/plugin/src/rules/alphabetize.ts @@ -23,6 +23,7 @@ import { import { GraphQLESLintRule } from '../types'; import { GraphQLESTreeNode } from '../estree-parser'; import { GraphQLESLintRuleListener } from '../testkit'; +import { getLocation } from '../utils'; const ALPHABETIZE = 'ALPHABETIZE'; @@ -137,7 +138,7 @@ const rule: GraphQLESLintRule = { ], }, messages: { - [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".', + [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"', }, schema: { type: 'array', @@ -203,17 +204,10 @@ const rule: GraphQLESLintRule = { for (const node of nodes) { const currName = node.name.value; if (prevName && prevName > currName) { - const { start, end } = node.name.loc; const isVariableNode = node.kind === Kind.VARIABLE; context.report({ - loc: { - start: { - line: start.line, - column: start.column - (isVariableNode ? 2 : 1), - }, - end, - }, + loc: getLocation(node.loc, node.name.value, { offsetEnd: isVariableNode ? 0 : 1 }), messageId: ALPHABETIZE, data: isVariableNode ? { diff --git a/packages/plugin/src/testkit.ts b/packages/plugin/src/testkit.ts index 49ba391cf77..cd60e2da74d 100644 --- a/packages/plugin/src/testkit.ts +++ b/packages/plugin/src/testkit.ts @@ -11,6 +11,7 @@ export type GraphQLESLintRuleListener = { } & Record; export type GraphQLValidTestCase = Omit & { + name: string; options?: Options; parserOptions?: ParserOptions; }; @@ -50,7 +51,26 @@ export class GraphQLRuleTester extends RuleTester { invalid: GraphQLInvalidTestCase[]; } ): void { - super.run(name, rule as Rule.RuleModule, tests); + const ruleTests = Linter.version.startsWith('8') + ? tests + : { + valid: tests.valid.map(test => { + if (typeof test === 'string') { + return test; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { name, ...testCaseOptions } = test; + return testCaseOptions; + }), + invalid: tests.invalid.map(test => { + // ESLint 7 throws an error on CI - Unexpected top-level property "name" + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { name, ...testCaseOptions } = test; + return testCaseOptions; + }), + }; + + super.run(name, rule as Rule.RuleModule, ruleTests); // Skip snapshot testing if `expect` variable is not defined if (typeof expect === 'undefined') { diff --git a/packages/plugin/tests/__snapshots__/alphabetize.spec.ts.snap b/packages/plugin/tests/__snapshots__/alphabetize.spec.ts.snap index c0e8205e432..26b03a47145 100644 --- a/packages/plugin/tests/__snapshots__/alphabetize.spec.ts.snap +++ b/packages/plugin/tests/__snapshots__/alphabetize.spec.ts.snap @@ -5,7 +5,7 @@ exports[` 1`] = ` 2 | type User { 3 | password: String > 4 | firstName: String! - | ^ "firstName" should be before "password". + | ^^^^^^^^^ "firstName" should be before "password" 5 | age: Int 6 | lastName: String! 7 | } @@ -18,7 +18,7 @@ exports[` 2`] = ` 3 | password: String 4 | firstName: String! > 5 | age: Int - | ^ "age" should be before "firstName". + | ^^^ "age" should be before "firstName" 6 | lastName: String! 7 | } 8 | @@ -31,7 +31,7 @@ exports[` 3`] = ` 4 | firstName: String! 5 | password: String > 6 | lastName: String! - | ^ "lastName" should be before "password". + | ^^^^^^^^ "lastName" should be before "password" 7 | } 8 | `; @@ -39,10 +39,10 @@ exports[` 3`] = ` exports[` 4`] = ` 1 | 2 | interface Test { - 3 | c: Int -> 4 | b: Int - | ^ "b" should be before "c". - 5 | a: Int + 3 | cc: Int +> 4 | bb: Int + | ^^ "bb" should be before "cc" + 5 | aa: Int 6 | } 7 | `; @@ -50,10 +50,10 @@ exports[` 4`] = ` exports[` 5`] = ` 1 | 2 | interface Test { - 3 | c: Int - 4 | b: Int -> 5 | a: Int - | ^ "a" should be before "b". + 3 | cc: Int + 4 | bb: Int +> 5 | aa: Int + | ^^ "aa" should be before "bb" 6 | } 7 | `; @@ -63,7 +63,7 @@ exports[` 6`] = ` 2 | input UserInput { 3 | password: String > 4 | firstName: String! - | ^ "firstName" should be before "password". + | ^^^^^^^^^ "firstName" should be before "password" 5 | age: Int 6 | lastName: String! 7 | } @@ -76,7 +76,7 @@ exports[` 7`] = ` 3 | password: String 4 | firstName: String! > 5 | age: Int - | ^ "age" should be before "firstName". + | ^^^ "age" should be before "firstName" 6 | lastName: String! 7 | } 8 | @@ -89,7 +89,7 @@ exports[` 8`] = ` 4 | firstName: String! 5 | password: String > 6 | lastName: String! - | ^ "lastName" should be before "password". + | ^^^^^^^^ "lastName" should be before "password" 7 | } 8 | `; @@ -99,7 +99,7 @@ exports[` 9`] = ` 2 | enum Role { 3 | SUPER_ADMIN > 4 | ADMIN - | ^ "ADMIN" should be before "SUPER_ADMIN". + | ^^^^^ "ADMIN" should be before "SUPER_ADMIN" 5 | USER 6 | GOD 7 | } @@ -113,7 +113,7 @@ exports[` 10`] = ` 4 | ADMIN 5 | USER > 6 | GOD - | ^ "GOD" should be before "USER". + | ^^^ "GOD" should be before "USER" 7 | } 8 | `; @@ -124,7 +124,7 @@ exports[` 11`] = ` 3 | ADMIN 4 | SUPER_ADMIN > 5 | GOD - | ^ "GOD" should be before "SUPER_ADMIN". + | ^^^ "GOD" should be before "SUPER_ADMIN" 6 | USER 7 | } 8 | @@ -132,23 +132,23 @@ exports[` 11`] = ` exports[` 12`] = ` 1 | -> 2 | directive @test(c: Int, b: Int, a: Int) on FIELD_DEFINITION - | ^ "b" should be before "c". +> 2 | directive @test(cc: Int, bb: Int, aa: Int) on FIELD_DEFINITION + | ^^ "bb" should be before "cc" 3 | `; exports[` 13`] = ` 1 | -> 2 | directive @test(c: Int, b: Int, a: Int) on FIELD_DEFINITION - | ^ "a" should be before "b". +> 2 | directive @test(cc: Int, bb: Int, aa: Int) on FIELD_DEFINITION + | ^^ "aa" should be before "bb" 3 | `; exports[` 14`] = ` 1 | 2 | type Query { -> 3 | test(c: Int, b: Int, a: Int): Int - | ^ "b" should be before "c". +> 3 | test(cc: Int, bb: Int, aa: Int): Int + | ^^ "bb" should be before "cc" 4 | } 5 | `; @@ -156,8 +156,8 @@ exports[` 14`] = ` exports[` 15`] = ` 1 | 2 | type Query { -> 3 | test(c: Int, b: Int, a: Int): Int - | ^ "a" should be before "b". +> 3 | test(cc: Int, bb: Int, aa: Int): Int + | ^^ "aa" should be before "bb" 4 | } 5 | `; @@ -165,10 +165,10 @@ exports[` 15`] = ` exports[` 16`] = ` 1 | 2 | fragment TestFields on Test { - 3 | c -> 4 | b - | ^ "b" should be before "c". - 5 | a + 3 | cc +> 4 | bb + | ^^ "bb" should be before "cc" + 5 | aa 6 | } 7 | `; @@ -176,10 +176,10 @@ exports[` 16`] = ` exports[` 17`] = ` 1 | 2 | fragment TestFields on Test { - 3 | c - 4 | b -> 5 | a - | ^ "a" should be before "b". + 3 | cc + 4 | bb +> 5 | aa + | ^^ "aa" should be before "bb" 6 | } 7 | `; @@ -188,14 +188,14 @@ exports[` 18`] = ` 1 | 2 | query { 3 | test { - 4 | c -> 5 | b - | ^ "b" should be before "c". - 6 | a + 4 | cc +> 5 | bb + | ^^ "bb" should be before "cc" + 6 | aa 7 | ... on Test { - 8 | cc - 9 | bb - 10 | aa + 8 | ccc + 9 | bbb + 10 | aaa 11 | } 12 | } 13 | } @@ -206,14 +206,14 @@ exports[` 19`] = ` 1 | 2 | query { 3 | test { - 4 | c - 5 | b -> 6 | a - | ^ "a" should be before "b". + 4 | cc + 5 | bb +> 6 | aa + | ^^ "aa" should be before "bb" 7 | ... on Test { - 8 | cc - 9 | bb - 10 | aa + 8 | ccc + 9 | bbb + 10 | aaa 11 | } 12 | } 13 | } @@ -224,14 +224,14 @@ exports[` 20`] = ` 1 | 2 | query { 3 | test { - 4 | c - 5 | b - 6 | a + 4 | cc + 5 | bb + 6 | aa 7 | ... on Test { - 8 | cc -> 9 | bb - | ^ "bb" should be before "cc". - 10 | aa + 8 | ccc +> 9 | bbb + | ^^^ "bbb" should be before "ccc" + 10 | aaa 11 | } 12 | } 13 | } @@ -242,14 +242,14 @@ exports[` 21`] = ` 1 | 2 | query { 3 | test { - 4 | c - 5 | b - 6 | a + 4 | cc + 5 | bb + 6 | aa 7 | ... on Test { - 8 | cc - 9 | bb -> 10 | aa - | ^ "aa" should be before "bb". + 8 | ccc + 9 | bbb +> 10 | aaa + | ^^^ "aaa" should be before "bbb" 11 | } 12 | } 13 | } @@ -258,9 +258,9 @@ exports[` 21`] = ` exports[` 22`] = ` 1 | -> 2 | mutation ($c: Int, $b: Int, $a: Int) { - | ^^ "$b" should be before "$c". - 3 | test(cc: $c, bb: $b, aa: $a) { +> 2 | mutation ($cc: Int, $bb: Int, $aa: Int) { + | ^^^ "$bb" should be before "$cc" + 3 | test(ccc: $cc, bbb: $bb, aaa: $aa) { 4 | something 5 | } 6 | } @@ -269,9 +269,9 @@ exports[` 22`] = ` exports[` 23`] = ` 1 | -> 2 | mutation ($c: Int, $b: Int, $a: Int) { - | ^^ "$a" should be before "$b". - 3 | test(cc: $c, bb: $b, aa: $a) { +> 2 | mutation ($cc: Int, $bb: Int, $aa: Int) { + | ^^^ "$aa" should be before "$bb" + 3 | test(ccc: $cc, bbb: $bb, aaa: $aa) { 4 | something 5 | } 6 | } @@ -280,9 +280,9 @@ exports[` 23`] = ` exports[` 24`] = ` 1 | - 2 | mutation ($c: Int, $b: Int, $a: Int) { -> 3 | test(cc: $c, bb: $b, aa: $a) { - | ^ "bb" should be before "cc". + 2 | mutation ($cc: Int, $bb: Int, $aa: Int) { +> 3 | test(ccc: $cc, bbb: $bb, aaa: $aa) { + | ^^^ "bbb" should be before "ccc" 4 | something 5 | } 6 | } @@ -291,9 +291,9 @@ exports[` 24`] = ` exports[` 25`] = ` 1 | - 2 | mutation ($c: Int, $b: Int, $a: Int) { -> 3 | test(cc: $c, bb: $b, aa: $a) { - | ^ "aa" should be before "bb". + 2 | mutation ($cc: Int, $bb: Int, $aa: Int) { +> 3 | test(ccc: $cc, bbb: $bb, aaa: $aa) { + | ^^^ "aaa" should be before "bbb" 4 | something 5 | } 6 | } diff --git a/packages/plugin/tests/alphabetize.spec.ts b/packages/plugin/tests/alphabetize.spec.ts index bfe1ce70954..9871c33c301 100644 --- a/packages/plugin/tests/alphabetize.spec.ts +++ b/packages/plugin/tests/alphabetize.spec.ts @@ -40,8 +40,8 @@ ruleTester.runGraphQLTests('alphabetize', rule, { `, }, { + name: 'should not report error if selection is duplicated', options: [{ selections: ['OperationDefinition'] }], - // should not report error if selection is duplicated code: /* GraphQL */ ` query { test { @@ -65,8 +65,8 @@ ruleTester.runGraphQLTests('alphabetize', rule, { } `, errors: [ - { message: '"firstName" should be before "password".' }, - { message: '"age" should be before "firstName".' }, + { message: '"firstName" should be before "password"' }, + { message: '"age" should be before "firstName"' }, ], }, { @@ -79,18 +79,18 @@ ruleTester.runGraphQLTests('alphabetize', rule, { lastName: String! } `, - errors: [{ message: '"lastName" should be before "password".' }], + errors: [{ message: '"lastName" should be before "password"' }], }, { options: [{ fields: ['InterfaceTypeDefinition'] }], code: /* GraphQL */ ` interface Test { - c: Int - b: Int - a: Int + cc: Int + bb: Int + aa: Int } `, - errors: [{ message: '"b" should be before "c".' }, { message: '"a" should be before "b".' }], + errors: [{ message: '"bb" should be before "cc"' }, { message: '"aa" should be before "bb"' }], }, { options: [{ fields: ['InputObjectTypeDefinition'] }], @@ -103,8 +103,8 @@ ruleTester.runGraphQLTests('alphabetize', rule, { } `, errors: [ - { message: '"firstName" should be before "password".' }, - { message: '"age" should be before "firstName".' }, + { message: '"firstName" should be before "password"' }, + { message: '"age" should be before "firstName"' }, ], }, { @@ -117,7 +117,7 @@ ruleTester.runGraphQLTests('alphabetize', rule, { lastName: String! } `, - errors: [{ message: '"lastName" should be before "password".' }], + errors: [{ message: '"lastName" should be before "password"' }], }, { options: [{ values: ['EnumTypeDefinition'] }], @@ -129,7 +129,7 @@ ruleTester.runGraphQLTests('alphabetize', rule, { GOD } `, - errors: [{ message: '"ADMIN" should be before "SUPER_ADMIN".' }, { message: '"GOD" should be before "USER".' }], + errors: [{ message: '"ADMIN" should be before "SUPER_ADMIN"' }, { message: '"GOD" should be before "USER"' }], }, { options: [{ values: ['EnumTypeDefinition'] }], @@ -141,72 +141,72 @@ ruleTester.runGraphQLTests('alphabetize', rule, { USER } `, - errors: [{ message: '"GOD" should be before "SUPER_ADMIN".' }], + errors: [{ message: '"GOD" should be before "SUPER_ADMIN"' }], }, { options: [{ arguments: ['DirectiveDefinition'] }], code: /* GraphQL */ ` - directive @test(c: Int, b: Int, a: Int) on FIELD_DEFINITION + directive @test(cc: Int, bb: Int, aa: Int) on FIELD_DEFINITION `, - errors: [{ message: '"b" should be before "c".' }, { message: '"a" should be before "b".' }], + errors: [{ message: '"bb" should be before "cc"' }, { message: '"aa" should be before "bb"' }], }, { options: [{ arguments: ['FieldDefinition'] }], code: /* GraphQL */ ` type Query { - test(c: Int, b: Int, a: Int): Int + test(cc: Int, bb: Int, aa: Int): Int } `, - errors: [{ message: '"b" should be before "c".' }, { message: '"a" should be before "b".' }], + errors: [{ message: '"bb" should be before "cc"' }, { message: '"aa" should be before "bb"' }], }, { options: [{ selections: ['FragmentDefinition'] }], code: /* GraphQL */ ` fragment TestFields on Test { - c - b - a + cc + bb + aa } `, - errors: [{ message: '"b" should be before "c".' }, { message: '"a" should be before "b".' }], + errors: [{ message: '"bb" should be before "cc"' }, { message: '"aa" should be before "bb"' }], }, { options: [{ selections: ['OperationDefinition'] }], code: /* GraphQL */ ` query { test { - c - b - a + cc + bb + aa ... on Test { - cc - bb - aa + ccc + bbb + aaa } } } `, errors: [ - { message: '"b" should be before "c".' }, - { message: '"a" should be before "b".' }, - { message: '"bb" should be before "cc".' }, - { message: '"aa" should be before "bb".' }, + { message: '"bb" should be before "cc"' }, + { message: '"aa" should be before "bb"' }, + { message: '"bbb" should be before "ccc"' }, + { message: '"aaa" should be before "bbb"' }, ], }, { options: [{ variables: ['OperationDefinition'], arguments: ['Field'] }], code: /* GraphQL */ ` - mutation ($c: Int, $b: Int, $a: Int) { - test(cc: $c, bb: $b, aa: $a) { + mutation ($cc: Int, $bb: Int, $aa: Int) { + test(ccc: $cc, bbb: $bb, aaa: $aa) { something } } `, errors: [ - { message: '"$b" should be before "$c".' }, - { message: '"$a" should be before "$b".' }, - { message: '"bb" should be before "cc".' }, - { message: '"aa" should be before "bb".' }, + { message: '"$bb" should be before "$cc"' }, + { message: '"$aa" should be before "$bb"' }, + { message: '"bbb" should be before "ccc"' }, + { message: '"aaa" should be before "bbb"' }, ], }, ],