Passed
Pull Request — master (#586)
by Šimon
12:52
created

SchemaParserTest   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 1093
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 44
eloc 548
c 2
b 0
f 0
dl 0
loc 1093
rs 8.8798

How to fix   Complexity   

Complex Class

Complex classes like SchemaParserTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SchemaParserTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Tests\Language;
6
7
use GraphQL\Error\SyntaxError;
8
use GraphQL\Language\AST\NodeKind;
9
use GraphQL\Language\Parser;
10
use GraphQL\Language\SourceLocation;
11
use PHPUnit\Framework\TestCase;
12
13
class SchemaParserTest extends TestCase
14
{
15
    // Describe: Schema Parser
16
    /**
17
     * @see it('Simple type')
18
     */
19
    public function testSimpleType() : void
20
    {
21
        $body = '
22
type Hello {
23
  world: String
24
}';
25
        $doc  = Parser::parse($body);
26
        $loc  = static function ($start, $end) {
27
            return TestUtils::locArray($start, $end);
28
        };
29
30
        $expected = [
31
            'kind'        => NodeKind::DOCUMENT,
32
            'definitions' => [
33
                [
34
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
35
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
36
                    'interfaces'  => [],
37
                    'directives'  => [],
38
                    'fields'      => [
39
                        $this->fieldNode(
40
                            $this->nameNode('world', $loc(16, 21)),
41
                            $this->typeNode('String', $loc(23, 29)),
42
                            $loc(16, 29)
43
                        ),
44
                    ],
45
                    'loc'         => $loc(1, 31),
46
                    'description' => null,
47
                ],
48
            ],
49
            'loc'         => $loc(0, 31),
50
        ];
51
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
52
    }
53
54
    private function nameNode($name, $loc)
55
    {
56
        return [
57
            'kind'  => NodeKind::NAME,
58
            'value' => $name,
59
            'loc'   => $loc,
60
        ];
61
    }
62
63
    private function fieldNode($name, $type, $loc)
64
    {
65
        return $this->fieldNodeWithArgs($name, $type, [], $loc);
66
    }
67
68
    private function fieldNodeWithArgs($name, $type, $args, $loc)
69
    {
70
        return [
71
            'kind'        => NodeKind::FIELD_DEFINITION,
72
            'name'        => $name,
73
            'arguments'   => $args,
74
            'type'        => $type,
75
            'directives'  => [],
76
            'loc'         => $loc,
77
            'description' => null,
78
        ];
79
    }
80
81
    private function typeNode($name, $loc)
82
    {
83
        return [
84
            'kind' => NodeKind::NAMED_TYPE,
85
            'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc],
86
            'loc'  => $loc,
87
        ];
88
    }
89
90
    /**
91
     * @see it('parses type with description string')
92
     */
93
    public function testParsesTypeWithDescriptionString() : void
94
    {
95
        $body = '
96
"Description"
97
type Hello {
98
  world: String
99
}';
100
        $doc  = Parser::parse($body);
101
        $loc  = static function ($start, $end) {
102
            return TestUtils::locArray($start, $end);
103
        };
104
105
        $expected = [
106
            'kind'        => NodeKind::DOCUMENT,
107
            'definitions' => [
108
                [
109
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
110
                    'name'        => $this->nameNode('Hello', $loc(20, 25)),
111
                    'interfaces'  => [],
112
                    'directives'  => [],
113
                    'fields'      => [
114
                        $this->fieldNode(
115
                            $this->nameNode('world', $loc(30, 35)),
116
                            $this->typeNode('String', $loc(37, 43)),
117
                            $loc(30, 43)
118
                        ),
119
                    ],
120
                    'loc'         => $loc(1, 45),
121
                    'description' => [
122
                        'kind'  => NodeKind::STRING,
123
                        'value' => 'Description',
124
                        'loc'   => $loc(1, 14),
125
                        'block' => false,
126
                    ],
127
                ],
128
            ],
129
            'loc'         => $loc(0, 45),
130
        ];
131
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
132
    }
133
134
    /**
135
     * @see it('parses type with description multi-linestring')
136
     */
137
    public function testParsesTypeWithDescriptionMultiLineString() : void
138
    {
139
        $body = '
140
"""
141
Description
142
"""
143
# Even with comments between them
144
type Hello {
145
  world: String
146
}';
147
        $doc  = Parser::parse($body);
148
        $loc  = static function ($start, $end) {
149
            return TestUtils::locArray($start, $end);
150
        };
151
152
        $expected = [
153
            'kind'        => NodeKind::DOCUMENT,
154
            'definitions' => [
155
                [
156
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
157
                    'name'        => $this->nameNode('Hello', $loc(60, 65)),
158
                    'interfaces'  => [],
159
                    'directives'  => [],
160
                    'fields'      => [
161
                        $this->fieldNode(
162
                            $this->nameNode('world', $loc(70, 75)),
163
                            $this->typeNode('String', $loc(77, 83)),
164
                            $loc(70, 83)
165
                        ),
166
                    ],
167
                    'loc'         => $loc(1, 85),
168
                    'description' => [
169
                        'kind'  => NodeKind::STRING,
170
                        'value' => 'Description',
171
                        'loc'   => $loc(1, 20),
172
                        'block' => true,
173
                    ],
174
                ],
175
            ],
176
            'loc'         => $loc(0, 85),
177
        ];
178
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
179
    }
180
181
    /**
182
     * @see it('Simple extension')
183
     */
184
    public function testSimpleExtension() : void
185
    {
186
        $body = '
187
extend type Hello {
188
  world: String
189
}
190
';
191
        $doc  = Parser::parse($body);
192
        $loc  = static function ($start, $end) {
193
            return TestUtils::locArray($start, $end);
194
        };
195
196
        $expected = [
197
            'kind'        => NodeKind::DOCUMENT,
198
            'definitions' => [
199
                [
200
                    'kind'       => NodeKind::OBJECT_TYPE_EXTENSION,
201
                    'name'       => $this->nameNode('Hello', $loc(13, 18)),
202
                    'interfaces' => [],
203
                    'directives' => [],
204
                    'fields'     => [
205
                        $this->fieldNode(
206
                            $this->nameNode('world', $loc(23, 28)),
207
                            $this->typeNode('String', $loc(30, 36)),
208
                            $loc(23, 36)
209
                        ),
210
                    ],
211
                    'loc'        => $loc(1, 38),
212
                ],
213
            ],
214
            'loc'         => $loc(0, 39),
215
        ];
216
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
217
    }
218
219
    /**
220
     * @see it('Extension without fields')
221
     */
222
    public function testExtensionWithoutFields() : void
223
    {
224
        $body = 'extend type Hello implements Greeting';
225
        $doc  = Parser::parse($body);
226
        $loc  = static function ($start, $end) {
227
            return TestUtils::locArray($start, $end);
228
        };
229
230
        $expected = [
231
            'kind'        => NodeKind::DOCUMENT,
232
            'definitions' => [
233
                [
234
                    'kind'       => NodeKind::OBJECT_TYPE_EXTENSION,
235
                    'name'       => $this->nameNode('Hello', $loc(12, 17)),
236
                    'interfaces' => [
237
                        $this->typeNode('Greeting', $loc(29, 37)),
238
                    ],
239
                    'directives' => [],
240
                    'fields'     => [],
241
                    'loc'        => $loc(0, 37),
242
                ],
243
            ],
244
            'loc'         => $loc(0, 37),
245
        ];
246
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
247
    }
248
249
    /**
250
     * @see it('Extension without fields followed by extension')
251
     */
252
    public function testExtensionWithoutFieldsFollowedByExtension() : void
253
    {
254
        $body     = '
255
          extend type Hello implements Greeting
256
    
257
          extend type Hello implements SecondGreeting
258
        ';
259
        $doc      = Parser::parse($body);
260
        $expected = [
261
            'kind'        => 'Document',
262
            'definitions' => [
263
                [
264
                    'kind'       => 'ObjectTypeExtension',
265
                    'name'       => $this->nameNode('Hello', ['start' => 23, 'end' => 28]),
266
                    'interfaces' => [$this->typeNode('Greeting', ['start' => 40, 'end' => 48])],
267
                    'directives' => [],
268
                    'fields'     => [],
269
                    'loc'        => ['start' => 11, 'end' => 48],
270
                ],
271
                [
272
                    'kind'       => 'ObjectTypeExtension',
273
                    'name'       => $this->nameNode('Hello', ['start' => 76, 'end' => 81]),
274
                    'interfaces' => [$this->typeNode('SecondGreeting', ['start' => 93, 'end' => 107])],
275
                    'directives' => [],
276
                    'fields'     => [],
277
                    'loc'        => ['start' => 64, 'end' => 107],
278
                ],
279
            ],
280
            'loc'         => ['start' => 0, 'end' => 116],
281
        ];
282
        self::assertEquals($expected, $doc->toArray(true));
283
    }
284
285
    /**
286
     * @see it('Extension without anything throws')
287
     */
288
    public function testExtensionWithoutAnythingThrows() : void
289
    {
290
        $this->expectSyntaxError(
291
            'extend type Hello',
292
            'Unexpected <EOF>',
293
            $this->loc(1, 18)
294
        );
295
    }
296
297
    private function expectSyntaxError($text, $message, $location)
298
    {
299
        $this->expectException(SyntaxError::class);
300
        $this->expectExceptionMessage($message);
301
        try {
302
            Parser::parse($text);
303
        } catch (SyntaxError $error) {
304
            self::assertEquals([$location], $error->getLocations());
305
            throw $error;
306
        }
307
    }
308
309
    private function loc($line, $column)
310
    {
311
        return new SourceLocation($line, $column);
312
    }
313
314
    /**
315
     * @see it('Extension do not include descriptions')
316
     */
317
    public function testExtensionDoNotIncludeDescriptions() : void
318
    {
319
        $body = '
320
      "Description"
321
      extend type Hello {
322
        world: String
323
      }';
324
        $this->expectSyntaxError(
325
            $body,
326
            'Unexpected Name "extend"',
327
            $this->loc(3, 7)
328
        );
329
    }
330
331
    /**
332
     * @see it('Extension do not include descriptions')
333
     */
334
    public function testExtensionDoNotIncludeDescriptions2() : void
335
    {
336
        $body = '
337
      extend "Description" type Hello {
338
        world: String
339
      }
340
}';
341
        $this->expectSyntaxError(
342
            $body,
343
            'Unexpected String "Description"',
344
            $this->loc(2, 14)
345
        );
346
    }
347
348
    /**
349
     * @see it('Simple non-null type')
350
     */
351
    public function testSimpleNonNullType() : void
352
    {
353
        $body = '
354
type Hello {
355
  world: String!
356
}';
357
        $doc  = Parser::parse($body);
358
359
        $loc = static function ($start, $end) {
360
            return TestUtils::locArray($start, $end);
361
        };
362
363
        $expected = [
364
            'kind'        => NodeKind::DOCUMENT,
365
            'definitions' => [
366
                [
367
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
368
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
369
                    'interfaces'  => [],
370
                    'directives'  => [],
371
                    'fields'      => [
372
                        $this->fieldNode(
373
                            $this->nameNode('world', $loc(16, 21)),
374
                            [
375
                                'kind' => NodeKind::NON_NULL_TYPE,
376
                                'type' => $this->typeNode('String', $loc(23, 29)),
377
                                'loc'  => $loc(23, 30),
378
                            ],
379
                            $loc(16, 30)
380
                        ),
381
                    ],
382
                    'loc'         => $loc(1, 32),
383
                    'description' => null,
384
                ],
385
            ],
386
            'loc'         => $loc(0, 32),
387
        ];
388
389
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
390
    }
391
392
    /**
393
     * @see it('Simple type inheriting interface')
394
     */
395
    public function testSimpleTypeInheritingInterface() : void
396
    {
397
        $body = 'type Hello implements World { field: String }';
398
        $doc  = Parser::parse($body);
399
        $loc  = static function ($start, $end) {
400
            return TestUtils::locArray($start, $end);
401
        };
402
403
        $expected = [
404
            'kind'        => NodeKind::DOCUMENT,
405
            'definitions' => [
406
                [
407
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
408
                    'name'        => $this->nameNode('Hello', $loc(5, 10)),
409
                    'interfaces'  => [
410
                        $this->typeNode('World', $loc(22, 27)),
411
                    ],
412
                    'directives'  => [],
413
                    'fields'      => [
414
                        $this->fieldNode(
415
                            $this->nameNode('field', $loc(30, 35)),
416
                            $this->typeNode('String', $loc(37, 43)),
417
                            $loc(30, 43)
418
                        ),
419
                    ],
420
                    'loc'         => $loc(0, 45),
421
                    'description' => null,
422
                ],
423
            ],
424
            'loc'         => $loc(0, 45),
425
        ];
426
427
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
428
    }
429
430
    /**
431
     * @see it('Simple type inheriting multiple interfaces')
432
     */
433
    public function testSimpleTypeInheritingMultipleInterfaces() : void
434
    {
435
        $body = 'type Hello implements Wo & rld { field: String }';
436
        $doc  = Parser::parse($body);
437
        $loc  = static function ($start, $end) {
438
            return TestUtils::locArray($start, $end);
439
        };
440
441
        $expected = [
442
            'kind'        => NodeKind::DOCUMENT,
443
            'definitions' => [
444
                [
445
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
446
                    'name'        => $this->nameNode('Hello', $loc(5, 10)),
447
                    'interfaces'  => [
448
                        $this->typeNode('Wo', $loc(22, 24)),
449
                        $this->typeNode('rld', $loc(27, 30)),
450
                    ],
451
                    'directives'  => [],
452
                    'fields'      => [
453
                        $this->fieldNode(
454
                            $this->nameNode('field', $loc(33, 38)),
455
                            $this->typeNode('String', $loc(40, 46)),
456
                            $loc(33, 46)
457
                        ),
458
                    ],
459
                    'loc'         => $loc(0, 48),
460
                    'description' => null,
461
                ],
462
            ],
463
            'loc'         => $loc(0, 48),
464
        ];
465
466
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
467
    }
468
469
    /**
470
     * @see it('Simple type inheriting multiple interfaces with leading ampersand')
471
     */
472
    public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand() : void
473
    {
474
        $body = 'type Hello implements & Wo & rld { field: String }';
475
        $doc  = Parser::parse($body);
476
        $loc  = static function ($start, $end) {
477
            return TestUtils::locArray($start, $end);
478
        };
479
480
        $expected = [
481
            'kind'        => 'Document',
482
            'definitions' => [
483
                [
484
                    'kind'        => 'ObjectTypeDefinition',
485
                    'name'        => $this->nameNode('Hello', $loc(5, 10)),
486
                    'interfaces'  => [
487
                        $this->typeNode('Wo', $loc(24, 26)),
488
                        $this->typeNode('rld', $loc(29, 32)),
489
                    ],
490
                    'directives'  => [],
491
                    'fields'      => [
492
                        $this->fieldNode(
493
                            $this->nameNode('field', $loc(35, 40)),
494
                            $this->typeNode('String', $loc(42, 48)),
495
                            $loc(35, 48)
496
                        ),
497
                    ],
498
                    'loc'         => $loc(0, 50),
499
                    'description' => null,
500
                ],
501
            ],
502
            'loc'         => $loc(0, 50),
503
        ];
504
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
505
    }
506
507
    /**
508
     * @see it('Single value enum')
509
     */
510
    public function testSingleValueEnum() : void
511
    {
512
        $body = 'enum Hello { WORLD }';
513
        $doc  = Parser::parse($body);
514
        $loc  = static function ($start, $end) {
515
            return TestUtils::locArray($start, $end);
516
        };
517
518
        $expected = [
519
            'kind'        => NodeKind::DOCUMENT,
520
            'definitions' => [
521
                [
522
                    'kind'        => NodeKind::ENUM_TYPE_DEFINITION,
523
                    'name'        => $this->nameNode('Hello', $loc(5, 10)),
524
                    'directives'  => [],
525
                    'values'      => [$this->enumValueNode('WORLD', $loc(13, 18))],
526
                    'loc'         => $loc(0, 20),
527
                    'description' => null,
528
                ],
529
            ],
530
            'loc'         => $loc(0, 20),
531
        ];
532
533
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
534
    }
535
536
    private function enumValueNode($name, $loc)
537
    {
538
        return [
539
            'kind'        => NodeKind::ENUM_VALUE_DEFINITION,
540
            'name'        => $this->nameNode($name, $loc),
541
            'directives'  => [],
542
            'loc'         => $loc,
543
            'description' => null,
544
        ];
545
    }
546
547
    /**
548
     * @see it('Double value enum')
549
     */
550
    public function testDoubleValueEnum() : void
551
    {
552
        $body = 'enum Hello { WO, RLD }';
553
        $doc  = Parser::parse($body);
554
        $loc  = static function ($start, $end) {
555
            return TestUtils::locArray($start, $end);
556
        };
557
558
        $expected = [
559
            'kind'        => NodeKind::DOCUMENT,
560
            'definitions' => [
561
                [
562
                    'kind'        => NodeKind::ENUM_TYPE_DEFINITION,
563
                    'name'        => $this->nameNode('Hello', $loc(5, 10)),
564
                    'directives'  => [],
565
                    'values'      => [
566
                        $this->enumValueNode('WO', $loc(13, 15)),
567
                        $this->enumValueNode('RLD', $loc(17, 20)),
568
                    ],
569
                    'loc'         => $loc(0, 22),
570
                    'description' => null,
571
                ],
572
            ],
573
            'loc'         => $loc(0, 22),
574
        ];
575
576
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
577
    }
578
579
    /**
580
     * @see it('Simple interface')
581
     */
582
    public function testSimpleInterface() : void
583
    {
584
        $body = '
585
interface Hello {
586
  world: String
587
}';
588
        $doc  = Parser::parse($body);
589
        $loc  = static function ($start, $end) {
590
            return TestUtils::locArray($start, $end);
591
        };
592
593
        $expected = [
594
            'kind'        => NodeKind::DOCUMENT,
595
            'definitions' => [
596
                [
597
                    'kind'        => NodeKind::INTERFACE_TYPE_DEFINITION,
598
                    'name'        => $this->nameNode('Hello', $loc(11, 16)),
599
                    'directives'  => [],
600
                    'fields'      => [
601
                        $this->fieldNode(
602
                            $this->nameNode('world', $loc(21, 26)),
603
                            $this->typeNode('String', $loc(28, 34)),
604
                            $loc(21, 34)
605
                        ),
606
                    ],
607
                    'loc'         => $loc(1, 36),
608
                    'description' => null,
609
                ],
610
            ],
611
            'loc'         => $loc(0, 36),
612
        ];
613
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
614
    }
615
616
    /**
617
     * @see it('Simple field with arg')
618
     */
619
    public function testSimpleFieldWithArg() : void
620
    {
621
        $body = '
622
type Hello {
623
  world(flag: Boolean): String
624
}';
625
        $doc  = Parser::parse($body);
626
        $loc  = static function ($start, $end) {
627
            return TestUtils::locArray($start, $end);
628
        };
629
630
        $expected = [
631
            'kind'        => NodeKind::DOCUMENT,
632
            'definitions' => [
633
                [
634
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
635
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
636
                    'interfaces'  => [],
637
                    'directives'  => [],
638
                    'fields'      => [
639
                        $this->fieldNodeWithArgs(
640
                            $this->nameNode('world', $loc(16, 21)),
641
                            $this->typeNode('String', $loc(38, 44)),
642
                            [
643
                                $this->inputValueNode(
644
                                    $this->nameNode('flag', $loc(22, 26)),
645
                                    $this->typeNode('Boolean', $loc(28, 35)),
646
                                    null,
647
                                    $loc(22, 35)
648
                                ),
649
                            ],
650
                            $loc(16, 44)
651
                        ),
652
                    ],
653
                    'loc'         => $loc(1, 46),
654
                    'description' => null,
655
                ],
656
            ],
657
            'loc'         => $loc(0, 46),
658
        ];
659
660
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
661
    }
662
663
    private function inputValueNode($name, $type, $defaultValue, $loc)
664
    {
665
        return [
666
            'kind'         => NodeKind::INPUT_VALUE_DEFINITION,
667
            'name'         => $name,
668
            'type'         => $type,
669
            'defaultValue' => $defaultValue,
670
            'directives'   => [],
671
            'loc'          => $loc,
672
            'description'  => null,
673
        ];
674
    }
675
676
    /**
677
     * @see it('Simple field with arg with default value')
678
     */
679
    public function testSimpleFieldWithArgWithDefaultValue() : void
680
    {
681
        $body = '
682
type Hello {
683
  world(flag: Boolean = true): String
684
}';
685
        $doc  = Parser::parse($body);
686
        $loc  = static function ($start, $end) {
687
            return TestUtils::locArray($start, $end);
688
        };
689
690
        $expected = [
691
            'kind'        => NodeKind::DOCUMENT,
692
            'definitions' => [
693
                [
694
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
695
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
696
                    'interfaces'  => [],
697
                    'directives'  => [],
698
                    'fields'      => [
699
                        $this->fieldNodeWithArgs(
700
                            $this->nameNode('world', $loc(16, 21)),
701
                            $this->typeNode('String', $loc(45, 51)),
702
                            [
703
                                $this->inputValueNode(
704
                                    $this->nameNode('flag', $loc(22, 26)),
705
                                    $this->typeNode('Boolean', $loc(28, 35)),
706
                                    ['kind' => NodeKind::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)],
707
                                    $loc(22, 42)
708
                                ),
709
                            ],
710
                            $loc(16, 51)
711
                        ),
712
                    ],
713
                    'loc'         => $loc(1, 53),
714
                    'description' => null,
715
                ],
716
            ],
717
            'loc'         => $loc(0, 53),
718
        ];
719
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
720
    }
