Skip to content

Commit b6a04a7

Browse files
committed
Fix tests and formatting
1 parent 1615f12 commit b6a04a7

File tree

5 files changed

+252
-50
lines changed

5 files changed

+252
-50
lines changed

src/language/__tests__/parser-test.ts

Lines changed: 223 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ describe('Parser', () => {
409409

410410
expectJSON(result).toDeepEqual({
411411
kind: Kind.DOCUMENT,
412-
loc: { start: 0, end: 43},
412+
loc: { start: 0, end: 43 },
413413
definitions: [
414414
{
415415
kind: Kind.OPERATION_DEFINITION,
@@ -729,7 +729,7 @@ describe('Parser', () => {
729729
});
730730
});
731731

732-
describe('operation and variable definition descriptions', () => {
732+
describe('operation and variable definition descriptions', () => {
733733
it('parses operation with description and variable descriptions', () => {
734734
const result = parse(dedent`
735735
"Operation description"
@@ -742,34 +742,158 @@ describe('Parser', () => {
742742
field(a: $a, b: $b)
743743
}
744744
`);
745+
745746
// Find the operation definition
746747
const opDef = result.definitions.find(
747748
(d) => d.kind === Kind.OPERATION_DEFINITION,
748749
);
749750
if (!opDef || opDef.kind !== Kind.OPERATION_DEFINITION) {
750751
throw new Error('No operation definition found');
751752
}
752-
expect(opDef.description?.value).to.equal('Operation description');
753-
expect(opDef.name?.value).to.equal('myQuery');
754-
expect(opDef.variableDefinitions?.[0].description?.value).to.equal(
755-
'Variable a description',
756-
);
757-
expect(opDef.variableDefinitions?.[0].description?.block).to.equal(false);
758-
expect(opDef.variableDefinitions?.[1].description?.value).to.equal(
759-
'Variable b\nmultiline description',
753+
754+
expectJSON(opDef).toDeepEqual({
755+
kind: Kind.OPERATION_DEFINITION,
756+
operation: 'query',
757+
description: {
758+
kind: Kind.STRING,
759+
value: 'Operation description',
760+
block: false,
761+
loc: { start: 0, end: 23 },
762+
},
763+
name: {
764+
kind: Kind.NAME,
765+
value: 'myQuery',
766+
loc: { start: 30, end: 37 },
767+
},
768+
variableDefinitions: [
769+
{
770+
kind: Kind.VARIABLE_DEFINITION,
771+
description: {
772+
kind: Kind.STRING,
773+
value: 'Variable a description',
774+
block: false,
775+
loc: { start: 41, end: 65 },
776+
},
777+
variable: {
778+
kind: Kind.VARIABLE,
779+
name: {
780+
kind: Kind.NAME,
781+
value: 'a',
782+
loc: { start: 69, end: 70 },
783+
},
784+
loc: { start: 68, end: 70 },
785+
},
786+
type: {
787+
kind: Kind.NAMED_TYPE,
788+
name: {
789+
kind: Kind.NAME,
790+
value: 'Int',
791+
loc: { start: 72, end: 75 },
792+
},
793+
loc: { start: 72, end: 75 },
794+
},
795+
defaultValue: undefined,
796+
directives: [],
797+
loc: { start: 41, end: 75 },
798+
},
799+
{
800+
kind: Kind.VARIABLE_DEFINITION,
801+
description: {
802+
kind: Kind.STRING,
803+
value: 'Variable b\nmultiline description',
804+
block: true,
805+
loc: { start: 79, end: 117 },
806+
},
807+
variable: {
808+
kind: Kind.VARIABLE,
809+
name: {
810+
kind: Kind.NAME,
811+
value: 'b',
812+
loc: { start: 121, end: 122 },
813+
},
814+
loc: { start: 120, end: 122 },
815+
},
816+
type: {
817+
kind: Kind.NAMED_TYPE,
818+
name: {
819+
kind: Kind.NAME,
820+
value: 'String',
821+
loc: { start: 124, end: 130 },
822+
},
823+
loc: { start: 124, end: 130 },
824+
},
825+
defaultValue: undefined,
826+
directives: [],
827+
loc: { start: 79, end: 130 },
828+
},
829+
],
830+
directives: [],
831+
selectionSet: {
832+
kind: Kind.SELECTION_SET,
833+
selections: [
834+
{
835+
kind: Kind.FIELD,
836+
alias: undefined,
837+
name: {
838+
kind: Kind.NAME,
839+
value: 'field',
840+
loc: { start: 137, end: 142 },
841+
},
842+
arguments: [
843+
{
844+
kind: Kind.ARGUMENT,
845+
name: {
846+
kind: Kind.NAME,
847+
value: 'a',
848+
loc: { start: 143, end: 144 },
849+
},
850+
value: {
851+
kind: Kind.VARIABLE,
852+
name: {
853+
kind: Kind.NAME,
854+
value: 'a',
855+
loc: { start: 147, end: 148 },
856+
},
857+
loc: { start: 146, end: 148 },
858+
},
859+
loc: { start: 143, end: 148 },
860+
},
861+
{
862+
kind: Kind.ARGUMENT,
863+
name: {
864+
kind: Kind.NAME,
865+
value: 'b',
866+
loc: { start: 150, end: 151 },
867+
},
868+
value: {
869+
kind: Kind.VARIABLE,
870+
name: {
871+
kind: Kind.NAME,
872+
value: 'b',
873+
loc: { start: 154, end: 155 },
874+
},
875+
loc: { start: 153, end: 155 },
876+
},
877+
loc: { start: 150, end: 155 },
878+
},
879+
],
880+
directives: [],
881+
selectionSet: undefined,
882+
loc: { start: 137, end: 156 },
883+
},
884+
],
885+
loc: { start: 133, end: 158 },
886+
},
887+
loc: { start: 0, end: 158 },
888+
});
889+
});
890+
891+
it('descriptions on a short-hand query produce a sensible error', () => {
892+
const input = `"""Invalid"""
893+
{ __typename }`;
894+
expect(() => parse(input)).to.throw(
895+
'Syntax Error: Unexpected description, descriptions are not supported on shorthand queries.',
760896
);
761-
expect(opDef.variableDefinitions?.[1].description?.block).to.equal(true);
762-
expect(opDef.variableDefinitions?.[0].variable.name.value).to.equal('a');
763-
expect(opDef.variableDefinitions?.[1].variable.name.value).to.equal('b');
764-
// Check type names safely
765-
const typeA = opDef.variableDefinitions?.[0].type;
766-
if (typeA && typeA.kind === Kind.NAMED_TYPE) {
767-
expect(typeA.name.value).to.equal('Int');
768-
}
769-
const typeB = opDef.variableDefinitions?.[1].type;
770-
if (typeB && typeB.kind === Kind.NAMED_TYPE) {
771-
expect(typeB.name.value).to.equal('String');
772-
}
773897
});
774898

775899
it('parses variable definition with description, default value, and directives', () => {
@@ -788,33 +912,96 @@ describe('Parser', () => {
788912
throw new Error('No operation definition found');
789913
}
790914
const varDef = opDef.variableDefinitions?.[0];
791-
expect(varDef?.description?.value).to.equal('desc');
792-
expect(varDef?.variable.name.value).to.equal('foo');
793-
if (varDef?.type.kind === Kind.NAMED_TYPE) {
794-
expect(varDef.type.name.value).to.equal('Int');
795-
}
796-
if (varDef?.defaultValue && 'value' in varDef.defaultValue) {
797-
expect(varDef.defaultValue.value).to.equal('42');
798-
}
799-
expect(varDef?.directives?.[0].name.value).to.equal('dir');
915+
expectJSON(varDef).toDeepEqual({
916+
kind: Kind.VARIABLE_DEFINITION,
917+
defaultValue: {
918+
kind: Kind.INT,
919+
value: '42',
920+
loc: { start: 31, end: 33 },
921+
},
922+
directives: [
923+
{
924+
arguments: [],
925+
kind: Kind.DIRECTIVE,
926+
name: {
927+
kind: Kind.NAME,
928+
value: 'dir',
929+
loc: { start: 35, end: 38 },
930+
},
931+
loc: { start: 34, end: 38 },
932+
},
933+
],
934+
description: {
935+
kind: Kind.STRING,
936+
value: 'desc',
937+
block: false,
938+
loc: { start: 10, end: 16 },
939+
},
940+
variable: {
941+
kind: Kind.VARIABLE,
942+
name: {
943+
kind: Kind.NAME,
944+
value: 'foo',
945+
loc: { start: 20, end: 23 },
946+
},
947+
loc: { start: 19, end: 23 },
948+
},
949+
type: {
950+
kind: Kind.NAMED_TYPE,
951+
name: {
952+
kind: Kind.NAME,
953+
value: 'Int',
954+
loc: { start: 25, end: 28 },
955+
},
956+
loc: { start: 25, end: 28 },
957+
},
958+
loc: { start: 10, end: 38 },
959+
});
800960
});
801961

802962
it('parses fragment with variable description (legacy)', () => {
803963
const result = parse('fragment Foo("desc" $foo: Int) on Bar { baz }', {
804964
allowLegacyFragmentVariables: true,
805965
});
966+
806967
const fragDef = result.definitions.find(
807968
(d) => d.kind === Kind.FRAGMENT_DEFINITION,
808969
);
809970
if (!fragDef || fragDef.kind !== Kind.FRAGMENT_DEFINITION) {
810971
throw new Error('No fragment definition found');
811972
}
812973
const varDef = fragDef.variableDefinitions?.[0];
813-
expect(varDef?.description?.value).to.equal('desc');
814-
expect(varDef?.variable.name.value).to.equal('foo');
815-
if (varDef?.type.kind === Kind.NAMED_TYPE) {
816-
expect(varDef.type.name.value).to.equal('Int');
817-
}
974+
975+
expectJSON(varDef).toDeepEqual({
976+
kind: Kind.VARIABLE_DEFINITION,
977+
description: {
978+
kind: Kind.STRING,
979+
value: 'desc',
980+
block: false,
981+
loc: { start: 13, end: 19 },
982+
},
983+
variable: {
984+
kind: Kind.VARIABLE,
985+
name: {
986+
kind: Kind.NAME,
987+
value: 'foo',
988+
loc: { start: 21, end: 24 },
989+
},
990+
loc: { start: 20, end: 24 },
991+
},
992+
type: {
993+
kind: Kind.NAMED_TYPE,
994+
name: {
995+
kind: Kind.NAME,
996+
value: 'Int',
997+
loc: { start: 26, end: 29 },
998+
},
999+
loc: { start: 26, end: 29 },
1000+
},
1001+
defaultValue: undefined,
1002+
directives: [],
1003+
loc: { start: 13, end: 29 },
1004+
});
8181005
});
8191006
});
8201007
});

src/language/__tests__/schema-parser-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ describe('Schema Parser', () => {
331331
}
332332
`).to.deep.equal({
333333
message:
334-
'Syntax Error: Unexpected description, descriptions are not supported on type extensions and shorthand queries.',
334+
'Syntax Error: Unexpected description, only GraphQL definitions support descriptions.',
335335
locations: [{ line: 2, column: 7 }],
336336
});
337337

@@ -353,7 +353,7 @@ describe('Schema Parser', () => {
353353
}
354354
`).to.deep.equal({
355355
message:
356-
'Syntax Error: Unexpected description, descriptions are not supported on type extensions and shorthand queries.',
356+
'Syntax Error: Unexpected description, only GraphQL definitions support descriptions.',
357357
locations: [{ line: 2, column: 7 }],
358358
});
359359

src/language/ast.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,13 @@ export const QueryDocumentKeys: {
204204
'directives',
205205
'selectionSet',
206206
],
207-
VariableDefinition: ['description', 'variable', 'type', 'defaultValue', 'directives'],
207+
VariableDefinition: [
208+
'description',
209+
'variable',
210+
'type',
211+
'defaultValue',
212+
'directives',
213+
],
208214
Variable: ['name'],
209215
SelectionSet: ['selections'],
210216
Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'],

src/language/parser.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ export class Parser {
268268
? this._lexer.lookahead()
269269
: this._lexer.token;
270270

271+
if (hasDescription && keywordToken.kind === TokenKind.BRACE_L) {
272+
throw syntaxError(
273+
this._lexer.source,
274+
this._lexer.token.start,
275+
'Unexpected description, descriptions are not supported on shorthand queries.',
276+
);
277+
}
271278
if (keywordToken.kind === TokenKind.NAME) {
272279
switch (keywordToken.value) {
273280
case 'schema':
@@ -301,7 +308,7 @@ export class Parser {
301308
throw syntaxError(
302309
this._lexer.source,
303310
this._lexer.token.start,
304-
'Unexpected description, descriptions are not supported on type extensions and shorthand queries.',
311+
'Unexpected description, only GraphQL definitions support descriptions.',
305312
);
306313
}
307314

@@ -551,7 +558,7 @@ export class Parser {
551558
}
552559
return this.node<FragmentDefinitionNode>(start, {
553560
kind: Kind.FRAGMENT_DEFINITION,
554-
description,
561+
description,
555562
name: this.parseFragmentName(),
556563
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
557564
directives: this.parseDirectives(false),

0 commit comments

Comments
 (0)