Skip to content

Commit 0254e5f

Browse files
authored
Cover empty field-selections (#4228)
Closes #3790 This follows the spec closer to the letter as we consider empty selection-sets invalid as well. This however does not cover empty operation-definitions, which should also be considered invalid. The parser itself considers empty-selections invalid as it requires you to specify a name before a closing curly, hence why testing happens a bit more manual here. Despite our parser not considering this valid, I think this is good to validate when folks use a separate parser or construct documents manually as this is valid in our type-system.
1 parent 89f23d1 commit 0254e5f

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

src/validation/__tests__/ScalarLeafsRule-test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { describe, it } from 'mocha';
22

3+
import { expectJSON } from '../../__testUtils__/expectJSON.js';
4+
5+
import type { DocumentNode } from '../../language/ast.js';
6+
import { OperationTypeNode } from '../../language/ast.js';
7+
import { Kind } from '../../language/kinds.js';
8+
39
import { ScalarLeafsRule } from '../rules/ScalarLeafsRule.js';
10+
import { validate } from '../validate.js';
411

5-
import { expectValidationErrors } from './harness.js';
12+
import { expectValidationErrors, testSchema } from './harness.js';
613

714
function expectErrors(queryStr: string) {
815
return expectValidationErrors(ScalarLeafsRule, queryStr);
@@ -35,6 +42,39 @@ describe('Validate: Scalar leafs', () => {
3542
]);
3643
});
3744

45+
it('object type having only one selection', () => {
46+
const doc: DocumentNode = {
47+
kind: Kind.DOCUMENT,
48+
definitions: [
49+
{
50+
kind: Kind.OPERATION_DEFINITION,
51+
operation: OperationTypeNode.QUERY,
52+
selectionSet: {
53+
kind: Kind.SELECTION_SET,
54+
selections: [
55+
{
56+
kind: Kind.FIELD,
57+
name: { kind: Kind.NAME, value: 'human' },
58+
selectionSet: { kind: Kind.SELECTION_SET, selections: [] },
59+
},
60+
],
61+
},
62+
},
63+
],
64+
};
65+
66+
// We can't leverage expectErrors since it doesn't support passing in the
67+
// documentNode directly. We have to do this because this is technically
68+
// an invalid document.
69+
const errors = validate(testSchema, doc, [ScalarLeafsRule]);
70+
expectJSON(errors).toDeepEqual([
71+
{
72+
message:
73+
'Field "human" of type "Human" must have at least one field selected.',
74+
},
75+
]);
76+
});
77+
3878
it('interface type missing selection', () => {
3979
expectErrors(`
4080
{

src/validation/rules/ScalarLeafsRule.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ export function ScalarLeafsRule(context: ValidationContext): ASTVisitor {
4141
{ nodes: node },
4242
),
4343
);
44+
} else if (selectionSet.selections.length === 0) {
45+
const fieldName = node.name.value;
46+
const typeStr = inspect(type);
47+
context.reportError(
48+
new GraphQLError(
49+
`Field "${fieldName}" of type "${typeStr}" must have at least one field selected.`,
50+
{ nodes: node },
51+
),
52+
);
4453
}
4554
}
4655
},

0 commit comments

Comments
 (0)