721
722
    /**
723
     * @see it('Simple field with list arg')
724
     */
725
    public function testSimpleFieldWithListArg() : void
726
    {
727
        $body = '
728
type Hello {
729
  world(things: [String]): String
730
}';
731
        $doc  = Parser::parse($body);
732
        $loc  = static function ($start, $end) {
733
            return TestUtils::locArray($start, $end);
734
        };
735
736
        $expected = [
737
            'kind'        => NodeKind::DOCUMENT,
738
            'definitions' => [
739
                [
740
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
741
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
742
                    'interfaces'  => [],
743
                    'directives'  => [],
744
                    'fields'      => [
745
                        $this->fieldNodeWithArgs(
746
                            $this->nameNode('world', $loc(16, 21)),
747
                            $this->typeNode('String', $loc(41, 47)),
748
                            [
749
                                $this->inputValueNode(
750
                                    $this->nameNode('things', $loc(22, 28)),
751
                                    [
752
                                        'kind'   => NodeKind::LIST_TYPE,
753
                                        'type'   => $this->typeNode(
754
                                            'String',
755
                                            $loc(31, 37)
756
                                        ), 'loc' => $loc(30, 38),
757
                                    ],
758
                                    null,
759
                                    $loc(22, 38)
760
                                ),
761
                            ],
762
                            $loc(16, 47)
763
                        ),
764
                    ],
765
                    'loc'         => $loc(1, 49),
766
                    'description' => null,
767
                ],
768
            ],
769
            'loc'         => $loc(0, 49),
770
        ];
771
772
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
773
    }
