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