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