774
775
    /**
776
     * @see it('Simple field with two args')
777
     */
778
    public function testSimpleFieldWithTwoArgs() : void
779
    {
780
        $body = '
781
type Hello {
782
  world(argOne: Boolean, argTwo: Int): String
783
}';
784
        $doc  = Parser::parse($body);
785
        $loc  = static function ($start, $end) {
786
            return TestUtils::locArray($start, $end);
787
        };
788
789
        $expected = [
790
            'kind'        => NodeKind::DOCUMENT,
791
            'definitions' => [
792
                [
793
                    'kind'        => NodeKind::OBJECT_TYPE_DEFINITION,
794
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
795
                    'interfaces'  => [],
796
                    'directives'  => [],
797
                    'fields'      => [
798
                        $this->fieldNodeWithArgs(
799
                            $this->nameNode('world', $loc(16, 21)),
800
                            $this->typeNode('String', $loc(53, 59)),
801
                            [
802
                                $this->inputValueNode(
803
                                    $this->nameNode('argOne', $loc(22, 28)),
804
                                    $this->typeNode('Boolean', $loc(30, 37)),
805
                                    null,
806
                                    $loc(22, 37)
807
                                ),
808
                                $this->inputValueNode(
809
                                    $this->nameNode('argTwo', $loc(39, 45)),
810
                                    $this->typeNode('Int', $loc(47, 50)),
811
                                    null,
812
                                    $loc(39, 50)
813
                                ),
814
                            ],
815
                            $loc(16, 59)
816
                        ),
817
                    ],
818
                    'loc'         => $loc(1, 61),
819
                    'description' => null,
820
                ],
821
            ],
822
            'loc'         => $loc(0, 61),
823
        ];
824
825
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
826
    }
