Skip to content

Commit 290f721

Browse files
committed
WIP: Move schema validation into separate step (type constructors)
This is the second step of moving work from type constructors to the schema validation function.
1 parent 7e147a8 commit 290f721

9 files changed

+1324
-1143
lines changed

src/type/__tests__/definition-test.js

Lines changed: 289 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ describe('Type System: Example', () => {
270270
expect(schema.getTypeMap().NestedInputObject).to.equal(NestedInputObject);
271271
});
272272

273-
it("includes interfaces' subtypes in the type map", () => {
273+
it('includes interface possible types in the type map', () => {
274274
const SomeInterface = new GraphQLInterfaceType({
275275
name: 'SomeInterface',
276276
fields: {
@@ -374,26 +374,13 @@ describe('Type System: Example', () => {
374374
});
375375
});
376376

377-
it('prohibits putting non-Object types in unions', () => {
378-
const badUnionTypes = [
379-
GraphQLInt,
380-
GraphQLNonNull(GraphQLInt),
381-
GraphQLList(GraphQLInt),
382-
InterfaceType,
383-
UnionType,
384-
EnumType,
385-
InputObjectType,
386-
];
387-
badUnionTypes.forEach(x => {
388-
expect(() =>
389-
new GraphQLUnionType({ name: 'BadUnion', types: [x] }).getTypes(),
390-
).to.throw(
391-
`BadUnion may only contain Object types, it cannot contain: ${x}.`,
392-
);
393-
});
377+
it('prohibits nesting NonNull inside NonNull', () => {
378+
expect(() => GraphQLNonNull(GraphQLNonNull(GraphQLInt))).to.throw(
379+
'Expected Int! to be a GraphQL nullable type.',
380+
);
394381
});
395382

396-
it("allows a thunk for Union's types", () => {
383+
it('allows a thunk for Union member types', () => {
397384
const union = new GraphQLUnionType({
398385
name: 'ThunkUnion',
399386
types: () => [ObjectType],
@@ -470,6 +457,289 @@ describe('Type System: Example', () => {
470457
});
471458
});
472459

460+
describe('Field config must be object', () => {
461+
it('accepts an Object type with a field function', () => {
462+
const objType = new GraphQLObjectType({
463+
name: 'SomeObject',
464+
fields() {
465+
return {
466+
f: { type: GraphQLString },
467+
};
468+
},
469+
});
470+
expect(objType.getFields().f.type).to.equal(GraphQLString);
471+
});
472+
473+
it('rejects an Object type field with undefined config', () => {
474+
const objType = new GraphQLObjectType({
475+
name: 'SomeObject',
476+
fields: {
477+
f: undefined,
478+
},
479+
});
480+
expect(() => objType.getFields()).to.throw(
481+
'SomeObject.f field config must be an object',
482+
);
483+
});
484+
485+
it('rejects an Object type with incorrectly typed fields', () => {
486+
const objType = new GraphQLObjectType({
487+
name: 'SomeObject',
488+
fields: [{ field: GraphQLString }],
489+
});
490+
expect(() => objType.getFields()).to.throw(
491+
'SomeObject fields must be an object with field names as keys or a ' +
492+
'function which returns such an object.',
493+
);
494+
});
495+
496+
it('rejects an Object type with a field function that returns incorrect type', () => {
497+
const objType = new GraphQLObjectType({
498+
name: 'SomeObject',
499+
fields() {
500+
return [{ field: GraphQLString }];
501+
},
502+
});
503+
expect(() => objType.getFields()).to.throw(
504+
'SomeObject fields must be an object with field names as keys or a ' +
505+
'function which returns such an object.',
506+
);
507+
});
508+
});
509+
510+
describe('Field arg config must be object', () => {
511+
it('accepts an Object type with field args', () => {
512+
const objType = new GraphQLObjectType({
513+
name: 'SomeObject',
514+
fields: {
515+
goodField: {
516+
type: GraphQLString,
517+
args: {
518+
goodArg: { type: GraphQLString },
519+
},
520+
},
521+
},
522+
});
523+
expect(() => objType.getFields()).not.to.throw();
524+
});
525+
526+
it('rejects an Object type with incorrectly typed field args', () => {
527+
const objType = new GraphQLObjectType({
528+
name: 'SomeObject',
529+
fields: {
530+
badField: {
531+
type: GraphQLString,
532+
args: [{ badArg: GraphQLString }],
533+
},
534+
},
535+
});
536+
expect(() => objType.getFields()).to.throw(
537+
'SomeObject.badField args must be an object with argument names as keys.',
538+
);
539+
});
540+
});
541+
542+
describe('Object interfaces must be array', () => {
543+
it('accepts an Object type with array interfaces', () => {
544+
const objType = new GraphQLObjectType({
545+
name: 'SomeObject',
546+
interfaces: [InterfaceType],
547+
fields: { f: { type: GraphQLString } },
548+
});
549+
expect(objType.getInterfaces()[0]).to.equal(InterfaceType);
550+
});
551+
552+
it('accepts an Object type with interfaces as a function returning an array', () => {
553+
const objType = new GraphQLObjectType({
554+
name: 'SomeObject',
555+
interfaces: () => [InterfaceType],
556+
fields: { f: { type: GraphQLString } },
557+
});
558+
expect(objType.getInterfaces()[0]).to.equal(InterfaceType);
559+
});
560+
561+
it('rejects an Object type with incorrectly typed interfaces', () => {
562+
const objType = new GraphQLObjectType({
563+
name: 'SomeObject',
564+
interfaces: {},
565+
fields: { f: { type: GraphQLString } },
566+
});
567+
expect(() => objType.getInterfaces()).to.throw(
568+
'SomeObject interfaces must be an Array or a function which returns an Array.',
569+
);
570+
});
571+
572+
it('rejects an Object type with interfaces as a function returning an incorrect type', () => {
573+
const objType = new GraphQLObjectType({
574+
name: 'SomeObject',
575+
interfaces() {
576+
return {};
577+
},
578+
fields: { f: { type: GraphQLString } },
579+
});
580+
expect(() => objType.getInterfaces()).to.throw(
581+
'SomeObject interfaces must be an Array or a function which returns an Array.',
582+
);
583+
});
584+
});
585+
586+
describe('Type System: Input Objects must have fields', () => {
587+
it('accepts an Input Object type with fields', () => {
588+
const inputObjType = new GraphQLInputObjectType({
589+
name: 'SomeInputObject',
590+
fields: {
591+
f: { type: GraphQLString },
592+
},
593+
});
594+
expect(inputObjType.getFields().f.type).to.equal(GraphQLString);
595+
});
596+
597+
it('accepts an Input Object type with a field function', () => {
598+
const inputObjType = new GraphQLInputObjectType({
599+
name: 'SomeInputObject',
600+
fields() {
601+
return {
602+
f: { type: GraphQLString },
603+
};
604+
},
605+
});
606+
expect(inputObjType.getFields().f.type).to.equal(GraphQLString);
607+
});
608+
609+
it('rejects an Input Object type with incorrect fields', () => {
610+
const inputObjType = new GraphQLInputObjectType({
611+
name: 'SomeInputObject',
612+
fields: [],
613+
});
614+
expect(() => inputObjType.getFields()).to.throw(
615+
'SomeInputObject fields must be an object with field names as keys or a ' +
616+
'function which returns such an object.',
617+
);
618+
});
619+
620+
it('rejects an Input Object type with fields function that returns incorrect type', () => {
621+
const inputObjType = new GraphQLInputObjectType({
622+
name: 'SomeInputObject',
623+
fields() {
624+
return [];
625+
},
626+
});
627+
expect(() => inputObjType.getFields()).to.throw(
628+
'SomeInputObject fields must be an object with field names as keys or a ' +
629+
'function which returns such an object.',
630+
);
631+
});
632+
});
633+
634+
describe('Type System: Input Object fields must not have resolvers', () => {
635+
it('rejects an Input Object type with resolvers', () => {
636+
const inputObjType = new GraphQLInputObjectType({
637+
name: 'SomeInputObject',
638+
fields: {
639+
f: {
640+
type: GraphQLString,
641+
resolve: () => {
642+
return 0;
643+
},
644+
},
645+
},
646+
});
647+
expect(() => inputObjType.getFields()).to.throw(
648+
'SomeInputObject.f field type has a resolve property, ' +
649+
'but Input Types cannot define resolvers.',
650+
);
651+
});
652+
653+
it('rejects an Input Object type with resolver constant', () => {
654+
const inputObjType = new GraphQLInputObjectType({
655+
name: 'SomeInputObject',
656+
fields: {
657+
f: {
658+
type: GraphQLString,
659+
resolve: {},
660+
},
661+
},
662+
});
663+
expect(() => inputObjType.getFields()).to.throw(
664+
'SomeInputObject.f field type has a resolve property, ' +
665+
'but Input Types cannot define resolvers.',
666+
);
667+
});
668+
});
669+
670+
describe('Type System: Enum types must be well defined', () => {
671+
it('accepts a well defined Enum type with empty value definition', () => {
672+
const enumType = new GraphQLEnumType({
673+
name: 'SomeEnum',
674+
values: {
675+
FOO: {},
676+
BAR: {},
677+
},
678+
});
679+
expect(enumType.getValue('FOO').value).to.equal('FOO');
680+
expect(enumType.getValue('BAR').value).to.equal('BAR');
681+
});
682+
683+
it('accepts a well defined Enum type with internal value definition', () => {
684+
const enumType = new GraphQLEnumType({
685+
name: 'SomeEnum',
686+
values: {
687+
FOO: { value: 10 },
688+
BAR: { value: 20 },
689+
},
690+
});
691+
expect(enumType.getValue('FOO').value).to.equal(10);
692+
expect(enumType.getValue('BAR').value).to.equal(20);
693+
});
694+
695+
it('rejects an Enum type with incorrectly typed values', () => {
696+
const enumType = new GraphQLEnumType({
697+
name: 'SomeEnum',
698+
values: [{ FOO: 10 }],
699+
});
700+
expect(() => enumType.getValue()).to.throw(
701+
'SomeEnum values must be an object with value names as keys.',
702+
);
703+
});
704+
705+
it('rejects an Enum type with missing value definition', () => {
706+
const enumType = new GraphQLEnumType({
707+
name: 'SomeEnum',
708+
values: { FOO: null },
709+
});
710+
expect(() => enumType.getValues()).to.throw(
711+
'SomeEnum.FOO must refer to an object with a "value" key representing ' +
712+
'an internal value but got: null.',
713+
);
714+
});
715+
716+
it('rejects an Enum type with incorrectly typed value definition', () => {
717+
const enumType = new GraphQLEnumType({
718+
name: 'SomeEnum',
719+
values: { FOO: 10 },
720+
});
721+
expect(() => enumType.getValues()).to.throw(
722+
'SomeEnum.FOO must refer to an object with a "value" key representing ' +
723+
'an internal value but got: 10.',
724+
);
725+
});
726+
727+
it('does not allow isDeprecated without deprecationReason on enum', () => {
728+
const enumType = new GraphQLEnumType({
729+
name: 'SomeEnum',
730+
values: {
731+
FOO: {
732+
isDeprecated: true,
733+
},
734+
},
735+
});
736+
expect(() => enumType.getValues()).to.throw(
737+
'SomeEnum.FOO should provide "deprecationReason" instead ' +
738+
'of "isDeprecated".',
739+
);
740+
});
741+
});
742+
473743
describe('Type System: List must accept only types', () => {
474744
const types = [
475745
GraphQLString,

0 commit comments

Comments
 (0)