Skip to content

Commit c8a97f7

Browse files
implement execution for fragment arguments syntax
Co-authored-by: mjmahone <[email protected]>
1 parent e2d8d2d commit c8a97f7

File tree

4 files changed

+467
-36
lines changed

4 files changed

+467
-36
lines changed

src/execution/__tests__/variables-test.ts

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ const TestComplexScalar = new GraphQLScalarType({
5959
},
6060
});
6161

62+
const NestedType: GraphQLObjectType = new GraphQLObjectType({
63+
name: 'NestedType',
64+
fields: {
65+
echo: fieldWithInputArg({ type: GraphQLString }),
66+
},
67+
});
68+
6269
const TestInputObject = new GraphQLInputObjectType({
6370
name: 'TestInputObject',
6471
fields: {
@@ -129,6 +136,10 @@ const TestType = new GraphQLObjectType({
129136
defaultValue: 'Hello World',
130137
}),
131138
list: fieldWithInputArg({ type: new GraphQLList(GraphQLString) }),
139+
nested: {
140+
type: NestedType,
141+
resolve: () => ({}),
142+
},
132143
nnList: fieldWithInputArg({
133144
type: new GraphQLNonNull(new GraphQLList(GraphQLString)),
134145
}),
@@ -153,6 +164,14 @@ function executeQuery(
153164
return executeSync({ schema, document, variableValues });
154165
}
155166

167+
function executeQueryWithFragmentArguments(
168+
query: string,
169+
variableValues?: { [variable: string]: unknown },
170+
) {
171+
const document = parse(query, { experimentalFragmentArguments: true });
172+
return executeSync({ schema, document, variableValues });
173+
}
174+
156175
describe('Execute: Handles inputs', () => {
157176
describe('Handles objects and nullability', () => {
158177
describe('using inline structs', () => {
@@ -1136,4 +1155,284 @@ describe('Execute: Handles inputs', () => {
11361155
});
11371156
});
11381157
});
1158+
1159+
describe('using fragment arguments', () => {
1160+
it('when there are no fragment arguments', () => {
1161+
const result = executeQueryWithFragmentArguments(`
1162+
query {
1163+
...a
1164+
}
1165+
fragment a on TestType {
1166+
fieldWithNonNullableStringInput(input: "A")
1167+
}
1168+
`);
1169+
expect(result).to.deep.equal({
1170+
data: {
1171+
fieldWithNonNullableStringInput: '"A"',
1172+
},
1173+
});
1174+
});
1175+
1176+
it('when a value is required and provided', () => {
1177+
const result = executeQueryWithFragmentArguments(`
1178+
query {
1179+
...a(value: "A")
1180+
}
1181+
fragment a($value: String!) on TestType {
1182+
fieldWithNonNullableStringInput(input: $value)
1183+
}
1184+
`);
1185+
expect(result).to.deep.equal({
1186+
data: {
1187+
fieldWithNonNullableStringInput: '"A"',
1188+
},
1189+
});
1190+
});
1191+
1192+
it('when a value is required and not provided', () => {
1193+
const result = executeQueryWithFragmentArguments(`
1194+
query {
1195+
...a
1196+
}
1197+
fragment a($value: String!) on TestType {
1198+
fieldWithNullableStringInput(input: $value)
1199+
}
1200+
`);
1201+
1202+
expect(result).to.have.property('errors');
1203+
expect(result.errors).to.have.length(1);
1204+
expect(result.errors?.at(0)?.message).to.match(
1205+
/Argument "value" of required type "String!"/,
1206+
);
1207+
});
1208+
1209+
it('when the definition has a default and is provided', () => {
1210+
const result = executeQueryWithFragmentArguments(`
1211+
query {
1212+
...a(value: "A")
1213+
}
1214+
fragment a($value: String! = "B") on TestType {
1215+
fieldWithNonNullableStringInput(input: $value)
1216+
}
1217+
`);
1218+
expect(result).to.deep.equal({
1219+
data: {
1220+
fieldWithNonNullableStringInput: '"A"',
1221+
},
1222+
});
1223+
});
1224+
1225+
it('when the definition has a default and is not provided', () => {
1226+
const result = executeQueryWithFragmentArguments(`
1227+
query {
1228+
...a
1229+
}
1230+
fragment a($value: String! = "B") on TestType {
1231+
fieldWithNonNullableStringInput(input: $value)
1232+
}
1233+
`);
1234+
expect(result).to.deep.equal({
1235+
data: {
1236+
fieldWithNonNullableStringInput: '"B"',
1237+
},
1238+
});
1239+
});
1240+
1241+
it('when a definition has a default, is not provided, and spreads another fragment', () => {
1242+
const result = executeQueryWithFragmentArguments(`
1243+
query {
1244+
...a
1245+
}
1246+
fragment a($a: String! = "B") on TestType {
1247+
...b(b: $a)
1248+
}
1249+
fragment b($b: String!) on TestType {
1250+
fieldWithNonNullableStringInput(input: $b)
1251+
}
1252+
`);
1253+
expect(result).to.deep.equal({
1254+
data: {
1255+
fieldWithNonNullableStringInput: '"B"',
1256+
},
1257+
});
1258+
});
1259+
1260+
it('when the definition has a non-nullable default and is provided null', () => {
1261+
const result = executeQueryWithFragmentArguments(`
1262+
query {
1263+
...a(value: null)
1264+
}
1265+
fragment a($value: String! = "B") on TestType {
1266+
fieldWithNullableStringInput(input: $value)
1267+
}
1268+
`);
1269+
1270+
expect(result).to.have.property('errors');
1271+
expect(result.errors).to.have.length(1);
1272+
expect(result.errors?.at(0)?.message).to.match(
1273+
/Argument "value" of non-null type "String!"/,
1274+
);
1275+
});
1276+
1277+
it('when the definition has no default and is not provided', () => {
1278+
const result = executeQueryWithFragmentArguments(`
1279+
query {
1280+
...a
1281+
}
1282+
fragment a($value: String) on TestType {
1283+
fieldWithNonNullableStringInputAndDefaultArgumentValue(input: $value)
1284+
}
1285+
`);
1286+
expect(result).to.deep.equal({
1287+
data: {
1288+
fieldWithNonNullableStringInputAndDefaultArgumentValue:
1289+
'"Hello World"',
1290+
},
1291+
});
1292+
});
1293+
1294+
it('when an argument is shadowed by an operation variable', () => {
1295+
const result = executeQueryWithFragmentArguments(`
1296+
query($x: String! = "A") {
1297+
...a(x: "B")
1298+
}
1299+
fragment a($x: String) on TestType {
1300+
fieldWithNullableStringInput(input: $x)
1301+
}
1302+
`);
1303+
expect(result).to.deep.equal({
1304+
data: {
1305+
fieldWithNullableStringInput: '"B"',
1306+
},
1307+
});
1308+
});
1309+
1310+
it('when a nullable argument with a field default is not provided and shadowed by an operation variable', () => {
1311+
const result = executeQueryWithFragmentArguments(`
1312+
query($x: String = "A") {
1313+
...a
1314+
}
1315+
fragment a($x: String) on TestType {
1316+
fieldWithNonNullableStringInputAndDefaultArgumentValue(input: $x)
1317+
}
1318+
`);
1319+
expect(result).to.deep.equal({
1320+
data: {
1321+
fieldWithNonNullableStringInputAndDefaultArgumentValue:
1322+
'"Hello World"',
1323+
},
1324+
});
1325+
});
1326+
1327+
it('when a fragment-variable is shadowed by an intermediate fragment-spread but defined in the operation-variables', () => {
1328+
const result = executeQueryWithFragmentArguments(`
1329+
query($x: String = "A") {
1330+
...a
1331+
}
1332+
fragment a($x: String) on TestType {
1333+
...b
1334+
}
1335+
1336+
fragment b on TestType {
1337+
fieldWithNullableStringInput(input: $x)
1338+
}
1339+
`);
1340+
expect(result).to.deep.equal({
1341+
data: {
1342+
fieldWithNullableStringInput: '"A"',
1343+
},
1344+
});
1345+
});
1346+
1347+
it('when a fragment is used with different args', () => {
1348+
const result = executeQueryWithFragmentArguments(`
1349+
query($x: String = "Hello") {
1350+
a: nested {
1351+
...a(x: "a")
1352+
}
1353+
b: nested {
1354+
...a(x: "b", b: true)
1355+
}
1356+
hello: nested {
1357+
...a(x: $x)
1358+
}
1359+
}
1360+
fragment a($x: String, $b: Boolean = false) on NestedType {
1361+
a: echo(input: $x) @skip(if: $b)
1362+
b: echo(input: $x) @include(if: $b)
1363+
}
1364+
`);
1365+
expect(result).to.deep.equal({
1366+
data: {
1367+
a: {
1368+
a: '"a"',
1369+
},
1370+
b: {
1371+
b: '"b"',
1372+
},
1373+
hello: {
1374+
a: '"Hello"',
1375+
},
1376+
},
1377+
});
1378+
});
1379+
1380+
it('when the argument variable is nested in a complex type', () => {
1381+
const result = executeQueryWithFragmentArguments(`
1382+
query {
1383+
...a(value: "C")
1384+
}
1385+
fragment a($value: String) on TestType {
1386+
list(input: ["A", "B", $value, "D"])
1387+
}
1388+
`);
1389+
expect(result).to.deep.equal({
1390+
data: {
1391+
list: '["A", "B", "C", "D"]',
1392+
},
1393+
});
1394+
});
1395+
1396+
it('when argument variables are used recursively', () => {
1397+
const result = executeQueryWithFragmentArguments(`
1398+
query {
1399+
...a(aValue: "C")
1400+
}
1401+
fragment a($aValue: String) on TestType {
1402+
...b(bValue: $aValue)
1403+
}
1404+
fragment b($bValue: String) on TestType {
1405+
list(input: ["A", "B", $bValue, "D"])
1406+
}
1407+
`);
1408+
expect(result).to.deep.equal({
1409+
data: {
1410+
list: '["A", "B", "C", "D"]',
1411+
},
1412+
});
1413+
});
1414+
1415+
it('when argument passed in as list', () => {
1416+
const result = executeQueryWithFragmentArguments(`
1417+
query Q($opValue: String = "op") {
1418+
...a(aValue: "A")
1419+
}
1420+
fragment a($aValue: String, $bValue: String) on TestType {
1421+
...b(aValue: [$aValue, "B"], bValue: [$bValue, $opValue])
1422+
}
1423+
fragment b($aValue: [String], $bValue: [String], $cValue: String) on TestType {
1424+
aList: list(input: $aValue)
1425+
bList: list(input: $bValue)
1426+
cList: list(input: [$cValue])
1427+
}
1428+
`);
1429+
expect(result).to.deep.equal({
1430+
data: {
1431+
aList: '["A", "B"]',
1432+
bList: '[null, "op"]',
1433+
cList: '[null]',
1434+
},
1435+
});
1436+
});
1437+
});
11391438
});

0 commit comments

Comments
 (0)