Skip to content

Commit 4e16ae3

Browse files
committed
Add tests
1 parent 83e7b16 commit 4e16ae3

13 files changed

+281
-21
lines changed

src/graphql.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import type { ExecutionResult } from './execution/types.js';
6161
export interface GraphQLArgs {
6262
schema: GraphQLSchema;
6363
source: string | Source;
64+
shouldProvideSuggestions?: boolean;
6465
rootValue?: unknown;
6566
contextValue?: unknown;
6667
variableValues?: Maybe<{ readonly [variable: string]: unknown }>;
@@ -101,6 +102,7 @@ function graphqlImpl(args: GraphQLArgs): PromiseOrValue<ExecutionResult> {
101102
operationName,
102103
fieldResolver,
103104
typeResolver,
105+
shouldProvideSuggestions,
104106
} = args;
105107

106108
// Validate Schema
@@ -118,7 +120,9 @@ function graphqlImpl(args: GraphQLArgs): PromiseOrValue<ExecutionResult> {
118120
}
119121

120122
// Validate
121-
const validationErrors = validate(schema, document);
123+
const validationErrors = validate(schema, document, undefined, {
124+
shouldProvideSuggestions: shouldProvideSuggestions ?? true,
125+
});
122126
if (validationErrors.length > 0) {
123127
return { errors: validationErrors };
124128
}
@@ -133,5 +137,6 @@ function graphqlImpl(args: GraphQLArgs): PromiseOrValue<ExecutionResult> {
133137
operationName,
134138
fieldResolver,
135139
typeResolver,
140+
shouldProvideSuggestions,
136141
});
137142
}