827
828
    /**
829
     * @see it('Simple union')
830
     */
831
    public function testSimpleUnion() : void
832
    {
833
        $body = 'union Hello = World';
834
        $doc  = Parser::parse($body);
835
        $loc  = static function ($start, $end) {
836
            return TestUtils::locArray($start, $end);
837
        };
838
839
        $expected = [
840
            'kind'        => NodeKind::DOCUMENT,
841
            'definitions' => [
842
                [
843
                    'kind'        => NodeKind::UNION_TYPE_DEFINITION,
844
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
845
                    'directives'  => [],
846
                    'types'       => [$this->typeNode('World', $loc(14, 19))],
847
                    'loc'         => $loc(0, 19),
848
                    'description' => null,
849
                ],
850
            ],
851
            'loc'         => $loc(0, 19),
852
        ];
853
854
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
855
    }
856
857
    /**
858
     * @see it('Union with two types')
859
     */
860
    public function testUnionWithTwoTypes() : void
861
    {
862
        $body = 'union Hello = Wo | Rld';
863
        $doc  = Parser::parse($body);
864
        $loc  = static function ($start, $end) {
865
            return TestUtils::locArray($start, $end);
866
        };
867
868
        $expected = [
869
            'kind'        => NodeKind::DOCUMENT,
870
            'definitions' => [
871
                [
872
                    'kind'        => NodeKind::UNION_TYPE_DEFINITION,
873
                    'name'        => $this->nameNode('Hello', $loc(6, 11)),
874
                    'directives'  => [],
875
                    'types'       => [
876
                        $this->typeNode('Wo', $loc(14, 16)),
877
                        $this->typeNode('Rld', $loc(19, 22)),
878
                    ],
879
                    'loc'         => $loc(0, 22),
880
                    'description' => null,
881
                ],
882
            ],
883
            'loc'         => $loc(0, 22),
884
        ];
885
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
886
    }
