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