src/type/__tests__/enumType-test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,14 @@ const schema = new GraphQLSchema({
135135
function executeQuery(
136136
source: string,
137137
variableValues?: { readonly [variable: string]: unknown },
138+
shouldProvideSuggestions = true,
138139
) {
139-
return graphqlSync({ schema, source, variableValues });
140+
return graphqlSync({
141+
schema,
142+
source,
143+
variableValues,
144+
shouldProvideSuggestions,
145+
});
140146
}
141147

142148
describe('Type System: Enum Values', () => {
@@ -192,6 +198,23 @@ describe('Type System: Enum Values', () => {
192198
});
193199
});
194200

201+
it('does not accept values not in the enum (no suggestions)', () => {
202+
const result = executeQuery(
203+
'{ colorEnum(fromEnum: GREENISH) }',
204+
undefined,
205+
false,
206+
);
207+
208+
expectJSON(result).toDeepEqual({
209+
errors: [
210+
{
211+
message: 'Value "GREENISH" does not exist in "Color" enum.',
212+
locations: [{ line: 1, column: 23 }],
213+
},
214+
],
215+
});
216+
});
217+
195218
it('does not accept values with incorrect casing', () => {
196219
const result = executeQuery('{ colorEnum(fromEnum: green) }');
197220

src/utilities/__tests__/coerceInputValue-test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ interface CoerceError {
5050
function coerceValue(
5151
inputValue: unknown,
5252
type: GraphQLInputType,
53+
shouldProvideSuggestions: boolean = true,
5354
): CoerceResult {
5455
const errors: Array<CoerceError> = [];
5556
const value = coerceInputValue(
5657
inputValue,
5758
type,
58-
true,
59+
shouldProvideSuggestions,
5960
(path, invalidValue, error) => {
6061
errors.push({ path, value: invalidValue, error: error.message });
6162
},
@@ -184,6 +185,18 @@ describe('coerceInputValue', () => {
184185
]);
185186
});
186187

188+
it('returns an error for misspelled enum value (no suggestions)', () => {
189+
const result = coerceValue('foo', TestEnum, false);
190+
expectErrors(result).to.deep.equal([
191+
{
192+
error:
193+
'Value "foo" does not exist in "TestEnum" enum.',
194+
path: [],
195+
value: 'foo',
196+
},
197+
]);
198+
});
199+
187200
it('returns an error for incorrect value type', () => {
188201
const result1 = coerceValue(123, TestEnum);
189202
expectErrors(result1).to.deep.equal([
@@ -204,6 +217,27 @@ describe('coerceInputValue', () => {
204217
},
205218
]);
206219
});
220+
221+
it('returns an error for incorrect value type (no suggestions)', () => {
222+
const result1 = coerceValue(123, TestEnum, false);
223+
expectErrors(result1).to.deep.equal([
224+
{
225+
error: 'Enum "TestEnum" cannot represent non-string value: 123.',
226+
path: [],
227+
value: 123,
228+
},
229+
]);
230+
231+
const result2 = coerceValue({ field: 'value' }, TestEnum, false);
232+
expectErrors(result2).to.deep.equal([
233+
{
234+
error:
235+
'Enum "TestEnum" cannot represent non-string value: { field: "value" }.',
236+
path: [],
237+
value: { field: 'value' },
238+
},
239+
]);
240+
});
207241
});
208242

209243
describe('for GraphQLInputObject', () => {
@@ -401,6 +435,23 @@ describe('coerceInputValue', () => {
401435
},
402436
]);
403437
});
438+
439+
it('returns error for a misspelled field without suggestions', () => {
440+
const result = coerceValue({ bart: 123 }, TestInputObject, false);
441+
expectErrors(result).to.deep.equal([
442+
{
443+
error: 'Field "bart" is not defined by type "TestInputObject".',
444+
path: [],
445+
value: { bart: 123 },
446+
},
447+
{
448+
error:
449+
'Exactly one key must be specified for OneOf type "TestInputObject".',
450+
path: [],
451+
value: { bart: 123 },
452+
},
453+
]);
454+
});
404455
});
405456

406457
describe('for GraphQLInputObject with default value', () => {

src/utilities/extendSchema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ function getDeprecationReason(
717717
| FieldDefinitionNode
718718
| InputValueDefinitionNode,
719719
): Maybe<string> {
720-
const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
720+
const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node, true);
721721
// @ts-expect-error validated by `getDirectiveValues`
722722
return deprecated?.reason;
723723
}
@@ -728,7 +728,7 @@ function getDeprecationReason(
728728
function getSpecifiedByURL(
729729
node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode,
730730
): Maybe<string> {
731-
const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node);
731+
const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node, true);
732732
// @ts-expect-error validated by `getDirectiveValues`
733733
return specifiedBy?.url;
734734
}
@@ -737,5 +737,5 @@ function getSpecifiedByURL(
737737
* Given an input object node, returns if the node should be OneOf.
738738
*/
739739
function isOneOf(node: InputObjectTypeDefinitionNode): boolean {
740-
return Boolean(getDirectiveValues(GraphQLOneOfDirective, node));
740+
return Boolean(getDirectiveValues(GraphQLOneOfDirective, node, true));
741741
}

src/utilities/valueFromAST.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export function valueFromAST(
148148
// no value is returned.
149149
let result;
150150
try {
151-
result = type.parseLiteral(valueNode, variables);
151+
result = type.parseLiteral(valueNode, variables, true);
152152
} catch (_error) {
153153
return; // Invalid: intentionally return no value.
154154
}

src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import { validate } from '../validate.js';
1212

1313
import { expectValidationErrorsWithSchema } from './harness.js';
1414

15-
function expectErrors(queryStr: string) {
15+
function expectErrors(queryStr: string, shouldProvideSuggestions = true) {
1616
return expectValidationErrorsWithSchema(
1717
testSchema,
1818
FieldsOnCorrectTypeRule,
1919
queryStr,
20+
shouldProvideSuggestions,
2021
);
2122
}
2223

@@ -140,6 +141,22 @@ describe('Validate: Fields on correct type', () => {
140141
]);
141142
});
142143

144+
it('Field not defined on fragment (no suggestions)', () => {
145+
expectErrors(
146+
`
147+
fragment fieldNotDefined on Dog {
148+
meowVolume
149+
}
150+
`,
151+
false,
152+
).toDeepEqual([
153+
{
154+
message: 'Cannot query field "meowVolume" on type "Dog".',
155+
locations: [{ line: 3, column: 9 }],
156+
},
157+
]);
158+
});
159+
143160
it('Ignores deeply unknown field', () => {
144161
expectErrors(`
145162
fragment deepFieldNotDefined on Dog {

src/validation/__tests__/KnownArgumentNamesRule-test.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ import {
1414
expectValidationErrors,
1515
} from './harness.js';
1616

17-
function expectErrors(queryStr: string) {
18-
return expectValidationErrors(KnownArgumentNamesRule, queryStr);
17+
function expectErrors(queryStr: string, shouldProvideSuggestions = true) {
18+
return expectValidationErrors(
19+
KnownArgumentNamesRule,
20+
queryStr,
21+
shouldProvideSuggestions,
22+
);
1923
}
2024

2125
function expectValid(queryStr: string) {
@@ -161,6 +165,22 @@ describe('Validate: Known argument names', () => {
161165
]);
162166
});
163167

168+
it('misspelled directive args are reported (no suggestions)', () => {
169+
expectErrors(
170+
`
171+
{
172+
dog @skip(iff: true)
173+
}
174+
`,
175+
false,
176+
).toDeepEqual([
177+
{
178+
message: 'Unknown argument "iff" on directive "@skip".',
179+
locations: [{ line: 3, column: 19 }],
180+
},
181+
]);
182+
});
183+
164184
it('arg passed to fragment without arg is reported', () => {
165185
expectErrors(`
166186
{
@@ -198,6 +218,27 @@ describe('Validate: Known argument names', () => {
198218
]);
199219
});
200220

221+
it('misspelled fragment args are reported (no suggestions)', () => {
222+
expectErrors(
223+
`
224+
{
225+
dog {
226+
...withArg(command: SIT)
227+
}
228+
}
229+
fragment withArg($dogCommand: DogCommand) on Dog {
230+
doesKnowCommand(dogCommand: $dogCommand)
231+
}
232+
`,
233+
false,
234+
).toDeepEqual([
235+
{
236+
message: 'Unknown argument "command" on fragment "withArg".',
237+
locations: [{ line: 4, column: 22 }],
238+
},
239+
]);
240+
});
241+
201242
it('invalid arg name', () => {
202243
expectErrors(`
203244
fragment invalidArgName on Dog {
@@ -225,6 +266,23 @@ describe('Validate: Known argument names', () => {
225266
]);
226267
});
227268

269+
it('misspelled arg name is reported (no suggestions)', () => {
270+
expectErrors(
271+
`
272+
fragment invalidArgName on Dog {
273+
doesKnowCommand(DogCommand: true)
274+
}
275+
`,
276+
false,
277+
).toDeepEqual([
278+
{
279+
message:
280+
'Unknown argument "DogCommand" on field "Dog.doesKnowCommand".',
281+
locations: [{ line: 3, column: 25 }],
282+
},
283+
]);
284+
});
285+
228286
it('unknown args amongst known args', () => {
229287
expectErrors(`
230288
fragment oneGoodArgOneInvalidArg on Dog {

src/validation/__tests__/KnownTypeNamesRule-test.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import {
1212
expectValidationErrorsWithSchema,
1313
} from './harness.js';
1414

15-
function expectErrors(queryStr: string) {
16-
return expectValidationErrors(KnownTypeNamesRule, queryStr);
15+
function expectErrors(queryStr: string, shouldProvideSuggestions = true) {
16+
return expectValidationErrors(
17+
KnownTypeNamesRule,
18+
queryStr,
19+
shouldProvideSuggestions,
20+
);
1721
}
1822

1923
function expectErrorsWithSchema(schema: GraphQLSchema, queryStr: string) {
@@ -78,6 +82,36 @@ describe('Validate: Known type names', () => {
7882
]);
7983
});
8084

85+
it('unknown type names are invalid (no suggestions)', () => {
86+
expectErrors(
87+
`
88+
query Foo($var: [JumbledUpLetters!]!) {
89+
user(id: 4) {
90+
name
91+
pets { ... on Badger { name }, ...PetFields }
92+
}
93+
}
94+
fragment PetFields on Peat {
95+
name
96+
}
97+
`,
98+
false,
99+
).toDeepEqual([
100+
{
101+
message: 'Unknown type "JumbledUpLetters".',
102+
locations: [{ line: 2, column: 24 }],
103+
},
104+
{
105+
message: 'Unknown type "Badger".',
106+
locations: [{ line: 5, column: 25 }],
107+
},
108+
{
109+
message: 'Unknown type "Peat".',
110+
locations: [{ line: 8, column: 29 }],
111+
},
112+
]);
113+
});
114+
81115
it('references to standard scalars that are missing in schema', () => {
82116
const schema = buildSchema('type Query { foo: String }');
83117
const query = `

0 commit comments

Comments
 (0)