887
888
    /**
889
     * @see it('Union with two types and leading pipe')
890
     */
891
    public function testUnionWithTwoTypesAndLeadingPipe() : void
892
    {
893
        $body     = 'union Hello = | Wo | Rld';
894
        $doc      = Parser::parse($body);
895
        $expected = [
896
            'kind'        => 'Document',
897
            'definitions' => [
898
                [
899
                    'kind'        => 'UnionTypeDefinition',
900
                    'name'        => $this->nameNode('Hello', ['start' => 6, 'end' => 11]),
901
                    'directives'  => [],
902
                    'types'       => [
903
                        $this->typeNode('Wo', ['start' => 16, 'end' => 18]),
904
                        $this->typeNode('Rld', ['start' => 21, 'end' => 24]),
905
                    ],
906
                    'loc'         => ['start' => 0, 'end' => 24],
907
                    'description' => null,
908
                ],
909
            ],
910
            'loc'         => ['start' => 0, 'end' => 24],
911
        ];
912
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
913
    }
914
915
    /**
916
     * @see it('Union fails with no types')
917
     */
918
    public function testUnionFailsWithNoTypes() : void
919
    {
920
        $this->expectSyntaxError(
921
            'union Hello = |',
922
            'Expected Name, found <EOF>',
923
            $this->loc(1, 16)
924
        );
925
    }
