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 ;
@@ -427,3 +440,244 @@ describe('coerceInputValue', () => {
427440 } ) ;
428441 } ) ;
429442} ) ;
443+
444+ describe ( 'coerceInputLiteral' , ( ) => {
445+ function test (
446+ valueText : string ,
447+ type : GraphQLInputType ,
448+ expected : unknown ,
449+ variables ?: ObjMap < unknown > ,
450+ ) {
451+ const ast = parseValue ( valueText ) ;
452+ const value = coerceInputLiteral ( ast , type , variables ) ;
453+ expect ( value ) . to . deep . equal ( expected ) ;
454+ }
455+
456+ function testWithVariables (
457+ variables : ObjMap < unknown > ,
458+ valueText : string ,
459+ type : GraphQLInputType ,
460+ expected : unknown ,
461+ ) {
462+ test ( valueText , type , expected , variables ) ;
463+ }
464+
465+ it ( 'converts according to input coercion rules' , ( ) => {
466+ test ( 'true' , GraphQLBoolean , true ) ;
467+ test ( 'false' , GraphQLBoolean , false ) ;
468+ test ( '123' , GraphQLInt , 123 ) ;
469+ test ( '123' , GraphQLFloat , 123 ) ;
470+ test ( '123.456' , GraphQLFloat , 123.456 ) ;
471+ test ( '"abc123"' , GraphQLString , 'abc123' ) ;
472+ test ( '123456' , GraphQLID , '123456' ) ;
473+ test ( '"123456"' , GraphQLID , '123456' ) ;
474+ } ) ;
475+
476+ it ( 'does not convert when input coercion rules reject a value' , ( ) => {
477+ test ( '123' , GraphQLBoolean , undefined ) ;
478+ test ( '123.456' , GraphQLInt , undefined ) ;
479+ test ( 'true' , GraphQLInt , undefined ) ;
480+ test ( '"123"' , GraphQLInt , undefined ) ;
481+ test ( '"123"' , GraphQLFloat , undefined ) ;
482+ test ( '123' , GraphQLString , undefined ) ;
483+ test ( 'true' , GraphQLString , undefined ) ;
484+ test ( '123.456' , GraphQLString , undefined ) ;
485+ test ( '123.456' , GraphQLID , undefined ) ;
486+ } ) ;
487+
488+ it ( 'convert using parseLiteral from a custom scalar type' , ( ) => {
489+ const passthroughScalar = new GraphQLScalarType ( {
490+ name : 'PassthroughScalar' ,
491+ parseLiteral ( node ) {
492+ invariant ( node . kind === 'StringValue' ) ;
493+ return node . value ;
494+ } ,
495+ parseValue : identityFunc ,
496+ } ) ;
497+
498+ test ( '"value"' , passthroughScalar , 'value' ) ;
499+
500+ const printScalar = new GraphQLScalarType ( {
501+ name : 'PrintScalar' ,
502+ parseLiteral ( node ) {
503+ return `~~~${ print ( node ) } ~~~` ;
504+ } ,
505+ parseValue : identityFunc ,
506+ } ) ;
507+
508+ test ( '"value"' , printScalar , '~~~"value"~~~' ) ;
509+
510+ const throwScalar = new GraphQLScalarType ( {
511+ name : 'ThrowScalar' ,
512+ parseLiteral ( ) {
513+ throw new Error ( 'Test' ) ;
514+ } ,
515+ parseValue : identityFunc ,
516+ } ) ;
517+
518+ test ( 'value' , throwScalar , undefined ) ;
519+
520+ const returnUndefinedScalar = new GraphQLScalarType ( {
521+ name : 'ReturnUndefinedScalar' ,
522+ parseLiteral ( ) {
523+ return undefined ;
524+ } ,
525+ parseValue : identityFunc ,
526+ } ) ;
527+
528+ test ( 'value' , returnUndefinedScalar , undefined ) ;
529+ } ) ;
530+
531+ it ( 'converts enum values according to input coercion rules' , ( ) => {
532+ const testEnum = new GraphQLEnumType ( {
533+ name : 'TestColor' ,
534+ values : {
535+ RED : { value : 1 } ,
536+ GREEN : { value : 2 } ,
537+ BLUE : { value : 3 } ,
538+ NULL : { value : null } ,
539+ NAN : { value : NaN } ,
540+ NO_CUSTOM_VALUE : { value : undefined } ,
541+ } ,
542+ } ) ;
543+
544+ test ( 'RED' , testEnum , 1 ) ;
545+ test ( 'BLUE' , testEnum , 3 ) ;
546+ test ( '3' , testEnum , undefined ) ;
547+ test ( '"BLUE"' , testEnum , undefined ) ;
548+ test ( 'null' , testEnum , null ) ;
549+ test ( 'NULL' , testEnum , null ) ;
550+ test ( 'NULL' , new GraphQLNonNull ( testEnum ) , null ) ;
551+ test ( 'NAN' , testEnum , NaN ) ;
552+ test ( 'NO_CUSTOM_VALUE' , testEnum , 'NO_CUSTOM_VALUE' ) ;
553+ } ) ;
554+
555+ // Boolean!
556+ const nonNullBool = new GraphQLNonNull ( GraphQLBoolean ) ;
557+ // [Boolean]
558+ const listOfBool = new GraphQLList ( GraphQLBoolean ) ;
559+ // [Boolean!]
560+ const listOfNonNullBool = new GraphQLList ( nonNullBool ) ;
561+ // [Boolean]!
562+ const nonNullListOfBool = new GraphQLNonNull ( listOfBool ) ;
563+ // [Boolean!]!
564+ const nonNullListOfNonNullBool = new GraphQLNonNull ( listOfNonNullBool ) ;
565+
566+ it ( 'coerces to null unless non-null' , ( ) => {
567+ test ( 'null' , GraphQLBoolean , null ) ;
568+ test ( 'null' , nonNullBool , undefined ) ;
569+ } ) ;
570+
571+ it ( 'coerces lists of values' , ( ) => {
572+ test ( 'true' , listOfBool , [ true ] ) ;
573+ test ( '123' , listOfBool , undefined ) ;
574+ test ( 'null' , listOfBool , null ) ;
575+ test ( '[true, false]' , listOfBool , [ true , false ] ) ;
576+ test ( '[true, 123]' , listOfBool , undefined ) ;
577+ test ( '[true, null]' , listOfBool , [ true , null ] ) ;
578+ test ( '{ true: true }' , listOfBool , undefined ) ;
579+ } ) ;
580+
581+ it ( 'coerces non-null lists of values' , ( ) => {
582+ test ( 'true' , nonNullListOfBool , [ true ] ) ;
583+ test ( '123' , nonNullListOfBool , undefined ) ;
584+ test ( 'null' , nonNullListOfBool , undefined ) ;
585+ test ( '[true, false]' , nonNullListOfBool , [ true , false ] ) ;
586+ test ( '[true, 123]' , nonNullListOfBool , undefined ) ;
587+ test ( '[true, null]' , nonNullListOfBool , [ true , null ] ) ;
588+ } ) ;
589+
590+ it ( 'coerces lists of non-null values' , ( ) => {
591+ test ( 'true' , listOfNonNullBool , [ true ] ) ;
592+ test ( '123' , listOfNonNullBool , undefined ) ;
593+ test ( 'null' , listOfNonNullBool , null ) ;
594+ test ( '[true, false]' , listOfNonNullBool , [ true , false ] ) ;
595+ test ( '[true, 123]' , listOfNonNullBool , undefined ) ;
596+ test ( '[true, null]' , listOfNonNullBool , undefined ) ;
597+ } ) ;
598+
599+ it ( 'coerces non-null lists of non-null values' , ( ) => {
600+ test ( 'true' , nonNullListOfNonNullBool , [ true ] ) ;
601+ test ( '123' , nonNullListOfNonNullBool , undefined ) ;
602+ test ( 'null' , nonNullListOfNonNullBool , undefined ) ;
603+ test ( '[true, false]' , nonNullListOfNonNullBool , [ true , false ] ) ;
604+ test ( '[true, 123]' , nonNullListOfNonNullBool , undefined ) ;
605+ test ( '[true, null]' , nonNullListOfNonNullBool , undefined ) ;
606+ } ) ;
607+
608+ it ( 'uses default values for unprovided fields' , ( ) => {
609+ const type = new GraphQLInputObjectType ( {
610+ name : 'TestInput' ,
611+ fields : {
612+ int : { type : GraphQLInt , defaultValue : 42 } ,
613+ } ,
614+ } ) ;
615+
616+ test ( '{}' , type , { int : 42 } ) ;
617+ } ) ;
618+
619+ const testInputObj = new GraphQLInputObjectType ( {
620+ name : 'TestInput' ,
621+ fields : {
622+ int : { type : GraphQLInt , defaultValue : 42 } ,
623+ bool : { type : GraphQLBoolean } ,
624+ requiredBool : { type : nonNullBool } ,
625+ } ,
626+ } ) ;
627+
628+ it ( 'coerces input objects according to input coercion rules' , ( ) => {
629+ test ( 'null' , testInputObj , null ) ;
630+ test ( '123' , testInputObj , undefined ) ;
631+ test ( '[]' , testInputObj , undefined ) ;
632+ test ( '{ requiredBool: true }' , testInputObj , {
633+ int : 42 ,
634+ requiredBool : true ,
635+ } ) ;
636+ test ( '{ int: null, requiredBool: true }' , testInputObj , {
637+ int : null ,
638+ requiredBool : true ,
639+ } ) ;
640+ test ( '{ int: 123, requiredBool: false }' , testInputObj , {
641+ int : 123 ,
642+ requiredBool : false ,
643+ } ) ;
644+ test ( '{ bool: true, requiredBool: false }' , testInputObj , {
645+ int : 42 ,
646+ bool : true ,
647+ requiredBool : false ,
648+ } ) ;
649+ test ( '{ int: true, requiredBool: true }' , testInputObj , undefined ) ;
650+ test ( '{ requiredBool: null }' , testInputObj , undefined ) ;
651+ test ( '{ bool: true }' , testInputObj , undefined ) ;
652+ test ( '{ requiredBool: true, unknown: 123 }' , testInputObj , undefined ) ;
653+ } ) ;
654+
655+ it ( 'accepts variable values assuming already coerced' , ( ) => {
656+ test ( '$var' , GraphQLBoolean , undefined ) ;
657+ testWithVariables ( { var : true } , '$var' , GraphQLBoolean , true ) ;
658+ testWithVariables ( { var : null } , '$var' , GraphQLBoolean , null ) ;
659+ testWithVariables ( { var : null } , '$var' , nonNullBool , undefined ) ;
660+ } ) ;
661+
662+ it ( 'asserts variables are provided as items in lists' , ( ) => {
663+ test ( '[ $foo ]' , listOfBool , [ null ] ) ;
664+ test ( '[ $foo ]' , listOfNonNullBool , undefined ) ;
665+ testWithVariables ( { foo : true } , '[ $foo ]' , listOfNonNullBool , [ true ] ) ;
666+ // Note: variables are expected to have already been coerced, so we
667+ // do not expect the singleton wrapping behavior for variables.
668+ testWithVariables ( { foo : true } , '$foo' , listOfNonNullBool , true ) ;
669+ testWithVariables ( { foo : [ true ] } , '$foo' , listOfNonNullBool , [ true ] ) ;
670+ } ) ;
671+
672+ it ( 'omits input object fields for unprovided variables' , ( ) => {
673+ test ( '{ int: $foo, bool: $foo, requiredBool: true }' , testInputObj , {
674+ int : 42 ,
675+ requiredBool : true ,
676+ } ) ;
677+ test ( '{ requiredBool: $foo }' , testInputObj , undefined ) ;
678+ testWithVariables ( { foo : true } , '{ requiredBool: $foo }' , testInputObj , {
679+ int : 42 ,
680+ requiredBool : true ,
681+ } ) ;
682+ } ) ;
683+ } ) ;
0 commit comments