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 ;
@@ -533,3 +546,264 @@ describe('coerceInputValue', () => {
533546 } ) ;
534547 } ) ;
535548} ) ;
549+
550+ describe ( 'coerceInputLiteral' , ( ) => {
551+ function test (
552+ valueText : string ,
553+ type : GraphQLInputType ,
554+ expected : unknown ,
555+ variables ?: ObjMap < unknown > ,
556+ ) {
557+ const ast = parseValue ( valueText ) ;
558+ const value = coerceInputLiteral ( ast , type , variables ) ;
559+ expect ( value ) . to . deep . equal ( expected ) ;
560+ }
561+
562+ function testWithVariables (
563+ variables : ObjMap < unknown > ,
564+ valueText : string ,
565+ type : GraphQLInputType ,
566+ expected : unknown ,
567+ ) {
568+ test ( valueText , type , expected , variables ) ;
569+ }
570+
571+ it ( 'converts according to input coercion rules' , ( ) => {
572+ test ( 'true' , GraphQLBoolean , true ) ;
573+ test ( 'false' , GraphQLBoolean , false ) ;
574+ test ( '123' , GraphQLInt , 123 ) ;
575+ test ( '123' , GraphQLFloat , 123 ) ;
576+ test ( '123.456' , GraphQLFloat , 123.456 ) ;
577+ test ( '"abc123"' , GraphQLString , 'abc123' ) ;
578+ test ( '123456' , GraphQLID , '123456' ) ;
579+ test ( '"123456"' , GraphQLID , '123456' ) ;
580+ } ) ;
581+
582+ it ( 'does not convert when input coercion rules reject a value' , ( ) => {
583+ test ( '123' , GraphQLBoolean , undefined ) ;
584+ test ( '123.456' , GraphQLInt , undefined ) ;
585+ test ( 'true' , GraphQLInt , undefined ) ;
586+ test ( '"123"' , GraphQLInt , undefined ) ;
587+ test ( '"123"' , GraphQLFloat , undefined ) ;
588+ test ( '123' , GraphQLString , undefined ) ;
589+ test ( 'true' , GraphQLString , undefined ) ;
590+ test ( '123.456' , GraphQLString , undefined ) ;
591+ test ( '123.456' , GraphQLID , undefined ) ;
592+ } ) ;
593+
594+ it ( 'convert using parseLiteral from a custom scalar type' , ( ) => {
595+ const passthroughScalar = new GraphQLScalarType ( {
596+ name : 'PassthroughScalar' ,
597+ parseLiteral ( node ) {
598+ invariant ( node . kind === 'StringValue' ) ;
599+ return node . value ;
600+ } ,
601+ parseValue : identityFunc ,
602+ } ) ;
603+
604+ test ( '"value"' , passthroughScalar , 'value' ) ;
605+
606+ const printScalar = new GraphQLScalarType ( {
607+ name : 'PrintScalar' ,
608+ parseLiteral ( node ) {
609+ return `~~~${ print ( node ) } ~~~` ;
610+ } ,
611+ parseValue : identityFunc ,
612+ } ) ;
613+
614+ test ( '"value"' , printScalar , '~~~"value"~~~' ) ;
615+
616+ const throwScalar = new GraphQLScalarType ( {
617+ name : 'ThrowScalar' ,
618+ parseLiteral ( ) {
619+ throw new Error ( 'Test' ) ;
620+ } ,
621+ parseValue : identityFunc ,
622+ } ) ;
623+
624+ test ( 'value' , throwScalar , undefined ) ;
625+
626+ const returnUndefinedScalar = new GraphQLScalarType ( {
627+ name : 'ReturnUndefinedScalar' ,
628+ parseLiteral ( ) {
629+ return undefined ;
630+ } ,
631+ parseValue : identityFunc ,
632+ } ) ;
633+
634+ test ( 'value' , returnUndefinedScalar , undefined ) ;
635+ } ) ;
636+
637+ it ( 'converts enum values according to input coercion rules' , ( ) => {
638+ const testEnum = new GraphQLEnumType ( {
639+ name : 'TestColor' ,
640+ values : {
641+ RED : { value : 1 } ,
642+ GREEN : { value : 2 } ,
643+ BLUE : { value : 3 } ,
644+ NULL : { value : null } ,
645+ NAN : { value : NaN } ,
646+ NO_CUSTOM_VALUE : { value : undefined } ,
647+ } ,
648+ } ) ;
649+
650+ test ( 'RED' , testEnum , 1 ) ;
651+ test ( 'BLUE' , testEnum , 3 ) ;
652+ test ( '3' , testEnum , undefined ) ;
653+ test ( '"BLUE"' , testEnum , undefined ) ;
654+ test ( 'null' , testEnum , null ) ;
655+ test ( 'NULL' , testEnum , null ) ;
656+ test ( 'NULL' , new GraphQLNonNull ( testEnum ) , null ) ;
657+ test ( 'NAN' , testEnum , NaN ) ;
658+ test ( 'NO_CUSTOM_VALUE' , testEnum , 'NO_CUSTOM_VALUE' ) ;
659+ } ) ;
660+
661+ // Boolean!
662+ const nonNullBool = new GraphQLNonNull ( GraphQLBoolean ) ;
663+ // [Boolean]
664+ const listOfBool = new GraphQLList ( GraphQLBoolean ) ;
665+ // [Boolean!]
666+ const listOfNonNullBool = new GraphQLList ( nonNullBool ) ;
667+ // [Boolean]!
668+ const nonNullListOfBool = new GraphQLNonNull ( listOfBool ) ;
669+ // [Boolean!]!
670+ const nonNullListOfNonNullBool = new GraphQLNonNull ( listOfNonNullBool ) ;
671+
672+ it ( 'coerces to null unless non-null' , ( ) => {
673+ test ( 'null' , GraphQLBoolean , null ) ;
674+ test ( 'null' , nonNullBool , undefined ) ;
675+ } ) ;
676+
677+ it ( 'coerces lists of values' , ( ) => {
678+ test ( 'true' , listOfBool , [ true ] ) ;
679+ test ( '123' , listOfBool , undefined ) ;
680+ test ( 'null' , listOfBool , null ) ;
681+ test ( '[true, false]' , listOfBool , [ true , false ] ) ;
682+ test ( '[true, 123]' , listOfBool , undefined ) ;
683+ test ( '[true, null]' , listOfBool , [ true , null ] ) ;
684+ test ( '{ true: true }' , listOfBool , undefined ) ;
685+ } ) ;
686+
687+ it ( 'coerces non-null lists of values' , ( ) => {
688+ test ( 'true' , nonNullListOfBool , [ true ] ) ;
689+ test ( '123' , nonNullListOfBool , undefined ) ;
690+ test ( 'null' , nonNullListOfBool , undefined ) ;
691+ test ( '[true, false]' , nonNullListOfBool , [ true , false ] ) ;
692+ test ( '[true, 123]' , nonNullListOfBool , undefined ) ;
693+ test ( '[true, null]' , nonNullListOfBool , [ true , null ] ) ;
694+ } ) ;
695+
696+ it ( 'coerces lists of non-null values' , ( ) => {
697+ test ( 'true' , listOfNonNullBool , [ true ] ) ;
698+ test ( '123' , listOfNonNullBool , undefined ) ;
699+ test ( 'null' , listOfNonNullBool , null ) ;
700+ test ( '[true, false]' , listOfNonNullBool , [ true , false ] ) ;
701+ test ( '[true, 123]' , listOfNonNullBool , undefined ) ;
702+ test ( '[true, null]' , listOfNonNullBool , undefined ) ;
703+ } ) ;
704+
705+ it ( 'coerces non-null lists of non-null values' , ( ) => {
706+ test ( 'true' , nonNullListOfNonNullBool , [ true ] ) ;
707+ test ( '123' , nonNullListOfNonNullBool , undefined ) ;
708+ test ( 'null' , nonNullListOfNonNullBool , undefined ) ;
709+ test ( '[true, false]' , nonNullListOfNonNullBool , [ true , false ] ) ;
710+ test ( '[true, 123]' , nonNullListOfNonNullBool , undefined ) ;
711+ test ( '[true, null]' , nonNullListOfNonNullBool , undefined ) ;
712+ } ) ;
713+
714+ it ( 'uses default values for unprovided fields' , ( ) => {
715+ const type = new GraphQLInputObjectType ( {
716+ name : 'TestInput' ,
717+ fields : {
718+ int : { type : GraphQLInt , defaultValue : 42 } ,
719+ } ,
720+ } ) ;
721+
722+ test ( '{}' , type , { int : 42 } ) ;
723+ } ) ;
724+
725+ const testInputObj = new GraphQLInputObjectType ( {
726+ name : 'TestInput' ,
727+ fields : {
728+ int : { type : GraphQLInt , defaultValue : 42 } ,
729+ bool : { type : GraphQLBoolean } ,
730+ requiredBool : { type : nonNullBool } ,
731+ } ,
732+ } ) ;
733+ const testOneOfInputObj = new GraphQLInputObjectType ( {
734+ name : 'TestOneOfInput' ,
735+ fields : {
736+ a : { type : GraphQLString } ,
737+ b : { type : GraphQLString } ,
738+ } ,
739+ isOneOf : true ,
740+ } ) ;
741+
742+ it ( 'coerces input objects according to input coercion rules' , ( ) => {
743+ test ( 'null' , testInputObj , null ) ;
744+ test ( '123' , testInputObj , undefined ) ;
745+ test ( '[]' , testInputObj , undefined ) ;
746+ test ( '{ requiredBool: true }' , testInputObj , {
747+ int : 42 ,
748+ requiredBool : true ,
749+ } ) ;
750+ test ( '{ int: null, requiredBool: true }' , testInputObj , {
751+ int : null ,
752+ requiredBool : true ,
753+ } ) ;
754+ test ( '{ int: 123, requiredBool: false }' , testInputObj , {
755+ int : 123 ,
756+ requiredBool : false ,
757+ } ) ;
758+ test ( '{ bool: true, requiredBool: false }' , testInputObj , {
759+ int : 42 ,
760+ bool : true ,
761+ requiredBool : false ,
762+ } ) ;
763+ test ( '{ int: true, requiredBool: true }' , testInputObj , undefined ) ;
764+ test ( '{ requiredBool: null }' , testInputObj , undefined ) ;
765+ test ( '{ bool: true }' , testInputObj , undefined ) ;
766+ test ( '{ requiredBool: true, unknown: 123 }' , testInputObj , undefined ) ;
767+ test ( '{ a: "abc" }' , testOneOfInputObj , {
768+ a : 'abc' ,
769+ } ) ;
770+ test ( '{ b: "def" }' , testOneOfInputObj , {
771+ b : 'def' ,
772+ } ) ;
773+ test ( '{ a: "abc", b: null }' , testOneOfInputObj , undefined ) ;
774+ test ( '{ a: null }' , testOneOfInputObj , undefined ) ;
775+ test ( '{ a: 1 }' , testOneOfInputObj , undefined ) ;
776+ test ( '{ a: "abc", b: "def" }' , testOneOfInputObj , undefined ) ;
777+ test ( '{}' , testOneOfInputObj , undefined ) ;
778+ test ( '{ c: "abc" }' , testOneOfInputObj , undefined ) ;
779+ } ) ;
780+
781+ it ( 'accepts variable values assuming already coerced' , ( ) => {
782+ test ( '$var' , GraphQLBoolean , undefined ) ;
783+ testWithVariables ( { var : true } , '$var' , GraphQLBoolean , true ) ;
784+ testWithVariables ( { var : null } , '$var' , GraphQLBoolean , null ) ;
785+ testWithVariables ( { var : null } , '$var' , nonNullBool , undefined ) ;
786+ } ) ;
787+
788+ it ( 'asserts variables are provided as items in lists' , ( ) => {
789+ test ( '[ $foo ]' , listOfBool , [ null ] ) ;
790+ test ( '[ $foo ]' , listOfNonNullBool , undefined ) ;
791+ testWithVariables ( { foo : true } , '[ $foo ]' , listOfNonNullBool , [ true ] ) ;
792+ // Note: variables are expected to have already been coerced, so we
793+ // do not expect the singleton wrapping behavior for variables.
794+ testWithVariables ( { foo : true } , '$foo' , listOfNonNullBool , true ) ;
795+ testWithVariables ( { foo : [ true ] } , '$foo' , listOfNonNullBool , [ true ] ) ;
796+ } ) ;
797+
798+ it ( 'omits input object fields for unprovided variables' , ( ) => {
799+ test ( '{ int: $foo, bool: $foo, requiredBool: true }' , testInputObj , {
800+ int : 42 ,
801+ requiredBool : true ,
802+ } ) ;
803+ test ( '{ requiredBool: $foo }' , testInputObj , undefined ) ;
804+ testWithVariables ( { foo : true } , '{ requiredBool: $foo }' , testInputObj , {
805+ int : 42 ,
806+ requiredBool : true ,
807+ } ) ;
808+ } ) ;
809+ } ) ;
0 commit comments