@@ -12,6 +12,10 @@ import { parse, parseConstValue, parseType, parseValue } from '../parser';
12
12
import { Source } from '../source' ;
13
13
import { TokenKind } from '../tokenKind' ;
14
14
15
+ function parseCCN ( source : string ) {
16
+ return parse ( source , { experimentalClientControlledNullability : true } ) ;
17
+ }
18
+
15
19
function expectSyntaxError ( text : string ) {
16
20
return expectToThrowJSON ( ( ) => parse ( text ) ) ;
17
21
}
@@ -153,7 +157,7 @@ describe('Parser', () => {
153
157
} ) ;
154
158
155
159
it ( 'parses kitchen sink' , ( ) => {
156
- expect ( ( ) => parse ( kitchenSinkQuery ) ) . to . not . throw ( ) ;
160
+ expect ( ( ) => parseCCN ( kitchenSinkQuery ) ) . to . not . throw ( ) ;
157
161
} ) ;
158
162
159
163
it ( 'allows non-keywords anywhere a Name is allowed' , ( ) => {
@@ -224,6 +228,206 @@ describe('Parser', () => {
224
228
) . to . not . throw ( ) ;
225
229
} ) ;
226
230
231
+ it ( 'parses required field' , ( ) => {
232
+ const result = parseCCN ( '{ requiredField! }' ) ;
233
+
234
+ expectJSON ( result ) . toDeepNestedProperty (
235
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
236
+ {
237
+ kind : Kind . NON_NULL_ASSERTION ,
238
+ loc : { start : 15 , end : 16 } ,
239
+ nullabilityAssertion : undefined ,
240
+ } ,
241
+ ) ;
242
+ } ) ;
243
+
244
+ it ( 'parses optional field' , ( ) => {
245
+ expect ( ( ) => parseCCN ( '{ optionalField? }' ) ) . to . not . throw ( ) ;
246
+ } ) ;
247
+
248
+ it ( 'does not parse field with multiple designators' , ( ) => {
249
+ expect ( ( ) => parseCCN ( '{ optionalField?! }' ) ) . to . throw (
250
+ 'Syntax Error: Expected Name, found "!".' ,
251
+ ) ;
252
+
253
+ expect ( ( ) => parseCCN ( '{ optionalField!? }' ) ) . to . throw (
254
+ 'Syntax Error: Expected Name, found "?".' ,
255
+ ) ;
256
+ } ) ;
257
+
258
+ it ( 'parses required with alias' , ( ) => {
259
+ expect ( ( ) => parseCCN ( '{ requiredField: field! }' ) ) . to . not . throw ( ) ;
260
+ } ) ;
261
+
262
+ it ( 'parses optional with alias' , ( ) => {
263
+ expect ( ( ) => parseCCN ( '{ requiredField: field? }' ) ) . to . not . throw ( ) ;
264
+ } ) ;
265
+
266
+ it ( 'does not parse aliased field with bang on left of colon' , ( ) => {
267
+ expect ( ( ) => parseCCN ( '{ requiredField!: field }' ) ) . to . throw ( ) ;
268
+ } ) ;
269
+
270
+ it ( 'does not parse aliased field with question mark on left of colon' , ( ) => {
271
+ expect ( ( ) => parseCCN ( '{ requiredField?: field }' ) ) . to . throw ( ) ;
272
+ } ) ;
273
+
274
+ it ( 'does not parse aliased field with bang on left and right of colon' , ( ) => {
275
+ expect ( ( ) => parseCCN ( '{ requiredField!: field! }' ) ) . to . throw ( ) ;
276
+ } ) ;
277
+
278
+ it ( 'does not parse aliased field with question mark on left and right of colon' , ( ) => {
279
+ expect ( ( ) => parseCCN ( '{ requiredField?: field? }' ) ) . to . throw ( ) ;
280
+ } ) ;
281
+
282
+ it ( 'does not parse designator on query' , ( ) => {
283
+ expect ( ( ) => parseCCN ( 'query? { field }' ) ) . to . throw ( ) ;
284
+ } ) ;
285
+
286
+ it ( 'parses required within fragment' , ( ) => {
287
+ expect ( ( ) =>
288
+ parseCCN ( 'fragment MyFragment on Query { field! }' ) ,
289
+ ) . to . not . throw ( ) ;
290
+ } ) ;
291
+
292
+ it ( 'parses optional within fragment' , ( ) => {
293
+ expect ( ( ) =>
294
+ parseCCN ( 'fragment MyFragment on Query { field? }' ) ,
295
+ ) . to . not . throw ( ) ;
296
+ } ) ;
297
+
298
+ it ( 'parses field with required list elements' , ( ) => {
299
+ const result = parseCCN ( '{ field[!] }' ) ;
300
+
301
+ expectJSON ( result ) . toDeepNestedProperty (
302
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
303
+ {
304
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
305
+ loc : { start : 7 , end : 10 } ,
306
+ nullabilityAssertion : {
307
+ kind : Kind . NON_NULL_ASSERTION ,
308
+ loc : { start : 8 , end : 9 } ,
309
+ nullabilityAssertion : undefined ,
310
+ } ,
311
+ } ,
312
+ ) ;
313
+ } ) ;
314
+
315
+ it ( 'parses field with optional list elements' , ( ) => {
316
+ const result = parseCCN ( '{ field[?] }' ) ;
317
+
318
+ expectJSON ( result ) . toDeepNestedProperty (
319
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
320
+ {
321
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
322
+ loc : { start : 7 , end : 10 } ,
323
+ nullabilityAssertion : {
324
+ kind : Kind . ERROR_BOUNDARY ,
325
+ loc : { start : 8 , end : 9 } ,
326
+ nullabilityAssertion : undefined ,
327
+ } ,
328
+ } ,
329
+ ) ;
330
+ } ) ;
331
+
332
+ it ( 'parses field with required list' , ( ) => {
333
+ const result = parseCCN ( '{ field[]! }' ) ;
334
+
335
+ expectJSON ( result ) . toDeepNestedProperty (
336
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
337
+ {
338
+ kind : Kind . NON_NULL_ASSERTION ,
339
+ loc : { start : 7 , end : 10 } ,
340
+ nullabilityAssertion : {
341
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
342
+ loc : { start : 7 , end : 9 } ,
343
+ nullabilityAssertion : undefined ,
344
+ } ,
345
+ } ,
346
+ ) ;
347
+ } ) ;
348
+
349
+ it ( 'parses field with optional list' , ( ) => {
350
+ const result = parseCCN ( '{ field[]? }' ) ;
351
+
352
+ expectJSON ( result ) . toDeepNestedProperty (
353
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
354
+ {
355
+ kind : Kind . ERROR_BOUNDARY ,
356
+ loc : { start : 7 , end : 10 } ,
357
+ nullabilityAssertion : {
358
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
359
+ loc : { start : 7 , end : 9 } ,
360
+ nullabilityAssertion : undefined ,
361
+ } ,
362
+ } ,
363
+ ) ;
364
+ } ) ;
365
+
366
+ it ( 'parses multidimensional field with mixed list elements' , ( ) => {
367
+ const result = parseCCN ( '{ field[[[?]!]]! }' ) ;
368
+
369
+ expectJSON ( result ) . toDeepNestedProperty (
370
+ 'definitions[0].selectionSet.selections[0].nullabilityAssertion' ,
371
+ {
372
+ kind : Kind . NON_NULL_ASSERTION ,
373
+ loc : { start : 7 , end : 16 } ,
374
+ nullabilityAssertion : {
375
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
376
+ loc : { start : 7 , end : 15 } ,
377
+ nullabilityAssertion : {
378
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
379
+ loc : { start : 8 , end : 14 } ,
380
+ nullabilityAssertion : {
381
+ kind : Kind . NON_NULL_ASSERTION ,
382
+ loc : { start : 9 , end : 13 } ,
383
+ nullabilityAssertion : {
384
+ kind : Kind . LIST_NULLABILITY_OPERATOR ,
385
+ loc : { start : 9 , end : 12 } ,
386
+ nullabilityAssertion : {
387
+ kind : Kind . ERROR_BOUNDARY ,
388
+ loc : { start : 10 , end : 11 } ,
389
+ nullabilityAssertion : undefined ,
390
+ } ,
391
+ } ,
392
+ } ,
393
+ } ,
394
+ } ,
395
+ } ,
396
+ ) ;
397
+ } ) ;
398
+
399
+ it ( 'does not parse field with unbalanced brackets' , ( ) => {
400
+ expect ( ( ) => parseCCN ( '{ field[[] }' ) ) . to . throw (
401
+ 'Syntax Error: Expected "]", found "}".' ,
402
+ ) ;
403
+
404
+ expect ( ( ) => parseCCN ( '{ field[]] }' ) ) . to . throw (
405
+ 'Syntax Error: Expected Name, found "]".' ,
406
+ ) ;
407
+
408
+ expect ( ( ) => parse ( '{ field] }' ) ) . to . throw (
409
+ 'Syntax Error: Expected Name, found "]".' ,
410
+ ) ;
411
+
412
+ expect ( ( ) => parseCCN ( '{ field[ }' ) ) . to . throw (
413
+ 'Syntax Error: Expected "]", found "}".' ,
414
+ ) ;
415
+ } ) ;
416
+
417
+ it ( 'does not parse field with assorted invalid nullability designators' , ( ) => {
418
+ expect ( ( ) => parseCCN ( '{ field[][] }' ) ) . to . throw (
419
+ 'Syntax Error: Expected Name, found "[".' ,
420
+ ) ;
421
+
422
+ expect ( ( ) => parseCCN ( '{ field[!!] }' ) ) . to . throw (
423
+ 'Syntax Error: Expected "]", found "!".' ,
424
+ ) ;
425
+
426
+ expect ( ( ) => parseCCN ( '{ field[]?! }' ) ) . to . throw (
427
+ 'Syntax Error: Expected Name, found "!".' ,
428
+ ) ;
429
+ } ) ;
430
+
227
431
it ( 'creates ast' , ( ) => {
228
432
const result = parse ( dedent `
229
433
{
@@ -274,6 +478,7 @@ describe('Parser', () => {
274
478
loc : { start : 9 , end : 14 } ,
275
479
} ,
276
480
] ,
481
+ nullabilityAssertion : undefined ,
277
482
directives : [ ] ,
278
483
selectionSet : {
279
484
kind : Kind . SELECTION_SET ,
@@ -289,6 +494,7 @@ describe('Parser', () => {
289
494
value : 'id' ,
290
495
} ,
291
496
arguments : [ ] ,
497
+ nullabilityAssertion : undefined ,
292
498
directives : [ ] ,
293
499
selectionSet : undefined ,
294
500
} ,
@@ -302,6 +508,7 @@ describe('Parser', () => {
302
508
value : 'name' ,
303
509
} ,
304
510
arguments : [ ] ,
511
+ nullabilityAssertion : undefined ,
305
512
directives : [ ] ,
306
513
selectionSet : undefined ,
307
514
} ,
@@ -349,6 +556,7 @@ describe('Parser', () => {
349
556
value : 'node' ,
350
557
} ,
351
558
arguments : [ ] ,
559
+ nullabilityAssertion : undefined ,
352
560
directives : [ ] ,
353
561
selectionSet : {
354
562
kind : Kind . SELECTION_SET ,
@@ -364,6 +572,7 @@ describe('Parser', () => {
364
572
value : 'id' ,
365
573
} ,
366
574
arguments : [ ] ,
575
+ nullabilityAssertion : undefined ,
367
576
directives : [ ] ,
368
577
selectionSet : undefined ,
369
578
} ,
0 commit comments