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 ;
@@ -671,3 +684,264 @@ describe('coerceInputValue', () => {
671684 } ) ;
672685 } ) ;
673686} ) ;
687+
688+ describe ( 'coerceInputLiteral' , ( ) => {
689+ function test (
690+ valueText : string ,
691+ type : GraphQLInputType ,
692+ expected : unknown ,
693+ variables ?: ObjMap < unknown > ,
694+ ) {
695+ const ast = parseValue ( valueText ) ;
696+ const value = coerceInputLiteral ( ast , type , variables ) ;
697+ expect ( value ) . to . deep . equal ( expected ) ;
698+ }
699+
700+ function testWithVariables (
701+ variables : ObjMap < unknown > ,
702+ valueText : string ,
703+ type : GraphQLInputType ,
704+ expected : unknown ,
705+ ) {
706+ test ( valueText , type , expected , variables ) ;
707+ }
708+
709+ it ( 'converts according to input coercion rules' , ( ) => {
710+ test ( 'true' , GraphQLBoolean , true ) ;
711+ test ( 'false' , GraphQLBoolean , false ) ;
712+ test ( '123' , GraphQLInt , 123 ) ;
713+ test ( '123' , GraphQLFloat , 123 ) ;
714+ test ( '123.456' , GraphQLFloat , 123.456 ) ;
715+ test ( '"abc123"' , GraphQLString , 'abc123' ) ;
716+ test ( '123456' , GraphQLID , '123456' ) ;
717+ test ( '"123456"' , GraphQLID , '123456' ) ;
718+ } ) ;
719+
720+ it ( 'does not convert when input coercion rules reject a value' , ( ) => {
721+ test ( '123' , GraphQLBoolean , undefined ) ;
722+ test ( '123.456' , GraphQLInt , undefined ) ;
723+ test ( 'true' , GraphQLInt , undefined ) ;
724+ test ( '"123"' , GraphQLInt , undefined ) ;
725+ test ( '"123"' , GraphQLFloat , undefined ) ;
726+ test ( '123' , GraphQLString , undefined ) ;
727+ test ( 'true' , GraphQLString , undefined ) ;
728+ test ( '123.456' , GraphQLString , undefined ) ;
729+ test ( '123.456' , GraphQLID , undefined ) ;
730+ } ) ;
731+
732+ it ( 'convert using parseLiteral from a custom scalar type' , ( ) => {
733+ const passthroughScalar = new GraphQLScalarType ( {
734+ name : 'PassthroughScalar' ,
735+ parseLiteral ( node ) {
736+ invariant ( node . kind === 'StringValue' ) ;
737+ return node . value ;
738+ } ,
739+ parseValue : identityFunc ,
740+ } ) ;
741+
742+ test ( '"value"' , passthroughScalar , 'value' ) ;
743+
744+ const printScalar = new GraphQLScalarType ( {
745+ name : 'PrintScalar' ,
746+ parseLiteral ( node ) {
747+ return `~~~${ print ( node ) } ~~~` ;
748+ } ,
749+ parseValue : identityFunc ,
750+ } ) ;
751+
752+ test ( '"value"' , printScalar , '~~~"value"~~~' ) ;
753+
754+ const throwScalar = new GraphQLScalarType ( {
755+ name : 'ThrowScalar' ,
756+ parseLiteral ( ) {
757+ throw new Error ( 'Test' ) ;
758+ } ,
759+ parseValue : identityFunc ,
760+ } ) ;
761+
762+ test ( 'value' , throwScalar , undefined ) ;
763+
764+ const returnUndefinedScalar = new GraphQLScalarType ( {
765+ name : 'ReturnUndefinedScalar' ,
766+ parseLiteral ( ) {
767+ return undefined ;
768+ } ,
769+ parseValue : identityFunc ,
770+ } ) ;
771+
772+ test ( 'value' , returnUndefinedScalar , undefined ) ;
773+ } ) ;
774+
775+ it ( 'converts enum values according to input coercion rules' , ( ) => {
776+ const testEnum = new GraphQLEnumType ( {
777+ name : 'TestColor' ,
778+ values : {
779+ RED : { value : 1 } ,
780+ GREEN : { value : 2 } ,
781+ BLUE : { value : 3 } ,
782+ NULL : { value : null } ,
783+ NAN : { value : NaN } ,
784+ NO_CUSTOM_VALUE : { value : undefined } ,
785+ } ,
786+ } ) ;
787+
788+ test ( 'RED' , testEnum , 1 ) ;
789+ test ( 'BLUE' , testEnum , 3 ) ;
790+ test ( '3' , testEnum , undefined ) ;
791+ test ( '"BLUE"' , testEnum , undefined ) ;
792+ test ( 'null' , testEnum , null ) ;
793+ test ( 'NULL' , testEnum , null ) ;
794+ test ( 'NULL' , new GraphQLNonNull ( testEnum ) , null ) ;
795+ test ( 'NAN' , testEnum , NaN ) ;
796+ test ( 'NO_CUSTOM_VALUE' , testEnum , 'NO_CUSTOM_VALUE' ) ;
797+ } ) ;
798+
799+ // Boolean!
800+ const nonNullBool = new GraphQLNonNull ( GraphQLBoolean ) ;
801+ // [Boolean]
802+ const listOfBool = new GraphQLList ( GraphQLBoolean ) ;
803+ // [Boolean!]
804+ const listOfNonNullBool = new GraphQLList ( nonNullBool ) ;
805+ // [Boolean]!
806+ const nonNullListOfBool = new GraphQLNonNull ( listOfBool ) ;
807+ // [Boolean!]!
808+ const nonNullListOfNonNullBool = new GraphQLNonNull ( listOfNonNullBool ) ;
809+
810+ it ( 'coerces to null unless non-null' , ( ) => {
811+ test ( 'null' , GraphQLBoolean , null ) ;
812+ test ( 'null' , nonNullBool , undefined ) ;
813+ } ) ;
814+
815+ it ( 'coerces lists of values' , ( ) => {
816+ test ( 'true' , listOfBool , [ true ] ) ;
817+ test ( '123' , listOfBool , undefined ) ;
818+ test ( 'null' , listOfBool , null ) ;
819+ test ( '[true, false]' , listOfBool , [ true , false ] ) ;
820+ test ( '[true, 123]' , listOfBool , undefined ) ;
821+ test ( '[true, null]' , listOfBool , [ true , null ] ) ;
822+ test ( '{ true: true }' , listOfBool , undefined ) ;
823+ } ) ;
824+
825+ it ( 'coerces non-null lists of values' , ( ) => {
826+ test ( 'true' , nonNullListOfBool , [ true ] ) ;
827+ test ( '123' , nonNullListOfBool , undefined ) ;
828+ test ( 'null' , nonNullListOfBool , undefined ) ;
829+ test ( '[true, false]' , nonNullListOfBool , [ true , false ] ) ;
830+ test ( '[true, 123]' , nonNullListOfBool , undefined ) ;
831+ test ( '[true, null]' , nonNullListOfBool , [ true , null ] ) ;
832+ } ) ;
833+
834+ it ( 'coerces lists of non-null values' , ( ) => {
835+ test ( 'true' , listOfNonNullBool , [ true ] ) ;
836+ test ( '123' , listOfNonNullBool , undefined ) ;
837+ test ( 'null' , listOfNonNullBool , null ) ;
838+ test ( '[true, false]' , listOfNonNullBool , [ true , false ] ) ;
839+ test ( '[true, 123]' , listOfNonNullBool , undefined ) ;
840+ test ( '[true, null]' , listOfNonNullBool , undefined ) ;
841+ } ) ;
842+
843+ it ( 'coerces non-null lists of non-null values' , ( ) => {
844+ test ( 'true' , nonNullListOfNonNullBool , [ true ] ) ;
845+ test ( '123' , nonNullListOfNonNullBool , undefined ) ;
846+ test ( 'null' , nonNullListOfNonNullBool , undefined ) ;
847+ test ( '[true, false]' , nonNullListOfNonNullBool , [ true , false ] ) ;
848+ test ( '[true, 123]' , nonNullListOfNonNullBool , undefined ) ;
849+ test ( '[true, null]' , nonNullListOfNonNullBool , undefined ) ;
850+ } ) ;
851+
852+ it ( 'uses default values for unprovided fields' , ( ) => {
853+ const type = new GraphQLInputObjectType ( {
854+ name : 'TestInput' ,
855+ fields : {
856+ int : { type : GraphQLInt , defaultValue : 42 } ,
857+ } ,
858+ } ) ;
859+
860+ test ( '{}' , type , { int : 42 } ) ;
861+ } ) ;
862+
863+ const testInputObj = new GraphQLInputObjectType ( {
864+ name : 'TestInput' ,
865+ fields : {
866+ int : { type : GraphQLInt , defaultValue : 42 } ,
867+ bool : { type : GraphQLBoolean } ,
868+ requiredBool : { type : nonNullBool } ,
869+ } ,
870+ } ) ;
871+ const testOneOfInputObj = new GraphQLInputObjectType ( {
872+ name : 'TestOneOfInput' ,
873+ fields : {
874+ a : { type : GraphQLString } ,
875+ b : { type : GraphQLString } ,
876+ } ,
877+ isOneOf : true ,
878+ } ) ;
879+
880+ it ( 'coerces input objects according to input coercion rules' , ( ) => {
881+ test ( 'null' , testInputObj , null ) ;
882+ test ( '123' , testInputObj , undefined ) ;
883+ test ( '[]' , testInputObj , undefined ) ;
884+ test ( '{ requiredBool: true }' , testInputObj , {
885+ int : 42 ,
886+ requiredBool : true ,
887+ } ) ;
888+ test ( '{ int: null, requiredBool: true }' , testInputObj , {
889+ int : null ,
890+ requiredBool : true ,
891+ } ) ;
892+ test ( '{ int: 123, requiredBool: false }' , testInputObj , {
893+ int : 123 ,
894+ requiredBool : false ,
895+ } ) ;
896+ test ( '{ bool: true, requiredBool: false }' , testInputObj , {
897+ int : 42 ,
898+ bool : true ,
899+ requiredBool : false ,
900+ } ) ;
901+ test ( '{ int: true, requiredBool: true }' , testInputObj , undefined ) ;
902+ test ( '{ requiredBool: null }' , testInputObj , undefined ) ;
903+ test ( '{ bool: true }' , testInputObj , undefined ) ;
904+ test ( '{ requiredBool: true, unknown: 123 }' , testInputObj , undefined ) ;
905+ test ( '{ a: "abc" }' , testOneOfInputObj , {
906+ a : 'abc' ,
907+ } ) ;
908+ test ( '{ b: "def" }' , testOneOfInputObj , {
909+ b : 'def' ,
910+ } ) ;
911+ test ( '{ a: "abc", b: null }' , testOneOfInputObj , undefined ) ;
912+ test ( '{ a: null }' , testOneOfInputObj , undefined ) ;
913+ test ( '{ a: 1 }' , testOneOfInputObj , undefined ) ;
914+ test ( '{ a: "abc", b: "def" }' , testOneOfInputObj , undefined ) ;
915+ test ( '{}' , testOneOfInputObj , undefined ) ;
916+ test ( '{ c: "abc" }' , testOneOfInputObj , undefined ) ;
917+ } ) ;
918+
919+ it ( 'accepts variable values assuming already coerced' , ( ) => {
920+ test ( '$var' , GraphQLBoolean , undefined ) ;
921+ testWithVariables ( { var : true } , '$var' , GraphQLBoolean , true ) ;
922+ testWithVariables ( { var : null } , '$var' , GraphQLBoolean , null ) ;
923+ testWithVariables ( { var : null } , '$var' , nonNullBool , undefined ) ;
924+ } ) ;
925+
926+ it ( 'asserts variables are provided as items in lists' , ( ) => {
927+ test ( '[ $foo ]' , listOfBool , [ null ] ) ;
928+ test ( '[ $foo ]' , listOfNonNullBool , undefined ) ;
929+ testWithVariables ( { foo : true } , '[ $foo ]' , listOfNonNullBool , [ true ] ) ;
930+ // Note: variables are expected to have already been coerced, so we
931+ // do not expect the singleton wrapping behavior for variables.
932+ testWithVariables ( { foo : true } , '$foo' , listOfNonNullBool , true ) ;
933+ testWithVariables ( { foo : [ true ] } , '$foo' , listOfNonNullBool , [ true ] ) ;
934+ } ) ;
935+
936+ it ( 'omits input object fields for unprovided variables' , ( ) => {
937+ test ( '{ int: $foo, bool: $foo, requiredBool: true }' , testInputObj , {
938+ int : 42 ,
939+ requiredBool : true ,
940+ } ) ;
941+ test ( '{ requiredBool: $foo }' , testInputObj , undefined ) ;
942+ testWithVariables ( { foo : true } , '{ requiredBool: $foo }' , testInputObj , {
943+ int : 42 ,
944+ requiredBool : true ,
945+ } ) ;
946+ } ) ;
947+ } ) ;
0 commit comments