11import { expect } from 'chai' ;
22import { describe , it } from 'mocha' ;
33
4+ import { identityFunc } from '../../jsutils/identityFunc.js' ;
5+ import { invariant } from '../../jsutils/invariant.js' ;
6+ import type { ObjMap } from '../../jsutils/ObjMap.js' ;
7+
8+ import { parseValue } from '../../language/parser.js' ;
9+ import { print } from '../../language/printer.js' ;
10+
411import type { GraphQLInputType } from '../../type/definition.js' ;
512import {
613 GraphQLEnumType ,
@@ -9,9 +16,15 @@ import {
916 GraphQLNonNull ,
1017 GraphQLScalarType ,
1118} from '../../type/definition.js' ;
12- import { GraphQLInt } from '../../type/scalars.js' ;
19+ import {
20+ GraphQLBoolean ,
21+ GraphQLFloat ,
22+ GraphQLID ,
23+ GraphQLInt ,
24+ GraphQLString ,
25+ } from '../../type/scalars.js' ;
1326
14- import { coerceInputValue } from '../coerceInputValue.js' ;
27+ import { coerceInputLiteral , coerceInputValue } from '../coerceInputValue.js' ;
1528
1629interface CoerceResult {
1730 value : unknown ;
@@ -566,3 +579,264 @@ describe('coerceInputValue', () => {
566579 } ) ;
567580 } ) ;
568581} ) ;
582+
583+ describe ( 'coerceInputLiteral' , ( ) => {
584+ function test (
585+ valueText : string ,
586+ type : GraphQLInputType ,
587+ expected : unknown ,
588+ variables ?: ObjMap < unknown > ,
589+ ) {
590+ const ast = parseValue ( valueText ) ;
591+ const value = coerceInputLiteral ( ast , type , variables ) ;
592+ expect ( value ) . to . deep . equal ( expected ) ;
593+ }
594+
595+ function testWithVariables (
596+ variables : ObjMap < unknown > ,
597+ valueText : string ,
598+ type : GraphQLInputType ,
599+ expected : unknown ,
600+ ) {
601+ test ( valueText , type , expected , variables ) ;
602+ }
603+
604+ it ( 'converts according to input coercion rules' , ( ) => {
605+ test ( 'true' , GraphQLBoolean , true ) ;
606+ test ( 'false' , GraphQLBoolean , false ) ;
607+ test ( '123' , GraphQLInt , 123 ) ;
608+ test ( '123' , GraphQLFloat , 123 ) ;
609+ test ( '123.456' , GraphQLFloat , 123.456 ) ;
610+ test ( '"abc123"' , GraphQLString , 'abc123' ) ;
611+ test ( '123456' , GraphQLID , '123456' ) ;
612+ test ( '"123456"' , GraphQLID , '123456' ) ;
613+ } ) ;
614+
615+ it ( 'does not convert when input coercion rules reject a value' , ( ) => {
616+ test ( '123' , GraphQLBoolean , undefined ) ;
617+ test ( '123.456' , GraphQLInt , undefined ) ;
618+ test ( 'true' , GraphQLInt , undefined ) ;
619+ test ( '"123"' , GraphQLInt , undefined ) ;
620+ test ( '"123"' , GraphQLFloat , undefined ) ;
621+ test ( '123' , GraphQLString , undefined ) ;
622+ test ( 'true' , GraphQLString , undefined ) ;
623+ test ( '123.456' , GraphQLString , undefined ) ;
624+ test ( '123.456' , GraphQLID , undefined ) ;
625+ } ) ;
626+
627+ it ( 'convert using parseLiteral from a custom scalar type' , ( ) => {
628+ const passthroughScalar = new GraphQLScalarType ( {
629+ name : 'PassthroughScalar' ,
630+ parseLiteral ( node ) {
631+ invariant ( node . kind === 'StringValue' ) ;
632+ return node . value ;
633+ } ,
634+ parseValue : identityFunc ,
635+ } ) ;
636+
637+ test ( '"value"' , passthroughScalar , 'value' ) ;
638+
639+ const printScalar = new GraphQLScalarType ( {
640+ name : 'PrintScalar' ,
641+ parseLiteral ( node ) {
642+ return `~~~${ print ( node ) } ~~~` ;
643+ } ,
644+ parseValue : identityFunc ,
645+ } ) ;
646+
647+ test ( '"value"' , printScalar , '~~~"value"~~~' ) ;
648+
649+ const throwScalar = new GraphQLScalarType ( {
650+ name : 'ThrowScalar' ,
651+ parseLiteral ( ) {
652+ throw new Error ( 'Test' ) ;
653+ } ,
654+ parseValue : identityFunc ,
655+ } ) ;
656+
657+ test ( 'value' , throwScalar , undefined ) ;
658+
659+ const returnUndefinedScalar = new GraphQLScalarType ( {
660+ name : 'ReturnUndefinedScalar' ,
661+ parseLiteral ( ) {
662+ return undefined ;
663+ } ,
664+ parseValue : identityFunc ,
665+ } ) ;
666+
667+ test ( 'value' , returnUndefinedScalar , undefined ) ;
668+ } ) ;
669+
670+ it ( 'converts enum values according to input coercion rules' , ( ) => {
671+ const testEnum = new GraphQLEnumType ( {
672+ name : 'TestColor' ,
673+ values : {
674+ RED : { value : 1 } ,
675+ GREEN : { value : 2 } ,
676+ BLUE : { value : 3 } ,
677+ NULL : { value : null } ,
678+ NAN : { value : NaN } ,
679+ NO_CUSTOM_VALUE : { value : undefined } ,
680+ } ,
681+ } ) ;
682+
683+ test ( 'RED' , testEnum , 1 ) ;
684+ test ( 'BLUE' , testEnum , 3 ) ;
685+ test ( '3' , testEnum , undefined ) ;
686+ test ( '"BLUE"' , testEnum , undefined ) ;
687+ test ( 'null' , testEnum , null ) ;
688+ test ( 'NULL' , testEnum , null ) ;
689+ test ( 'NULL' , new GraphQLNonNull ( testEnum ) , null ) ;
690+ test ( 'NAN' , testEnum , NaN ) ;
691+ test ( 'NO_CUSTOM_VALUE' , testEnum , 'NO_CUSTOM_VALUE' ) ;
692+ } ) ;
693+
694+ // Boolean!
695+ const nonNullBool = new GraphQLNonNull ( GraphQLBoolean ) ;
696+ // [Boolean]
697+ const listOfBool = new GraphQLList ( GraphQLBoolean ) ;
698+ // [Boolean!]
699+ const listOfNonNullBool = new GraphQLList ( nonNullBool ) ;
700+ // [Boolean]!
701+ const nonNullListOfBool = new GraphQLNonNull ( listOfBool ) ;
702+ // [Boolean!]!
703+ const nonNullListOfNonNullBool = new GraphQLNonNull ( listOfNonNullBool ) ;
704+
705+ it ( 'coerces to null unless non-null' , ( ) => {
706+ test ( 'null' , GraphQLBoolean , null ) ;
707+ test ( 'null' , nonNullBool , undefined ) ;
708+ } ) ;
709+
710+ it ( 'coerces lists of values' , ( ) => {
711+ test ( 'true' , listOfBool , [ true ] ) ;
712+ test ( '123' , listOfBool , undefined ) ;
713+ test ( 'null' , listOfBool , null ) ;
714+ test ( '[true, false]' , listOfBool , [ true , false ] ) ;
715+ test ( '[true, 123]' , listOfBool , undefined ) ;
716+ test ( '[true, null]' , listOfBool , [ true , null ] ) ;
717+ test ( '{ true: true }' , listOfBool , undefined ) ;
718+ } ) ;
719+
720+ it ( 'coerces non-null lists of values' , ( ) => {
721+ test ( 'true' , nonNullListOfBool , [ true ] ) ;
722+ test ( '123' , nonNullListOfBool , undefined ) ;
723+ test ( 'null' , nonNullListOfBool , undefined ) ;
724+ test ( '[true, false]' , nonNullListOfBool , [ true , false ] ) ;
725+ test ( '[true, 123]' , nonNullListOfBool , undefined ) ;
726+ test ( '[true, null]' , nonNullListOfBool , [ true , null ] ) ;
727+ } ) ;
728+
729+ it ( 'coerces lists of non-null values' , ( ) => {
730+ test ( 'true' , listOfNonNullBool , [ true ] ) ;
731+ test ( '123' , listOfNonNullBool , undefined ) ;
732+ test ( 'null' , listOfNonNullBool , null ) ;
733+ test ( '[true, false]' , listOfNonNullBool , [ true , false ] ) ;
734+ test ( '[true, 123]' , listOfNonNullBool , undefined ) ;
735+ test ( '[true, null]' , listOfNonNullBool , undefined ) ;
736+ } ) ;
737+
738+ it ( 'coerces non-null lists of non-null values' , ( ) => {
739+ test ( 'true' , nonNullListOfNonNullBool , [ true ] ) ;
740+ test ( '123' , nonNullListOfNonNullBool , undefined ) ;
741+ test ( 'null' , nonNullListOfNonNullBool , undefined ) ;
742+ test ( '[true, false]' , nonNullListOfNonNullBool , [ true , false ] ) ;
743+ test ( '[true, 123]' , nonNullListOfNonNullBool , undefined ) ;
744+ test ( '[true, null]' , nonNullListOfNonNullBool , undefined ) ;
745+ } ) ;
746+
747+ it ( 'uses default values for unprovided fields' , ( ) => {
748+ const type = new GraphQLInputObjectType ( {
749+ name : 'TestInput' ,
750+ fields : {
751+ int : { type : GraphQLInt , defaultValue : 42 } ,
752+ } ,
753+ } ) ;
754+
755+ test ( '{}' , type , { int : 42 } ) ;
756+ } ) ;
757+
758+ const testInputObj = new GraphQLInputObjectType ( {
759+ name : 'TestInput' ,
760+ fields : {
761+ int : { type : GraphQLInt , defaultValue : 42 } ,
762+ bool : { type : GraphQLBoolean } ,
763+ requiredBool : { type : nonNullBool } ,
764+ } ,
765+ } ) ;
766+ const testOneOfInputObj = new GraphQLInputObjectType ( {
767+ name : 'TestOneOfInput' ,
768+ fields : {
769+ a : { type : GraphQLString } ,
770+ b : { type : GraphQLString } ,
771+ } ,
772+ isOneOf : true ,
773+ } ) ;
774+
775+ it ( 'coerces input objects according to input coercion rules' , ( ) => {
776+ test ( 'null' , testInputObj , null ) ;
777+ test ( '123' , testInputObj , undefined ) ;
778+ test ( '[]' , testInputObj , undefined ) ;
779+ test ( '{ requiredBool: true }' , testInputObj , {
780+ int : 42 ,
781+ requiredBool : true ,
782+ } ) ;
783+ test ( '{ int: null, requiredBool: true }' , testInputObj , {
784+ int : null ,
785+ requiredBool : true ,
786+ } ) ;
787+ test ( '{ int: 123, requiredBool: false }' , testInputObj , {
788+ int : 123 ,
789+ requiredBool : false ,
790+ } ) ;
791+ test ( '{ bool: true, requiredBool: false }' , testInputObj , {
792+ int : 42 ,
793+ bool : true ,
794+ requiredBool : false ,
795+ } ) ;
796+ test ( '{ int: true, requiredBool: true }' , testInputObj , undefined ) ;
797+ test ( '{ requiredBool: null }' , testInputObj , undefined ) ;
798+ test ( '{ bool: true }' , testInputObj , undefined ) ;
799+ test ( '{ requiredBool: true, unknown: 123 }' , testInputObj , undefined ) ;
800+ test ( '{ a: "abc" }' , testOneOfInputObj , {
801+ a : 'abc' ,
802+ } ) ;
803+ test ( '{ b: "def" }' , testOneOfInputObj , {
804+ b : 'def' ,
805+ } ) ;
806+ test ( '{ a: "abc", b: null }' , testOneOfInputObj , undefined ) ;
807+ test ( '{ a: null }' , testOneOfInputObj , undefined ) ;
808+ test ( '{ a: 1 }' , testOneOfInputObj , undefined ) ;
809+ test ( '{ a: "abc", b: "def" }' , testOneOfInputObj , undefined ) ;
810+ test ( '{}' , testOneOfInputObj , undefined ) ;
811+ test ( '{ c: "abc" }' , testOneOfInputObj , undefined ) ;
812+ } ) ;
813+
814+ it ( 'accepts variable values assuming already coerced' , ( ) => {
815+ test ( '$var' , GraphQLBoolean , undefined ) ;
816+ testWithVariables ( { var : true } , '$var' , GraphQLBoolean , true ) ;
817+ testWithVariables ( { var : null } , '$var' , GraphQLBoolean , null ) ;
818+ testWithVariables ( { var : null } , '$var' , nonNullBool , undefined ) ;
819+ } ) ;
820+
821+ it ( 'asserts variables are provided as items in lists' , ( ) => {
822+ test ( '[ $foo ]' , listOfBool , [ null ] ) ;
823+ test ( '[ $foo ]' , listOfNonNullBool , undefined ) ;
824+ testWithVariables ( { foo : true } , '[ $foo ]' , listOfNonNullBool , [ true ] ) ;
825+ // Note: variables are expected to have already been coerced, so we
826+ // do not expect the singleton wrapping behavior for variables.
827+ testWithVariables ( { foo : true } , '$foo' , listOfNonNullBool , true ) ;
828+ testWithVariables ( { foo : [ true ] } , '$foo' , listOfNonNullBool , [ true ] ) ;
829+ } ) ;
830+
831+ it ( 'omits input object fields for unprovided variables' , ( ) => {
832+ test ( '{ int: $foo, bool: $foo, requiredBool: true }' , testInputObj , {
833+ int : 42 ,
834+ requiredBool : true ,
835+ } ) ;
836+ test ( '{ requiredBool: $foo }' , testInputObj , undefined ) ;
837+ testWithVariables ( { foo : true } , '{ requiredBool: $foo }' , testInputObj , {
838+ int : 42 ,
839+ requiredBool : true ,
840+ } ) ;
841+ } ) ;
842+ } ) ;
0 commit comments