926
927
    /**
928
     * @see it('Union fails with leading douple pipe')
929
     */
930
    public function testUnionFailsWithLeadingDoublePipe() : void
931
    {
932
        $this->expectSyntaxError(
933
            'union Hello = || Wo | Rld',
934
            'Expected Name, found |',
935
            $this->loc(1, 16)
936
        );
937
    }
938
939
    /**
940
     * @see it('Union fails with double pipe')
941
     */
942
    public function testUnionFailsWithDoublePipe() : void
943
    {
944
        $this->expectSyntaxError(
945
            'union Hello = Wo || Rld',
946
            'Expected Name, found |',
947
            $this->loc(1, 19)
948
        );
949
    }
950
951
    /**
952
     * @see it('Union fails with trailing pipe')
953
     */
954
    public function testUnionFailsWithTrailingPipe() : void
955
    {
956
        $this->expectSyntaxError(
957
            'union Hello = | Wo | Rld |',
958
            'Expected Name, found <EOF>',
959
            $this->loc(1, 27)
960
        );
961
    }
962
963
    /**
964
     * @see it('Scalar')
965
     */
966
    public function testScalar() : void
967
    {
968
        $body = 'scalar Hello';
969
        $doc  = Parser::parse($body);
970
        $loc  = static function ($start, $end) {
971
            return TestUtils::locArray($start, $end);
972
        };
973
974
        $expected = [
975
            'kind'        => NodeKind::DOCUMENT,
976
            'definitions' => [
977
                [
978
                    'kind'        => NodeKind::SCALAR_TYPE_DEFINITION,
979
                    'name'        => $this->nameNode('Hello', $loc(7, 12)),
980
                    'directives'  => [],
981
                    'loc'         => $loc(0, 12),
982
                    'description' => null,
983
                ],
984
            ],
985
            'loc'         => $loc(0, 12),
986
        ];
987
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
988
    }
