1- // eslint-disable-next-line import/no-extraneous-dependencies
1+
22import { inspect } from 'cross-inspect' ;
33import {
44 GraphQLArgumentConfig ,
@@ -30,6 +30,13 @@ const TestComplexScalar = new GraphQLScalarType({
3030 } ,
3131} ) ;
3232
33+ const NestedType : GraphQLObjectType = new GraphQLObjectType ( {
34+ name : 'NestedType' ,
35+ fields : {
36+ echo : fieldWithInputArg ( { type : GraphQLString } ) ,
37+ } ,
38+ } ) ;
39+
3340const TestInputObject = new GraphQLInputObjectType ( {
3441 name : 'TestInputObject' ,
3542 fields : {
@@ -98,6 +105,10 @@ const TestType = new GraphQLObjectType({
98105 defaultValue : 'Hello World' ,
99106 } ) ,
100107 list : fieldWithInputArg ( { type : new GraphQLList ( GraphQLString ) } ) ,
108+ nested : {
109+ type : NestedType ,
110+ resolve : ( ) => ( { } ) ,
111+ } ,
101112 nnList : fieldWithInputArg ( {
102113 type : new GraphQLNonNull ( new GraphQLList ( GraphQLString ) ) ,
103114 } ) ,
@@ -117,6 +128,16 @@ function executeQuery(query: string, variableValues?: { [variable: string]: unkn
117128 return executeSync ( { schema, document, variableValues } ) ;
118129}
119130
131+
132+ function executeQueryWithFragmentArguments (
133+ query : string ,
134+ variableValues ?: { [ variable : string ] : unknown } ,
135+ ) {
136+ // TODO: figure out how to do custom parser here
137+ const document = parse ( query , { experimentalFragmentArguments : true } ) ;
138+ return executeSync ( { schema, document, variableValues } ) ;
139+ }
140+
120141describe ( 'Execute: Handles inputs' , ( ) => {
121142 describe ( 'Handles objects and nullability' , ( ) => {
122143 describe ( 'using inline structs' , ( ) => {
@@ -1038,4 +1059,283 @@ describe('Execute: Handles inputs', () => {
10381059 } ) ;
10391060 } ) ;
10401061 } ) ;
1062+
1063+ describe ( 'using fragment arguments' , ( ) => {
1064+ it ( 'when there are no fragment arguments' , ( ) => {
1065+ const result = executeQueryWithFragmentArguments ( `
1066+ query {
1067+ ...a
1068+ }
1069+ fragment a on TestType {
1070+ fieldWithNonNullableStringInput(input: "A")
1071+ }
1072+ ` ) ;
1073+ expectJSON ( result ) . toDeepEqual ( {
1074+ data : {
1075+ fieldWithNonNullableStringInput : '"A"' ,
1076+ } ,
1077+ } ) ;
1078+ } ) ;
1079+
1080+ it ( 'when a value is required and provided' , ( ) => {
1081+ const result = executeQueryWithFragmentArguments ( `
1082+ query {
1083+ ...a(value: "A")
1084+ }
1085+ fragment a($value: String!) on TestType {
1086+ fieldWithNonNullableStringInput(input: $value)
1087+ }
1088+ ` ) ;
1089+ expectJSON ( result ) . toDeepEqual ( {
1090+ data : {
1091+ fieldWithNonNullableStringInput : '"A"' ,
1092+ } ,
1093+ } ) ;
1094+ } ) ;
1095+
1096+ it ( 'when a value is required and not provided' , ( ) => {
1097+ const result = executeQueryWithFragmentArguments ( `
1098+ query {
1099+ ...a
1100+ }
1101+ fragment a($value: String!) on TestType {
1102+ fieldWithNullableStringInput(input: $value)
1103+ }
1104+ ` ) ;
1105+
1106+ expect ( result ) . toHaveProperty ( 'errors' ) ;
1107+ expect ( result . errors ) . toHaveLength ( 1 ) ;
1108+ expect ( result . errors ?. at ( 0 ) ?. message ) . toMatch (
1109+ / A r g u m e n t " v a l u e " o f r e q u i r e d t y p e " S t r i n g ! " / ,
1110+ ) ;
1111+ } ) ;
1112+
1113+ it ( 'when the definition has a default and is provided' , ( ) => {
1114+ const result = executeQueryWithFragmentArguments ( `
1115+ query {
1116+ ...a(value: "A")
1117+ }
1118+ fragment a($value: String! = "B") on TestType {
1119+ fieldWithNonNullableStringInput(input: $value)
1120+ }
1121+ ` ) ;
1122+ expectJSON ( result ) . toDeepEqual ( {
1123+ data : {
1124+ fieldWithNonNullableStringInput : '"A"' ,
1125+ } ,
1126+ } ) ;
1127+ } ) ;
1128+
1129+ it ( 'when the definition has a default and is not provided' , ( ) => {
1130+ const result = executeQueryWithFragmentArguments ( `
1131+ query {
1132+ ...a
1133+ }
1134+ fragment a($value: String! = "B") on TestType {
1135+ fieldWithNonNullableStringInput(input: $value)
1136+ }
1137+ ` ) ;
1138+ expectJSON ( result ) . toDeepEqual ( {
1139+ data : {
1140+ fieldWithNonNullableStringInput : '"B"' ,
1141+ } ,
1142+ } ) ;
1143+ } ) ;
1144+
1145+ it ( 'when a definition has a default, is not provided, and spreads another fragment' , ( ) => {
1146+ const result = executeQueryWithFragmentArguments ( `
1147+ query {
1148+ ...a
1149+ }
1150+ fragment a($a: String! = "B") on TestType {
1151+ ...b(b: $a)
1152+ }
1153+ fragment b($b: String!) on TestType {
1154+ fieldWithNonNullableStringInput(input: $b)
1155+ }
1156+ ` ) ;
1157+ expectJSON ( result ) . toDeepEqual ( {
1158+ data : {
1159+ fieldWithNonNullableStringInput : '"B"' ,
1160+ } ,
1161+ } ) ;
1162+ } ) ;
1163+
1164+ it ( 'when the definition has a non-nullable default and is provided null' , ( ) => {
1165+ const result = executeQueryWithFragmentArguments ( `
1166+ query {
1167+ ...a(value: null)
1168+ }
1169+ fragment a($value: String! = "B") on TestType {
1170+ fieldWithNullableStringInput(input: $value)
1171+ }
1172+ ` ) ;
1173+
1174+ expect ( result ) . toHaveProperty ( 'errors' ) ;
1175+ expect ( result . errors ) . toHaveLength ( 1 ) ;
1176+ expect ( result . errors ?. at ( 0 ) ?. message ) . toMatch (
1177+ / A r g u m e n t " v a l u e " o f n o n - n u l l t y p e " S t r i n g ! " / ,
1178+ ) ;
1179+ } ) ;
1180+
1181+ it ( 'when the definition has no default and is not provided' , ( ) => {
1182+ const result = executeQueryWithFragmentArguments ( `
1183+ query {
1184+ ...a
1185+ }
1186+ fragment a($value: String) on TestType {
1187+ fieldWithNonNullableStringInputAndDefaultArgumentValue(input: $value)
1188+ }
1189+ ` ) ;
1190+ expectJSON ( result ) . toDeepEqual ( {
1191+ data : {
1192+ fieldWithNonNullableStringInputAndDefaultArgumentValue :
1193+ '"Hello World"' ,
1194+ } ,
1195+ } ) ;
1196+ } ) ;
1197+
1198+ it ( 'when an argument is shadowed by an operation variable' , ( ) => {
1199+ const result = executeQueryWithFragmentArguments ( `
1200+ query($x: String! = "A") {
1201+ ...a(x: "B")
1202+ }
1203+ fragment a($x: String) on TestType {
1204+ fieldWithNullableStringInput(input: $x)
1205+ }
1206+ ` ) ;
1207+ expectJSON ( result ) . toDeepEqual ( {
1208+ data : {
1209+ fieldWithNullableStringInput : '"B"' ,
1210+ } ,
1211+ } ) ;
1212+ } ) ;
1213+
1214+ it ( 'when a nullable argument with a field default is not provided and shadowed by an operation variable' , ( ) => {
1215+ const result = executeQueryWithFragmentArguments ( `
1216+ query($x: String = "A") {
1217+ ...a
1218+ }
1219+ fragment a($x: String) on TestType {
1220+ fieldWithNonNullableStringInputAndDefaultArgumentValue(input: $x)
1221+ }
1222+ ` ) ;
1223+ expectJSON ( result ) . toDeepEqual ( {
1224+ data : {
1225+ fieldWithNonNullableStringInputAndDefaultArgumentValue :
1226+ '"Hello World"' ,
1227+ } ,
1228+ } ) ;
1229+ } ) ;
1230+
1231+ it ( 'when a fragment-variable is shadowed by an intermediate fragment-spread but defined in the operation-variables' , ( ) => {
1232+ const result = executeQueryWithFragmentArguments ( `
1233+ query($x: String = "A") {
1234+ ...a
1235+ }
1236+ fragment a($x: String) on TestType {
1237+ ...b
1238+ }
1239+ fragment b on TestType {
1240+ fieldWithNullableStringInput(input: $x)
1241+ }
1242+ ` ) ;
1243+ expectJSON ( result ) . toDeepEqual ( {
1244+ data : {
1245+ fieldWithNullableStringInput : '"A"' ,
1246+ } ,
1247+ } ) ;
1248+ } ) ;
1249+
1250+ it ( 'when a fragment is used with different args' , ( ) => {
1251+ const result = executeQueryWithFragmentArguments ( `
1252+ query($x: String = "Hello") {
1253+ a: nested {
1254+ ...a(x: "a")
1255+ }
1256+ b: nested {
1257+ ...a(x: "b", b: true)
1258+ }
1259+ hello: nested {
1260+ ...a(x: $x)
1261+ }
1262+ }
1263+ fragment a($x: String, $b: Boolean = false) on NestedType {
1264+ a: echo(input: $x) @skip(if: $b)
1265+ b: echo(input: $x) @include(if: $b)
1266+ }
1267+ ` ) ;
1268+ expectJSON ( result ) . toDeepEqual ( {
1269+ data : {
1270+ a : {
1271+ a : '"a"' ,
1272+ } ,
1273+ b : {
1274+ b : '"b"' ,
1275+ } ,
1276+ hello : {
1277+ a : '"Hello"' ,
1278+ } ,
1279+ } ,
1280+ } ) ;
1281+ } ) ;
1282+
1283+ it ( 'when the argument variable is nested in a complex type' , ( ) => {
1284+ const result = executeQueryWithFragmentArguments ( `
1285+ query {
1286+ ...a(value: "C")
1287+ }
1288+ fragment a($value: String) on TestType {
1289+ list(input: ["A", "B", $value, "D"])
1290+ }
1291+ ` ) ;
1292+ expectJSON ( result ) . toDeepEqual ( {
1293+ data : {
1294+ list : '["A", "B", "C", "D"]' ,
1295+ } ,
1296+ } ) ;
1297+ } ) ;
1298+
1299+ it ( 'when argument variables are used recursively' , ( ) => {
1300+ const result = executeQueryWithFragmentArguments ( `
1301+ query {
1302+ ...a(aValue: "C")
1303+ }
1304+ fragment a($aValue: String) on TestType {
1305+ ...b(bValue: $aValue)
1306+ }
1307+ fragment b($bValue: String) on TestType {
1308+ list(input: ["A", "B", $bValue, "D"])
1309+ }
1310+ ` ) ;
1311+ expectJSON ( result ) . toDeepEqual ( {
1312+ data : {
1313+ list : '["A", "B", "C", "D"]' ,
1314+ } ,
1315+ } ) ;
1316+ } ) ;
1317+
1318+ it ( 'when argument passed in as list' , ( ) => {
1319+ const result = executeQueryWithFragmentArguments ( `
1320+ query Q($opValue: String = "op") {
1321+ ...a(aValue: "A")
1322+ }
1323+ fragment a($aValue: String, $bValue: String) on TestType {
1324+ ...b(aValue: [$aValue, "B"], bValue: [$bValue, $opValue])
1325+ }
1326+ fragment b($aValue: [String], $bValue: [String], $cValue: String) on TestType {
1327+ aList: list(input: $aValue)
1328+ bList: list(input: $bValue)
1329+ cList: list(input: [$cValue])
1330+ }
1331+ ` ) ;
1332+ expectJSON ( result ) . toDeepEqual ( {
1333+ data : {
1334+ aList : '["A", "B"]' ,
1335+ bList : '[null, "op"]' ,
1336+ cList : '[null]' ,
1337+ } ,
1338+ } ) ;
1339+ } ) ;
1340+ } ) ;
10411341} ) ;
0 commit comments