1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
1
import { inspect } from 'cross-inspect' ;
3
2
import {
4
3
GraphQLArgumentConfig ,
@@ -30,6 +29,13 @@ const TestComplexScalar = new GraphQLScalarType({
30
29
} ,
31
30
} ) ;
32
31
32
+ const NestedType : GraphQLObjectType = new GraphQLObjectType ( {
33
+ name : 'NestedType' ,
34
+ fields : {
35
+ echo : fieldWithInputArg ( { type : GraphQLString } ) ,
36
+ } ,
37
+ } ) ;
38
+
33
39
const TestInputObject = new GraphQLInputObjectType ( {
34
40
name : 'TestInputObject' ,
35
41
fields : {
@@ -98,6 +104,10 @@ const TestType = new GraphQLObjectType({
98
104
defaultValue : 'Hello World' ,
99
105
} ) ,
100
106
list : fieldWithInputArg ( { type : new GraphQLList ( GraphQLString ) } ) ,
107
+ nested : {
108
+ type : NestedType ,
109
+ resolve : ( ) => ( { } ) ,
110
+ } ,
101
111
nnList : fieldWithInputArg ( {
102
112
type : new GraphQLNonNull ( new GraphQLList ( GraphQLString ) ) ,
103
113
} ) ,
@@ -117,6 +127,15 @@ function executeQuery(query: string, variableValues?: { [variable: string]: unkn
117
127
return executeSync ( { schema, document, variableValues } ) ;
118
128
}
119
129
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
+
120
139
describe ( 'Execute: Handles inputs' , ( ) => {
121
140
describe ( 'Handles objects and nullability' , ( ) => {
122
141
describe ( 'using inline structs' , ( ) => {
@@ -1038,4 +1057,277 @@ describe('Execute: Handles inputs', () => {
1038
1057
} ) ;
1039
1058
} ) ;
1040
1059
} ) ;
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
+ } ) ;
1041
1333
} ) ;
0 commit comments