989
990
    /**
991
     * @see it('Simple input object')
992
     */
993
    public function testSimpleInputObject() : void
994
    {
995
        $body = '
996
input Hello {
997
  world: String
998
}';
999
        $doc  = Parser::parse($body);
1000
        $loc  = static function ($start, $end) {
1001
            return TestUtils::locArray($start, $end);
1002
        };
1003
1004
        $expected = [
1005
            'kind'        => NodeKind::DOCUMENT,
1006
            'definitions' => [
1007
                [
1008
                    'kind'        => NodeKind::INPUT_OBJECT_TYPE_DEFINITION,
1009
                    'name'        => $this->nameNode('Hello', $loc(7, 12)),
1010
                    'directives'  => [],
1011
                    'fields'      => [
1012
                        $this->inputValueNode(
1013
                            $this->nameNode('world', $loc(17, 22)),
1014
                            $this->typeNode('String', $loc(24, 30)),
1015
                            null,
1016
                            $loc(17, 30)
1017
                        ),
1018
                    ],
1019
                    'loc'         => $loc(1, 32),
1020
                    'description' => null,
1021
                ],
1022
            ],
1023
            'loc'         => $loc(0, 32),
1024
        ];
1025
        self::assertEquals($expected, TestUtils::nodeToArray($doc));
1026
    }
1027
1028
    /**
1029
     * @see it('Simple input object with args should fail')
1030
     */
1031
    public function testSimpleInputObjectWithArgsShouldFail() : void
1032
    {
1033
        $body = '
1034
      input Hello {
1035
        world(foo: Int): String
1036
      }';
1037
        $this->expectSyntaxError(
1038
            $body,
1039
            'Expected :, found (',
1040
            $this->loc(3, 14)
1041
        );
1042
    }
1043
1044
    /**
1045
     * @see it('Directive with incorrect locations')
1046
     */
1047
    public function testDirectiveWithIncorrectLocationShouldFail() : void
1048
    {
1049
        $body = '
1050
      directive @foo on FIELD | INCORRECT_LOCATION
1051
';
1052
        $this->expectSyntaxError(
1053
            $body,
1054
            'Unexpected Name "INCORRECT_LOCATION"',
1055
            $this->loc(2, 33)
1056
        );
1057
    }
1058
1059
    public function testDoesNotAllowEmptyFields() : void
1060
    {
1061
        $body = 'type Hello { }';
1062
        $this->expectSyntaxError($body, 'Syntax Error: Expected Name, found }', new SourceLocation(1, 14));
1063
    }
1064
1065
    /**
1066
     * @see it('Option: allowLegacySDLEmptyFields supports type with empty fields')
1067
     */
1068
    public function testAllowLegacySDLEmptyFieldsOption() : void
1069
    {
1070
        $body     = 'type Hello { }';
1071
        $doc      = Parser::parse($body, ['allowLegacySDLEmptyFields' => true]);
1072
        $expected = [
1073
            'definitions' => [
1074
                [
1075
                    'fields' => [],
1076
                ],
1077
            ],
1078
        ];
1079
        self::assertArraySubset($expected, $doc->toArray(true));
1080
    }
1081
1082
    public function testDoesntAllowLegacySDLImplementsInterfacesByDefault() : void
1083
    {
1084
        $body = 'type Hello implements Wo rld { field: String }';
1085
        $this->expectSyntaxError($body, 'Syntax Error: Unexpected Name "rld"', new SourceLocation(1, 26));
1086
    }
1087
1088
    /**
1089
     * @see it('Option: allowLegacySDLImplementsInterfaces')
1090
     */
1091
    public function testDefaultSDLImplementsInterfaces() : void
1092
    {
1093
        $body     = 'type Hello implements Wo rld { field: String }';
1094
        $doc      = Parser::parse($body, ['allowLegacySDLImplementsInterfaces' => true]);
1095
        $expected = [
1096
            'definitions' => [
1097
                [
1098
                    'interfaces' => [
1099
                        $this->typeNode('Wo', ['start' => 22, 'end' => 24]),
1100
                        $this->typeNode('rld', ['start' => 25, 'end' => 28]),
1101
                    ],
1102
                ],
1103
            ],
1104
        ];
1105
        self::assertArraySubset($expected, $doc->toArray(true));
1106
    }
1107
}
1108