Fix CS in test/Language

This commit is contained in:
Simon Podlipsky 2018-09-01 20:21:08 +02:00
parent ec54d6152b
commit d1f49bedbd
No known key found for this signature in database
GPG Key ID: 725C2BD962B42663
9 changed files with 2157 additions and 1836 deletions

View File

@ -1,13 +1,18 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Language; namespace GraphQL\Tests\Language;
use GraphQL\Error\SyntaxError;
use GraphQL\Language\Lexer; use GraphQL\Language\Lexer;
use GraphQL\Language\Source; use GraphQL\Language\Source;
use GraphQL\Language\SourceLocation; use GraphQL\Language\SourceLocation;
use GraphQL\Language\Token; use GraphQL\Language\Token;
use GraphQL\Error\SyntaxError;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function count;
use function json_decode;
class LexerTest extends TestCase class LexerTest extends TestCase
{ {
@ -23,6 +28,34 @@ class LexerTest extends TestCase
); );
} }
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
$this->lexOne($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
}
}
/**
* @param string $body
* @return Token
*/
private function lexOne($body)
{
$lexer = new Lexer(new Source($body));
return $lexer->advance();
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
/** /**
* @see it('accepts BOM header') * @see it('accepts BOM header')
*/ */
@ -33,7 +66,7 @@ class LexerTest extends TestCase
'kind' => Token::NAME, 'kind' => Token::NAME,
'start' => 2, 'start' => 2,
'end' => 5, 'end' => 5,
'value' => 'foo' 'value' => 'foo',
]; ];
$this->assertArraySubset($expected, (array) $this->lexOne($bom . ' foo')); $this->assertArraySubset($expected, (array) $this->lexOne($bom . ' foo'));
@ -50,7 +83,7 @@ class LexerTest extends TestCase
'end' => 11, 'end' => 11,
'line' => 4, 'line' => 4,
'column' => 3, 'column' => 3,
'value' => 'foo' 'value' => 'foo',
]; ];
$this->assertArraySubset($expected, (array) $this->lexOne("\n \r\n \r foo\n")); $this->assertArraySubset($expected, (array) $this->lexOne("\n \r\n \r foo\n"));
} }
@ -70,7 +103,7 @@ class LexerTest extends TestCase
'kind' => Token::NAME, 'kind' => Token::NAME,
'start' => 6, 'start' => 6,
'end' => 9, 'end' => 9,
'value' => 'foo' 'value' => 'foo',
]; ];
$this->assertArraySubset($expected, (array) $this->lexOne($example1)); $this->assertArraySubset($expected, (array) $this->lexOne($example1));
@ -83,7 +116,7 @@ class LexerTest extends TestCase
'kind' => Token::NAME, 'kind' => Token::NAME,
'start' => 18, 'start' => 18,
'end' => 21, 'end' => 21,
'value' => 'foo' 'value' => 'foo',
]; ];
$this->assertArraySubset($expected, (array) $this->lexOne($example2)); $this->assertArraySubset($expected, (array) $this->lexOne($example2));
@ -91,7 +124,7 @@ class LexerTest extends TestCase
'kind' => Token::NAME, 'kind' => Token::NAME,
'start' => 3, 'start' => 3,
'end' => 6, 'end' => 6,
'value' => 'foo' 'value' => 'foo',
]; ];
$example3 = ',,,foo,,,'; $example3 = ',,,foo,,,';
@ -181,63 +214,86 @@ class LexerTest extends TestCase
*/ */
public function testLexesStrings() : void public function testLexesStrings() : void
{ {
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 8, 'end' => 8,
'value' => 'simple' 'value' => 'simple',
], (array) $this->lexOne('"simple"')); ],
(array) $this->lexOne('"simple"')
);
$this->assertArraySubset(
$this->assertArraySubset([ [
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 15, 'end' => 15,
'value' => ' white space ' 'value' => ' white space ',
], (array) $this->lexOne('" white space "')); ],
(array) $this->lexOne('" white space "')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 10, 'end' => 10,
'value' => 'quote "' 'value' => 'quote "',
], (array) $this->lexOne('"quote \\""')); ],
(array) $this->lexOne('"quote \\""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 25, 'end' => 25,
'value' => 'escaped \n\r\b\t\f' 'value' => 'escaped \n\r\b\t\f',
], (array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"')); ],
(array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 16, 'end' => 16,
'value' => 'slashes \\ \/' 'value' => 'slashes \\ \/',
], (array) $this->lexOne('"slashes \\\\ \\\\/"')); ],
(array) $this->lexOne('"slashes \\\\ \\\\/"')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 13, 'end' => 13,
'value' => 'unicode яуц' 'value' => 'unicode яуц',
], (array) $this->lexOne('"unicode яуц"')); ],
(array) $this->lexOne('"unicode яуц"')
);
$unicode = json_decode('"\u1234\u5678\u90AB\uCDEF"'); $unicode = json_decode('"\u1234\u5678\u90AB\uCDEF"');
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 34, 'end' => 34,
'value' => 'unicode ' . $unicode 'value' => 'unicode ' . $unicode,
], (array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"')); ],
(array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::STRING, 'kind' => Token::STRING,
'start' => 0, 'start' => 0,
'end' => 26, 'end' => 26,
'value' => $unicode 'value' => $unicode,
], (array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"')); ],
(array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"')
);
} }
/** /**
@ -245,86 +301,128 @@ class LexerTest extends TestCase
*/ */
public function testLexesBlockString() : void public function testLexesBlockString() : void
{ {
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 12, 'end' => 12,
'value' => 'simple' 'value' => 'simple',
], (array) $this->lexOne('"""simple"""')); ],
(array) $this->lexOne('"""simple"""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 19, 'end' => 19,
'value' => ' white space ' 'value' => ' white space ',
], (array) $this->lexOne('""" white space """')); ],
(array) $this->lexOne('""" white space """')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 22, 'end' => 22,
'value' => 'contains " quote' 'value' => 'contains " quote',
], (array) $this->lexOne('"""contains " quote"""')); ],
(array) $this->lexOne('"""contains " quote"""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 31, 'end' => 31,
'value' => 'contains """ triplequote' 'value' => 'contains """ triplequote',
], (array) $this->lexOne('"""contains \\""" triplequote"""')); ],
(array) $this->lexOne('"""contains \\""" triplequote"""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 16, 'end' => 16,
'value' => "multi\nline" 'value' => "multi\nline",
], (array) $this->lexOne("\"\"\"multi\nline\"\"\"")); ],
(array) $this->lexOne("\"\"\"multi\nline\"\"\"")
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 28, 'end' => 28,
'value' => "multi\nline\nnormalized" 'value' => "multi\nline\nnormalized",
], (array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"")); ],
(array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"")
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 32, 'end' => 32,
'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234' 'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234',
], (array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""')); ],
(array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 19, 'end' => 19,
'value' => 'slashes \\\\ \\/' 'value' => 'slashes \\\\ \\/',
], (array) $this->lexOne('"""slashes \\\\ \\/"""')); ],
(array) $this->lexOne('"""slashes \\\\ \\/"""')
);
$this->assertArraySubset([ $this->assertArraySubset(
[
'kind' => Token::BLOCK_STRING, 'kind' => Token::BLOCK_STRING,
'start' => 0, 'start' => 0,
'end' => 68, 'end' => 68,
'value' => "spans\n multiple\n lines" 'value' => "spans\n multiple\n lines",
], (array) $this->lexOne("\"\"\" ],
(array) $this->lexOne('"""
spans spans
multiple multiple
lines lines
\"\"\"")); """')
);
} }
public function reportsUsefulStringErrors() { public function reportsUsefulStringErrors()
{
return [ return [
['"', "Unterminated string.", $this->loc(1, 2)], ['"', 'Unterminated string.', $this->loc(1, 2)],
['"no end quote', "Unterminated string.", $this->loc(1, 14)], ['"no end quote', 'Unterminated string.', $this->loc(1, 14)],
["'single quotes'", "Unexpected single quote character ('), did you mean to use a double quote (\")?", $this->loc(1, 1)], [
['"contains unescaped \u0007 control char"', "Invalid character within String: \"\\u0007\"", $this->loc(1, 21)], "'single quotes'",
"Unexpected single quote character ('), did you mean to use a double quote (\")?",
$this->loc(
1,
1
),
],
[
'"contains unescaped \u0007 control char"',
"Invalid character within String: \"\\u0007\"",
$this->loc(
1,
21
),
],
['"null-byte is not \u0000 end of file"', 'Invalid character within String: "\\u0000"', $this->loc(1, 19)], ['"null-byte is not \u0000 end of file"', 'Invalid character within String: "\\u0000"', $this->loc(1, 19)],
['"multi' . "\n" . 'line"', "Unterminated string.", $this->loc(1, 7)], ['"multi' . "\n" . 'line"', 'Unterminated string.', $this->loc(1, 7)],
['"multi' . "\r" . 'line"', "Unterminated string.", $this->loc(1, 7)], ['"multi' . "\r" . 'line"', 'Unterminated string.', $this->loc(1, 7)],
['"bad \\z esc"', "Invalid character escape sequence: \\z", $this->loc(1, 7)], ['"bad \\z esc"', 'Invalid character escape sequence: \\z', $this->loc(1, 7)],
['"bad \\x esc"', "Invalid character escape sequence: \\x", $this->loc(1, 7)], ['"bad \\x esc"', "Invalid character escape sequence: \\x", $this->loc(1, 7)],
['"bad \\u1 esc"', "Invalid character escape sequence: \\u1 es", $this->loc(1, 7)], ['"bad \\u1 esc"', "Invalid character escape sequence: \\u1 es", $this->loc(1, 7)],
['"bad \\u0XX1 esc"', "Invalid character escape sequence: \\u0XX1", $this->loc(1, 7)], ['"bad \\u0XX1 esc"', "Invalid character escape sequence: \\u0XX1", $this->loc(1, 7)],
@ -343,12 +441,27 @@ class LexerTest extends TestCase
$this->expectSyntaxError($str, $expectedMessage, $location); $this->expectSyntaxError($str, $expectedMessage, $location);
} }
public function reportsUsefulBlockStringErrors() { public function reportsUsefulBlockStringErrors()
{
return [ return [
['"""', "Unterminated string.", $this->loc(1, 4)], ['"""', 'Unterminated string.', $this->loc(1, 4)],
['"""no end quote', "Unterminated string.", $this->loc(1, 16)], ['"""no end quote', 'Unterminated string.', $this->loc(1, 16)],
['"""contains unescaped ' . json_decode('"\u0007"') . ' control char"""', "Invalid character within String: \"\\u0007\"", $this->loc(1, 23)], [
['"""null-byte is not ' . json_decode('"\u0000"') . ' end of file"""', "Invalid character within String: \"\\u0000\"", $this->loc(1, 21)], '"""contains unescaped ' . json_decode('"\u0007"') . ' control char"""',
"Invalid character within String: \"\\u0007\"",
$this->loc(
1,
23
),
],
[
'"""null-byte is not ' . json_decode('"\u0000"') . ' end of file"""',
"Invalid character within String: \"\\u0000\"",
$this->loc(
1,
21
),
],
]; ];
} }
@ -435,15 +548,15 @@ class LexerTest extends TestCase
public function reportsUsefulNumberErrors() public function reportsUsefulNumberErrors()
{ {
return [ return [
[ '00', "Invalid number, unexpected digit after 0: \"0\"", $this->loc(1, 2)], ['00', 'Invalid number, unexpected digit after 0: "0"', $this->loc(1, 2)],
[ '+1', "Cannot parse the unexpected character \"+\".", $this->loc(1, 1)], ['+1', 'Cannot parse the unexpected character "+".', $this->loc(1, 1)],
[ '1.', "Invalid number, expected digit but got: <EOF>", $this->loc(1, 3)], ['1.', 'Invalid number, expected digit but got: <EOF>', $this->loc(1, 3)],
[ '1.e1', "Invalid number, expected digit but got: \"e\"", $this->loc(1, 3)], ['1.e1', 'Invalid number, expected digit but got: "e"', $this->loc(1, 3)],
[ '.123', "Cannot parse the unexpected character \".\".", $this->loc(1, 1)], ['.123', 'Cannot parse the unexpected character ".".', $this->loc(1, 1)],
[ '1.A', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 3)], ['1.A', 'Invalid number, expected digit but got: "A"', $this->loc(1, 3)],
[ '-A', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 2)], ['-A', 'Invalid number, expected digit but got: "A"', $this->loc(1, 2)],
[ '1.0e', "Invalid number, expected digit but got: <EOF>", $this->loc(1, 5)], ['1.0e', 'Invalid number, expected digit but got: <EOF>', $this->loc(1, 5)],
[ '1.0eA', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 5)], ['1.0eA', 'Invalid number, expected digit but got: "A"', $this->loc(1, 5)],
]; ];
} }
@ -521,8 +634,8 @@ class LexerTest extends TestCase
$unicode2 = json_decode('"\u200b"'); $unicode2 = json_decode('"\u200b"');
return [ return [
['..', "Cannot parse the unexpected character \".\".", $this->loc(1, 1)], ['..', 'Cannot parse the unexpected character ".".', $this->loc(1, 1)],
['?', "Cannot parse the unexpected character \"?\".", $this->loc(1, 1)], ['?', 'Cannot parse the unexpected character "?".', $this->loc(1, 1)],
[$unicode1, "Cannot parse the unexpected character \"\\u203b\".", $this->loc(1, 1)], [$unicode1, "Cannot parse the unexpected character \"\\u203b\".", $this->loc(1, 1)],
[$unicode2, "Cannot parse the unexpected character \"\\u200b\".", $this->loc(1, 1)], [$unicode2, "Cannot parse the unexpected character \"\\u200b\".", $this->loc(1, 1)],
]; ];
@ -544,7 +657,10 @@ class LexerTest extends TestCase
{ {
$q = 'a-b'; $q = 'a-b';
$lexer = new Lexer(new Source($q)); $lexer = new Lexer(new Source($q));
$this->assertArraySubset(['kind' => Token::NAME, 'start' => 0, 'end' => 1, 'value' => 'a'], (array) $lexer->advance()); $this->assertArraySubset(
['kind' => Token::NAME, 'start' => 0, 'end' => 1, 'value' => 'a'],
(array) $lexer->advance()
);
$this->expectException(SyntaxError::class); $this->expectException(SyntaxError::class);
$this->expectExceptionMessage('Syntax Error: Invalid number, expected digit but got: "b"'); $this->expectExceptionMessage('Syntax Error: Invalid number, expected digit but got: "b"');
@ -587,42 +703,21 @@ class LexerTest extends TestCase
$tokens[] = $tok; $tokens[] = $tok;
} }
$this->assertEquals([ $this->assertEquals(
[
'<SOF>', '<SOF>',
'{', '{',
'Comment', 'Comment',
'Name', 'Name',
'}', '}',
'<EOF>' '<EOF>',
], Utils::map($tokens, function ($tok) { ],
Utils::map(
$tokens,
function ($tok) {
return $tok->kind; return $tok->kind;
}));
}
/**
* @param string $body
* @return Token
*/
private function lexOne($body)
{
$lexer = new Lexer(new Source($body));
return $lexer->advance();
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
$this->lexOne($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
} }
)
);
} }
} }

View File

@ -1,7 +1,11 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Language; namespace GraphQL\Tests\Language;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Error\SyntaxError;
use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\FieldNode; use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\NameNode; use GraphQL\Language\AST\NameNode;
@ -13,9 +17,10 @@ use GraphQL\Language\AST\StringValueNode;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\Source; use GraphQL\Language\Source;
use GraphQL\Language\SourceLocation; use GraphQL\Language\SourceLocation;
use GraphQL\Error\SyntaxError;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function file_get_contents;
use function sprintf;
class ParserTest extends TestCase class ParserTest extends TestCase
{ {
@ -43,13 +48,26 @@ class ParserTest extends TestCase
public function parseProvidesUsefulErrors() public function parseProvidesUsefulErrors()
{ {
return [ return [
['{', "Syntax Error: Expected Name, found <EOF>", "Syntax Error: Expected Name, found <EOF>\n\nGraphQL request (1:2)\n1: {\n ^\n", [1], [new SourceLocation(1, 2)]], [
['{ ...MissingOn } '{',
'Syntax Error: Expected Name, found <EOF>',
"Syntax Error: Expected Name, found <EOF>\n\nGraphQL request (1:2)\n1: {\n ^\n",
[1],
[new SourceLocation(
1,
2
),
],
],
[
'{ ...MissingOn }
fragment MissingOn Type fragment MissingOn Type
', "Syntax Error: Expected \"on\", found Name \"Type\"", "Syntax Error: Expected \"on\", found Name \"Type\"\n\nGraphQL request (2:20)\n1: { ...MissingOn }\n2: fragment MissingOn Type\n ^\n3: \n",], ', 'Syntax Error: Expected "on", found Name "Type"',
['{ field: {} }', "Syntax Error: Expected Name, found {", "Syntax Error: Expected Name, found {\n\nGraphQL request (1:10)\n1: { field: {} }\n ^\n"], "Syntax Error: Expected \"on\", found Name \"Type\"\n\nGraphQL request (2:20)\n1: { ...MissingOn }\n2: fragment MissingOn Type\n ^\n3: \n",
['notanoperation Foo { field }', "Syntax Error: Unexpected Name \"notanoperation\"", "Syntax Error: Unexpected Name \"notanoperation\"\n\nGraphQL request (1:1)\n1: notanoperation Foo { field }\n ^\n"], ],
['...', "Syntax Error: Unexpected ...", "Syntax Error: Unexpected ...\n\nGraphQL request (1:1)\n1: ...\n ^\n"], ['{ field: {} }', 'Syntax Error: Expected Name, found {', "Syntax Error: Expected Name, found {\n\nGraphQL request (1:10)\n1: { field: {} }\n ^\n"],
['notanoperation Foo { field }', 'Syntax Error: Unexpected Name "notanoperation"', "Syntax Error: Unexpected Name \"notanoperation\"\n\nGraphQL request (1:1)\n1: notanoperation Foo { field }\n ^\n"],
['...', 'Syntax Error: Unexpected ...', "Syntax Error: Unexpected ...\n\nGraphQL request (1:1)\n1: ...\n ^\n"],
]; ];
} }
@ -57,8 +75,13 @@ fragment MissingOn Type
* @dataProvider parseProvidesUsefulErrors * @dataProvider parseProvidesUsefulErrors
* @see it('parse provides useful errors') * @see it('parse provides useful errors')
*/ */
public function testParseProvidesUsefulErrors($str, $expectedMessage, $stringRepresentation, $expectedPositions = null, $expectedLocations = null) : void public function testParseProvidesUsefulErrors(
{ $str,
$expectedMessage,
$stringRepresentation,
$expectedPositions = null,
$expectedLocations = null
) : void {
try { try {
Parser::parse($str); Parser::parse($str);
$this->fail('Expected exception not thrown'); $this->fail('Expected exception not thrown');
@ -114,6 +137,23 @@ fragment MissingOn Type
); );
} }
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
Parser::parse($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
}
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
/** /**
* @see it('does not accept fragments spread of "on"') * @see it('does not accept fragments spread of "on"')
*/ */
@ -160,14 +200,14 @@ HEREDOC;
'arguments' => new NodeList([ 'arguments' => new NodeList([
new ArgumentNode([ new ArgumentNode([
'name' => new NameNode(['value' => 'arg']), 'name' => new NameNode(['value' => 'arg']),
'value' => new StringValueNode([ 'value' => new StringValueNode(
'value' => "Has a $char multi-byte character." ['value' => sprintf('Has a %s multi-byte character.', $char)]
]) ),
]) ]),
]),
'directives' => new NodeList([]),
]),
]), ]),
'directives' => new NodeList([])
])
])
]); ]);
$this->assertEquals($expected, $result->definitions[0]->selectionSet); $this->assertEquals($expected, $result->definitions[0]->selectionSet);
@ -196,7 +236,7 @@ HEREDOC;
'mutation', 'mutation',
'subscription', 'subscription',
'true', 'true',
'false' 'false',
]; ];
foreach ($nonKeywords as $keyword) { foreach ($nonKeywords as $keyword) {
$fragmentName = $keyword; $fragmentName = $keyword;
@ -205,14 +245,19 @@ HEREDOC;
} }
// Expected not to throw: // Expected not to throw:
$result = Parser::parse("query $keyword { $result = Parser::parse(<<<GRAPHQL
query $keyword {
... $fragmentName ... $fragmentName
... on $keyword { field } ... on $keyword { field }
} }
fragment $fragmentName on Type { fragment $fragmentName on Type {
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword) $keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
} }
"); fragment $fragmentName on Type {
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
}
GRAPHQL
);
$this->assertNotEmpty($result); $this->assertNotEmpty($result);
} }
} }
@ -289,7 +334,7 @@ fragment $fragmentName on Type {
$loc = function ($start, $end) use ($source) { $loc = function ($start, $end) use ($source) {
return [ return [
'start' => $start, 'start' => $start,
'end' => $end 'end' => $end,
]; ];
}; };
@ -315,7 +360,7 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(4, 8), 'loc' => $loc(4, 8),
'value' => 'node' 'value' => 'node',
], ],
'arguments' => [ 'arguments' => [
[ [
@ -323,15 +368,15 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(9, 11), 'loc' => $loc(9, 11),
'value' => 'id' 'value' => 'id',
], ],
'value' => [ 'value' => [
'kind' => NodeKind::INT, 'kind' => NodeKind::INT,
'loc' => $loc(13, 14), 'loc' => $loc(13, 14),
'value' => '4' 'value' => '4',
],
'loc' => $loc(9, 14, $source),
], ],
'loc' => $loc(9, 14, $source)
]
], ],
'directives' => [], 'directives' => [],
'selectionSet' => [ 'selectionSet' => [
@ -345,11 +390,11 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(22, 24), 'loc' => $loc(22, 24),
'value' => 'id' 'value' => 'id',
], ],
'arguments' => [], 'arguments' => [],
'directives' => [], 'directives' => [],
'selectionSet' => null 'selectionSet' => null,
], ],
[ [
'kind' => NodeKind::FIELD, 'kind' => NodeKind::FIELD,
@ -358,22 +403,30 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(30, 34), 'loc' => $loc(30, 34),
'value' => 'name' 'value' => 'name',
], ],
'arguments' => [], 'arguments' => [],
'directives' => [], 'directives' => [],
'selectionSet' => null 'selectionSet' => null,
] ],
] ],
] ],
] ],
] ],
] ],
] ],
] ],
]; ];
$this->assertEquals($expected, $this->nodeToArray($result)); $this->assertEquals($expected, self::nodeToArray($result));
}
/**
* @return mixed[]
*/
public static function nodeToArray(Node $node) : array
{
return TestUtils::nodeToArray($node);
} }
/** /**
@ -392,7 +445,7 @@ fragment $fragmentName on Type {
$loc = function ($start, $end) use ($source) { $loc = function ($start, $end) use ($source) {
return [ return [
'start' => $start, 'start' => $start,
'end' => $end 'end' => $end,
]; ];
}; };
@ -418,7 +471,7 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(10, 14), 'loc' => $loc(10, 14),
'value' => 'node' 'value' => 'node',
], ],
'arguments' => [], 'arguments' => [],
'directives' => [], 'directives' => [],
@ -433,19 +486,19 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => $loc(21, 23), 'loc' => $loc(21, 23),
'value' => 'id' 'value' => 'id',
], ],
'arguments' => [], 'arguments' => [],
'directives' => [], 'directives' => [],
'selectionSet' => null 'selectionSet' => null,
] ],
] ],
] ],
] ],
] ],
] ],
] ],
] ],
]; ];
$this->assertEquals($expected, $this->nodeToArray($result)); $this->assertEquals($expected, $this->nodeToArray($result));
@ -475,6 +528,8 @@ fragment $fragmentName on Type {
Parser::parse($source); Parser::parse($source);
} }
// Describe: parseValue
/** /**
* @see it('contains location information that only stringifys start/end') * @see it('contains location information that only stringifys start/end')
*/ */
@ -495,6 +550,8 @@ fragment $fragmentName on Type {
$this->assertEquals($source, $result->loc->source); $this->assertEquals($source, $result->loc->source);
} }
// Describe: parseType
/** /**
* @see it('contains references to start and end tokens') * @see it('contains references to start and end tokens')
*/ */
@ -506,17 +563,18 @@ fragment $fragmentName on Type {
$this->assertEquals('<EOF>', $result->loc->endToken->kind); $this->assertEquals('<EOF>', $result->loc->endToken->kind);
} }
// Describe: parseValue
/** /**
* @see it('parses null value') * @see it('parses null value')
*/ */
public function testParsesNullValues() : void public function testParsesNullValues() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::NULL, 'kind' => NodeKind::NULL,
'loc' => ['start' => 0, 'end' => 4] 'loc' => ['start' => 0, 'end' => 4],
], $this->nodeToArray(Parser::parseValue('null'))); ],
$this->nodeToArray(Parser::parseValue('null'))
);
} }
/** /**
@ -524,41 +582,45 @@ fragment $fragmentName on Type {
*/ */
public function testParsesListValues() : void public function testParsesListValues() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::LST, 'kind' => NodeKind::LST,
'loc' => ['start' => 0, 'end' => 11], 'loc' => ['start' => 0, 'end' => 11],
'values' => [ 'values' => [
[ [
'kind' => NodeKind::INT, 'kind' => NodeKind::INT,
'loc' => ['start' => 1, 'end' => 4], 'loc' => ['start' => 1, 'end' => 4],
'value' => '123' 'value' => '123',
], ],
[ [
'kind' => NodeKind::STRING, 'kind' => NodeKind::STRING,
'loc' => ['start' => 5, 'end' => 10], 'loc' => ['start' => 5, 'end' => 10],
'value' => 'abc', 'value' => 'abc',
'block' => false 'block' => false,
] ],
] ],
], $this->nodeToArray(Parser::parseValue('[123 "abc"]'))); ],
$this->nodeToArray(Parser::parseValue('[123 "abc"]'))
);
} }
// Describe: parseType
/** /**
* @see it('parses well known types') * @see it('parses well known types')
*/ */
public function testParsesWellKnownTypes() : void public function testParsesWellKnownTypes() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::NAMED_TYPE, 'kind' => NodeKind::NAMED_TYPE,
'loc' => ['start' => 0, 'end' => 6], 'loc' => ['start' => 0, 'end' => 6],
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => ['start' => 0, 'end' => 6], 'loc' => ['start' => 0, 'end' => 6],
'value' => 'String' 'value' => 'String',
] ],
], $this->nodeToArray(Parser::parseType('String'))); ],
$this->nodeToArray(Parser::parseType('String'))
);
} }
/** /**
@ -566,15 +628,18 @@ fragment $fragmentName on Type {
*/ */
public function testParsesCustomTypes() : void public function testParsesCustomTypes() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::NAMED_TYPE, 'kind' => NodeKind::NAMED_TYPE,
'loc' => ['start' => 0, 'end' => 6], 'loc' => ['start' => 0, 'end' => 6],
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => ['start' => 0, 'end' => 6], 'loc' => ['start' => 0, 'end' => 6],
'value' => 'MyType' 'value' => 'MyType',
] ],
], $this->nodeToArray(Parser::parseType('MyType'))); ],
$this->nodeToArray(Parser::parseType('MyType'))
);
} }
/** /**
@ -582,7 +647,8 @@ fragment $fragmentName on Type {
*/ */
public function testParsesListTypes() : void public function testParsesListTypes() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::LIST_TYPE, 'kind' => NodeKind::LIST_TYPE,
'loc' => ['start' => 0, 'end' => 8], 'loc' => ['start' => 0, 'end' => 8],
'type' => [ 'type' => [
@ -591,10 +657,12 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => ['start' => 1, 'end' => 7], 'loc' => ['start' => 1, 'end' => 7],
'value' => 'MyType' 'value' => 'MyType',
] ],
] ],
], $this->nodeToArray(Parser::parseType('[MyType]'))); ],
$this->nodeToArray(Parser::parseType('[MyType]'))
);
} }
/** /**
@ -602,7 +670,8 @@ fragment $fragmentName on Type {
*/ */
public function testParsesNonNullTypes() : void public function testParsesNonNullTypes() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::NON_NULL_TYPE, 'kind' => NodeKind::NON_NULL_TYPE,
'loc' => ['start' => 0, 'end' => 7], 'loc' => ['start' => 0, 'end' => 7],
'type' => [ 'type' => [
@ -611,10 +680,12 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => ['start' => 0, 'end' => 6], 'loc' => ['start' => 0, 'end' => 6],
'value' => 'MyType' 'value' => 'MyType',
] ],
] ],
], $this->nodeToArray(Parser::parseType('MyType!'))); ],
$this->nodeToArray(Parser::parseType('MyType!'))
);
} }
/** /**
@ -622,7 +693,8 @@ fragment $fragmentName on Type {
*/ */
public function testParsesNestedTypes() : void public function testParsesNestedTypes() : void
{ {
$this->assertEquals([ $this->assertEquals(
[
'kind' => NodeKind::LIST_TYPE, 'kind' => NodeKind::LIST_TYPE,
'loc' => ['start' => 0, 'end' => 9], 'loc' => ['start' => 0, 'end' => 9],
'type' => [ 'type' => [
@ -634,36 +706,12 @@ fragment $fragmentName on Type {
'name' => [ 'name' => [
'kind' => NodeKind::NAME, 'kind' => NodeKind::NAME,
'loc' => ['start' => 1, 'end' => 7], 'loc' => ['start' => 1, 'end' => 7],
'value' => 'MyType' 'value' => 'MyType',
] ],
] ],
] ],
], $this->nodeToArray(Parser::parseType('[MyType!]'))); ],
} $this->nodeToArray(Parser::parseType('[MyType!]'))
);
/**
* @param Node $node
* @return array
*/
public static function nodeToArray(Node $node)
{
return TestUtils::nodeToArray($node);
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
Parser::parse($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
}
} }
} }

View File

@ -1,18 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Language; namespace GraphQL\Tests\Language;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\AST\EnumValueNode;
use GraphQL\Language\AST\FieldNode; use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\NameNode; use GraphQL\Language\AST\NameNode;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\AST\StringValueNode;
use GraphQL\Language\AST\VariableNode;
use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\Printer; use GraphQL\Language\Printer;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function file_get_contents;
class PrinterTest extends TestCase class PrinterTest extends TestCase
{ {
@ -160,7 +157,8 @@ END;
*/ */
public function testExperimentalCorrectlyPrintsFragmentDefinedVariables() : void public function testExperimentalCorrectlyPrintsFragmentDefinedVariables() : void
{ {
$fragmentWithVariable = Parser::parse(' $fragmentWithVariable = Parser::parse(
'
fragment Foo($a: ComplexType, $b: Boolean = false) on TestType { fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
id id
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Language; namespace GraphQL\Tests\Language;
use GraphQL\Error\SyntaxError; use GraphQL\Error\SyntaxError;
@ -10,7 +13,6 @@ use PHPUnit\Framework\TestCase;
class SchemaParserTest extends TestCase class SchemaParserTest extends TestCase
{ {
// Describe: Schema Parser // Describe: Schema Parser
/** /**
* @see it('Simple type') * @see it('Simple type')
*/ */
@ -21,7 +23,9 @@ type Hello {
world: String world: String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -36,17 +40,53 @@ type Hello {
$this->nameNode('world', $loc(16, 21)), $this->nameNode('world', $loc(16, 21)),
$this->typeNode('String', $loc(23, 29)), $this->typeNode('String', $loc(23, 29)),
$loc(16, 29) $loc(16, 29)
) ),
], ],
'loc' => $loc(1, 31), 'loc' => $loc(1, 31),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 31) ],
'loc' => $loc(0, 31),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
private function nameNode($name, $loc)
{
return [
'kind' => NodeKind::NAME,
'value' => $name,
'loc' => $loc,
];
}
private function fieldNode($name, $type, $loc)
{
return $this->fieldNodeWithArgs($name, $type, [], $loc);
}
private function fieldNodeWithArgs($name, $type, $args, $loc)
{
return [
'kind' => NodeKind::FIELD_DEFINITION,
'name' => $name,
'arguments' => $args,
'type' => $type,
'directives' => [],
'loc' => $loc,
'description' => null,
];
}
private function typeNode($name, $loc)
{
return [
'kind' => NodeKind::NAMED_TYPE,
'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc],
'loc' => $loc,
];
}
/** /**
* @see it('parses type with description string') * @see it('parses type with description string')
*/ */
@ -58,7 +98,9 @@ type Hello {
world: String world: String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -73,18 +115,18 @@ type Hello {
$this->nameNode('world', $loc(30, 35)), $this->nameNode('world', $loc(30, 35)),
$this->typeNode('String', $loc(37, 43)), $this->typeNode('String', $loc(37, 43)),
$loc(30, 43) $loc(30, 43)
) ),
], ],
'loc' => $loc(1, 45), 'loc' => $loc(1, 45),
'description' => [ 'description' => [
'kind' => NodeKind::STRING, 'kind' => NodeKind::STRING,
'value' => 'Description', 'value' => 'Description',
'loc' => $loc(1, 14), 'loc' => $loc(1, 14),
'block' => false 'block' => false,
]
]
], ],
'loc' => $loc(0, 45) ],
],
'loc' => $loc(0, 45),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -103,7 +145,9 @@ type Hello {
world: String world: String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -118,18 +162,18 @@ type Hello {
$this->nameNode('world', $loc(70, 75)), $this->nameNode('world', $loc(70, 75)),
$this->typeNode('String', $loc(77, 83)), $this->typeNode('String', $loc(77, 83)),
$loc(70, 83) $loc(70, 83)
) ),
], ],
'loc' => $loc(1, 85), 'loc' => $loc(1, 85),
'description' => [ 'description' => [
'kind' => NodeKind::STRING, 'kind' => NodeKind::STRING,
'value' => 'Description', 'value' => 'Description',
'loc' => $loc(1, 20), 'loc' => $loc(1, 20),
'block' => true 'block' => true,
]
]
], ],
'loc' => $loc(0, 85) ],
],
'loc' => $loc(0, 85),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -148,6 +192,7 @@ extend type Hello {
$loc = function ($start, $end) { $loc = function ($start, $end) {
return TestUtils::locArray($start, $end); return TestUtils::locArray($start, $end);
}; };
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
'definitions' => [ 'definitions' => [
@ -161,12 +206,12 @@ extend type Hello {
$this->nameNode('world', $loc(23, 28)), $this->nameNode('world', $loc(23, 28)),
$this->typeNode('String', $loc(30, 36)), $this->typeNode('String', $loc(30, 36)),
$loc(23, 36) $loc(23, 36)
) ),
], ],
'loc' => $loc(1, 38) 'loc' => $loc(1, 38),
]
], ],
'loc' => $loc(0, 39) ],
'loc' => $loc(0, 39),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -181,6 +226,7 @@ extend type Hello {
$loc = function ($start, $end) { $loc = function ($start, $end) {
return TestUtils::locArray($start, $end); return TestUtils::locArray($start, $end);
}; };
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
'definitions' => [ 'definitions' => [
@ -192,10 +238,10 @@ extend type Hello {
], ],
'directives' => [], 'directives' => [],
'fields' => [], 'fields' => [],
'loc' => $loc(0, 37) 'loc' => $loc(0, 37),
]
], ],
'loc' => $loc(0, 37) ],
'loc' => $loc(0, 37),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -248,6 +294,23 @@ extend type Hello {
); );
} }
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
Parser::parse($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
}
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
/** /**
* @see it('Extension do not include descriptions') * @see it('Extension do not include descriptions')
*/ */
@ -291,10 +354,11 @@ extend type Hello {
type Hello { type Hello {
world: String! world: String!
}'; }';
$doc = Parser::parse($body);
$loc = function ($start, $end) { $loc = function ($start, $end) {
return TestUtils::locArray($start, $end); return TestUtils::locArray($start, $end);
}; };
$doc = Parser::parse($body);
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -310,16 +374,16 @@ type Hello {
[ [
'kind' => NodeKind::NON_NULL_TYPE, 'kind' => NodeKind::NON_NULL_TYPE,
'type' => $this->typeNode('String', $loc(23, 29)), 'type' => $this->typeNode('String', $loc(23, 29)),
'loc' => $loc(23, 30) 'loc' => $loc(23, 30),
], ],
$loc(16, 30) $loc(16, 30)
) ),
], ],
'loc' => $loc(1, 32), 'loc' => $loc(1, 32),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0,32) ],
'loc' => $loc(0, 32),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -331,8 +395,10 @@ type Hello {
public function testSimpleTypeInheritingInterface() : void public function testSimpleTypeInheritingInterface() : void
{ {
$body = 'type Hello implements World { field: String }'; $body = 'type Hello implements World { field: String }';
$loc = function($start, $end) { return TestUtils::locArray($start, $end); };
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -341,7 +407,7 @@ type Hello {
'kind' => NodeKind::OBJECT_TYPE_DEFINITION, 'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
'name' => $this->nameNode('Hello', $loc(5, 10)), 'name' => $this->nameNode('Hello', $loc(5, 10)),
'interfaces' => [ 'interfaces' => [
$this->typeNode('World', $loc(22, 27)) $this->typeNode('World', $loc(22, 27)),
], ],
'directives' => [], 'directives' => [],
'fields' => [ 'fields' => [
@ -349,13 +415,13 @@ type Hello {
$this->nameNode('field', $loc(30, 35)), $this->nameNode('field', $loc(30, 35)),
$this->typeNode('String', $loc(37, 43)), $this->typeNode('String', $loc(37, 43)),
$loc(30, 43) $loc(30, 43)
) ),
], ],
'loc' => $loc(0, 45), 'loc' => $loc(0, 45),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 45) ],
'loc' => $loc(0, 45),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -367,8 +433,10 @@ type Hello {
public function testSimpleTypeInheritingMultipleInterfaces() : void public function testSimpleTypeInheritingMultipleInterfaces() : void
{ {
$body = 'type Hello implements Wo & rld { field: String }'; $body = 'type Hello implements Wo & rld { field: String }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -378,7 +446,7 @@ type Hello {
'name' => $this->nameNode('Hello', $loc(5, 10)), 'name' => $this->nameNode('Hello', $loc(5, 10)),
'interfaces' => [ 'interfaces' => [
$this->typeNode('Wo', $loc(22, 24)), $this->typeNode('Wo', $loc(22, 24)),
$this->typeNode('rld', $loc(27, 30)) $this->typeNode('rld', $loc(27, 30)),
], ],
'directives' => [], 'directives' => [],
'fields' => [ 'fields' => [
@ -386,13 +454,13 @@ type Hello {
$this->nameNode('field', $loc(33, 38)), $this->nameNode('field', $loc(33, 38)),
$this->typeNode('String', $loc(40, 46)), $this->typeNode('String', $loc(40, 46)),
$loc(33, 46) $loc(33, 46)
) ),
], ],
'loc' => $loc(0, 48), 'loc' => $loc(0, 48),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 48) ],
'loc' => $loc(0, 48),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -404,8 +472,11 @@ type Hello {
public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand() : void public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand() : void
{ {
$body = 'type Hello implements & Wo & rld { field: String }'; $body = 'type Hello implements & Wo & rld { field: String }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => 'Document', 'kind' => 'Document',
'definitions' => [ 'definitions' => [
@ -439,8 +510,10 @@ type Hello {
public function testSingleValueEnum() : void public function testSingleValueEnum() : void
{ {
$body = 'enum Hello { WORLD }'; $body = 'enum Hello { WORLD }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -451,23 +524,36 @@ type Hello {
'directives' => [], 'directives' => [],
'values' => [$this->enumValueNode('WORLD', $loc(13, 18))], 'values' => [$this->enumValueNode('WORLD', $loc(13, 18))],
'loc' => $loc(0, 20), 'loc' => $loc(0, 20),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 20) ],
'loc' => $loc(0, 20),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
private function enumValueNode($name, $loc)
{
return [
'kind' => NodeKind::ENUM_VALUE_DEFINITION,
'name' => $this->nameNode($name, $loc),
'directives' => [],
'loc' => $loc,
'description' => null,
];
}
/** /**
* @see it('Double value enum') * @see it('Double value enum')
*/ */
public function testDoubleValueEnum() : void public function testDoubleValueEnum() : void
{ {
$body = 'enum Hello { WO, RLD }'; $body = 'enum Hello { WO, RLD }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -478,13 +564,13 @@ type Hello {
'directives' => [], 'directives' => [],
'values' => [ 'values' => [
$this->enumValueNode('WO', $loc(13, 15)), $this->enumValueNode('WO', $loc(13, 15)),
$this->enumValueNode('RLD', $loc(17, 20)) $this->enumValueNode('RLD', $loc(17, 20)),
], ],
'loc' => $loc(0, 22), 'loc' => $loc(0, 22),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 22) ],
'loc' => $loc(0, 22),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -500,7 +586,9 @@ interface Hello {
world: String world: String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -514,13 +602,13 @@ interface Hello {
$this->nameNode('world', $loc(21, 26)), $this->nameNode('world', $loc(21, 26)),
$this->typeNode('String', $loc(28, 34)), $this->typeNode('String', $loc(28, 34)),
$loc(21, 34) $loc(21, 34)
) ),
], ],
'loc' => $loc(1, 36), 'loc' => $loc(1, 36),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0,36) ],
'loc' => $loc(0, 36),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -535,7 +623,9 @@ type Hello {
world(flag: Boolean): String world(flag: Boolean): String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -555,21 +645,34 @@ type Hello {
$this->typeNode('Boolean', $loc(28, 35)), $this->typeNode('Boolean', $loc(28, 35)),
null, null,
$loc(22, 35) $loc(22, 35)
) ),
], ],
$loc(16, 44) $loc(16, 44)
) ),
], ],
'loc' => $loc(1, 46), 'loc' => $loc(1, 46),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 46) ],
'loc' => $loc(0, 46),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
private function inputValueNode($name, $type, $defaultValue, $loc)
{
return [
'kind' => NodeKind::INPUT_VALUE_DEFINITION,
'name' => $name,
'type' => $type,
'defaultValue' => $defaultValue,
'directives' => [],
'loc' => $loc,
'description' => null,
];
}
/** /**
* @see it('Simple field with arg with default value') * @see it('Simple field with arg with default value')
*/ */
@ -580,7 +683,9 @@ type Hello {
world(flag: Boolean = true): String world(flag: Boolean = true): String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -600,16 +705,16 @@ type Hello {
$this->typeNode('Boolean', $loc(28, 35)), $this->typeNode('Boolean', $loc(28, 35)),
['kind' => NodeKind::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)], ['kind' => NodeKind::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)],
$loc(22, 42) $loc(22, 42)
) ),
], ],
$loc(16, 51) $loc(16, 51)
) ),
], ],
'loc' => $loc(1, 53), 'loc' => $loc(1, 53),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 53) ],
'loc' => $loc(0, 53),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -624,7 +729,9 @@ type Hello {
world(things: [String]): String world(things: [String]): String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -641,19 +748,25 @@ type Hello {
[ [
$this->inputValueNode( $this->inputValueNode(
$this->nameNode('things', $loc(22, 28)), $this->nameNode('things', $loc(22, 28)),
['kind' => NodeKind::LIST_TYPE, 'type' => $this->typeNode('String', $loc(31, 37)), 'loc' => $loc(30, 38)], [
'kind' => NodeKind::LIST_TYPE,
'type' => $this->typeNode(
'String',
$loc(31, 37)
), 'loc' => $loc(30, 38),
],
null, null,
$loc(22, 38) $loc(22, 38)
) ),
], ],
$loc(16, 47) $loc(16, 47)
) ),
], ],
'loc' => $loc(1, 49), 'loc' => $loc(1, 49),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 49) ],
'loc' => $loc(0, 49),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -669,7 +782,9 @@ type Hello {
world(argOne: Boolean, argTwo: Int): String world(argOne: Boolean, argTwo: Int): String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -695,16 +810,16 @@ type Hello {
$this->typeNode('Int', $loc(47, 50)), $this->typeNode('Int', $loc(47, 50)),
null, null,
$loc(39, 50) $loc(39, 50)
) ),
], ],
$loc(16, 59) $loc(16, 59)
) ),
], ],
'loc' => $loc(1, 61), 'loc' => $loc(1, 61),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 61) ],
'loc' => $loc(0, 61),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -717,7 +832,10 @@ type Hello {
{ {
$body = 'union Hello = World'; $body = 'union Hello = World';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
'definitions' => [ 'definitions' => [
@ -727,10 +845,10 @@ type Hello {
'directives' => [], 'directives' => [],
'types' => [$this->typeNode('World', $loc(14, 19))], 'types' => [$this->typeNode('World', $loc(14, 19))],
'loc' => $loc(0, 19), 'loc' => $loc(0, 19),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 19) ],
'loc' => $loc(0, 19),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
@ -743,7 +861,9 @@ type Hello {
{ {
$body = 'union Hello = Wo | Rld'; $body = 'union Hello = Wo | Rld';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -754,18 +874,17 @@ type Hello {
'directives' => [], 'directives' => [],
'types' => [ 'types' => [
$this->typeNode('Wo', $loc(14, 16)), $this->typeNode('Wo', $loc(14, 16)),
$this->typeNode('Rld', $loc(19, 22)) $this->typeNode('Rld', $loc(19, 22)),
], ],
'loc' => $loc(0, 22), 'loc' => $loc(0, 22),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 22) ],
'loc' => $loc(0, 22),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
/** /**
* @see it('Union with two types and leading pipe') * @see it('Union with two types and leading pipe')
*/ */
@ -785,8 +904,8 @@ type Hello {
$this->typeNode('Rld', ['start' => 21, 'end' => 24]), $this->typeNode('Rld', ['start' => 21, 'end' => 24]),
], ],
'loc' => ['start' => 0, 'end' => 24], 'loc' => ['start' => 0, 'end' => 24],
'description' => null 'description' => null,
] ],
], ],
'loc' => ['start' => 0, 'end' => 24], 'loc' => ['start' => 0, 'end' => 24],
]; ];
@ -848,7 +967,10 @@ type Hello {
{ {
$body = 'scalar Hello'; $body = 'scalar Hello';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
'definitions' => [ 'definitions' => [
@ -857,10 +979,10 @@ type Hello {
'name' => $this->nameNode('Hello', $loc(7, 12)), 'name' => $this->nameNode('Hello', $loc(7, 12)),
'directives' => [], 'directives' => [],
'loc' => $loc(0, 12), 'loc' => $loc(0, 12),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 12) ],
'loc' => $loc(0, 12),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -875,7 +997,9 @@ input Hello {
world: String world: String
}'; }';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$loc = function($start, $end) {return TestUtils::locArray($start, $end);}; $loc = function ($start, $end) {
return TestUtils::locArray($start, $end);
};
$expected = [ $expected = [
'kind' => NodeKind::DOCUMENT, 'kind' => NodeKind::DOCUMENT,
@ -890,13 +1014,13 @@ input Hello {
$this->typeNode('String', $loc(24, 30)), $this->typeNode('String', $loc(24, 30)),
null, null,
$loc(17, 30) $loc(17, 30)
) ),
], ],
'loc' => $loc(1, 32), 'loc' => $loc(1, 32),
'description' => null 'description' => null,
]
], ],
'loc' => $loc(0, 32) ],
'loc' => $loc(0, 32),
]; ];
$this->assertEquals($expected, TestUtils::nodeToArray($doc)); $this->assertEquals($expected, TestUtils::nodeToArray($doc));
} }
@ -980,81 +1104,4 @@ input Hello {
]; ];
$this->assertArraySubset($expected, $doc->toArray(true)); $this->assertArraySubset($expected, $doc->toArray(true));
} }
private function typeNode($name, $loc)
{
return [
'kind' => NodeKind::NAMED_TYPE,
'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc],
'loc' => $loc
];
}
private function nameNode($name, $loc)
{
return [
'kind' => NodeKind::NAME,
'value' => $name,
'loc' => $loc
];
}
private function fieldNode($name, $type, $loc)
{
return $this->fieldNodeWithArgs($name, $type, [], $loc);
}
private function fieldNodeWithArgs($name, $type, $args, $loc)
{
return [
'kind' => NodeKind::FIELD_DEFINITION,
'name' => $name,
'arguments' => $args,
'type' => $type,
'directives' => [],
'loc' => $loc,
'description' => null
];
}
private function enumValueNode($name, $loc)
{
return [
'kind' => NodeKind::ENUM_VALUE_DEFINITION,
'name' => $this->nameNode($name, $loc),
'directives' => [],
'loc' => $loc,
'description' => null
];
}
private function inputValueNode($name, $type, $defaultValue, $loc)
{
return [
'kind' => NodeKind::INPUT_VALUE_DEFINITION,
'name' => $name,
'type' => $type,
'defaultValue' => $defaultValue,
'directives' => [],
'loc' => $loc,
'description' => null
];
}
private function loc($line, $column)
{
return new SourceLocation($line, $column);
}
private function expectSyntaxError($text, $message, $location)
{
$this->expectException(SyntaxError::class);
$this->expectExceptionMessage($message);
try {
Parser::parse($text);
} catch (SyntaxError $error) {
$this->assertEquals([$location], $error->getLocations());
throw $error;
}
}
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests; namespace GraphQL\Tests;
use GraphQL\Language\AST\NameNode; use GraphQL\Language\AST\NameNode;
@ -7,6 +10,7 @@ use GraphQL\Language\Parser;
use GraphQL\Language\Printer; use GraphQL\Language\Printer;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Throwable; use Throwable;
use function file_get_contents;
class SchemaPrinterTest extends TestCase class SchemaPrinterTest extends TestCase
{ {
@ -16,7 +20,7 @@ class SchemaPrinterTest extends TestCase
public function testPrintsMinimalAst() : void public function testPrintsMinimalAst() : void
{ {
$ast = new ScalarTypeDefinitionNode([ $ast = new ScalarTypeDefinitionNode([
'name' => new NameNode(['value' => 'foo']) 'name' => new NameNode(['value' => 'foo']),
]); ]);
$this->assertEquals('scalar foo', Printer::doPrint($ast)); $this->assertEquals('scalar foo', Printer::doPrint($ast));
} }

View File

@ -1,5 +1,8 @@
<?php <?php
namespace GraphQL\Tests;
declare(strict_types=1);
namespace GraphQL\Tests\Language;
use GraphQL\Language\AST\Location; use GraphQL\Language\AST\Location;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
@ -7,6 +10,13 @@ use GraphQL\Language\AST\NodeList;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Utils\AST; use GraphQL\Utils\AST;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function array_keys;
use function count;
use function file_get_contents;
use function get_class;
use function get_object_vars;
use function implode;
use function json_decode;
class SerializationTest extends TestCase class SerializationTest extends TestCase
{ {
@ -27,33 +37,14 @@ class SerializationTest extends TestCase
$this->assertNodesAreEqual($parsedAst, $actualAst); $this->assertNodesAreEqual($parsedAst, $actualAst);
} }
public function testSerializeSupportsNoLocationOption() : void
{
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
$ast = Parser::parse($kitchenSink, ['noLocation' => true]);
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
$this->assertEquals($expectedAst, $ast->toArray(true));
}
public function testUnserializeSupportsNoLocationOption() : void
{
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
$actualAst = AST::fromArray($serializedAst);
$parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]);
$this->assertNodesAreEqual($parsedAst, $actualAst);
}
/** /**
* Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc * Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc
* *
* @param $expected * @param string[] $path
* @param $actual
* @param array $path
*/ */
private function assertNodesAreEqual($expected, $actual, $path = []) private function assertNodesAreEqual(Node $expected, Node $actual, array $path = []) : void
{ {
$err = "Mismatch at AST path: " . implode(', ', $path); $err = 'Mismatch at AST path: ' . implode(', ', $path);
$this->assertInstanceOf(Node::class, $actual, $err); $this->assertInstanceOf(Node::class, $actual, $err);
$this->assertEquals(get_class($expected), get_class($actual), $err); $this->assertEquals(get_class($expected), get_class($actual), $err);
@ -67,7 +58,7 @@ class SerializationTest extends TestCase
$actualValue = $actualVars[$name]; $actualValue = $actualVars[$name];
$tmpPath = $path; $tmpPath = $path;
$tmpPath[] = $name; $tmpPath[] = $name;
$err = "Mismatch at AST path: " . implode(', ', $tmpPath); $err = 'Mismatch at AST path: ' . implode(', ', $tmpPath);
if ($expectedValue instanceof Node) { if ($expectedValue instanceof Node) {
$this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath); $this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath);
@ -89,4 +80,21 @@ class SerializationTest extends TestCase
} }
} }
} }
public function testSerializeSupportsNoLocationOption() : void
{
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
$ast = Parser::parse($kitchenSink, ['noLocation' => true]);
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
$this->assertEquals($expectedAst, $ast->toArray(true));
}
public function testUnserializeSupportsNoLocationOption() : void
{
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
$actualAst = AST::fromArray($serializedAst);
$parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]);
$this->assertNodesAreEqual($parsedAst, $actualAst);
}
} }

View File

@ -1,27 +1,32 @@
<?php <?php
namespace GraphQL\Tests\Language;
declare(strict_types=1);
namespace GraphQL\Tests\Language;
use GraphQL\Language\AST\Location; use GraphQL\Language\AST\Location;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeList; use GraphQL\Language\AST\NodeList;
use function get_object_vars;
use function is_array;
use function is_scalar;
class TestUtils class TestUtils
{ {
/** /**
* @param Node $node * @return mixed[]
* @return array
*/ */
public static function nodeToArray(Node $node) public static function nodeToArray(Node $node) : array
{ {
$result = [ $result = [
'kind' => $node->kind, 'kind' => $node->kind,
'loc' => self::locationToArray($node->loc) 'loc' => self::locationToArray($node->loc),
]; ];
foreach (get_object_vars($node) as $prop => $propValue) { foreach (get_object_vars($node) as $prop => $propValue) {
if (isset($result[$prop])) if (isset($result[$prop])) {
continue; continue;
}
if (is_array($propValue) || $propValue instanceof NodeList) { if (is_array($propValue) || $propValue instanceof NodeList) {
$tmp = []; $tmp = [];
@ -30,7 +35,7 @@ class TestUtils
} }
} elseif ($propValue instanceof Node) { } elseif ($propValue instanceof Node) {
$tmp = self::nodeToArray($propValue); $tmp = self::nodeToArray($propValue);
} else if (is_scalar($propValue) || null === $propValue) { } elseif (is_scalar($propValue) || $propValue === null) {
$tmp = $propValue; $tmp = $propValue;
} else { } else {
$tmp = null; $tmp = null;
@ -38,27 +43,25 @@ class TestUtils
$result[$prop] = $tmp; $result[$prop] = $tmp;
} }
return $result; return $result;
} }
/** /**
* @param Location $loc * @return int[]
* @return array
*/ */
public static function locationToArray(Location $loc) public static function locationToArray(Location $loc) : array
{ {
return [ return [
'start' => $loc->start, 'start' => $loc->start,
'end' => $loc->end 'end' => $loc->end,
]; ];
} }
/** /**
* @param $start * @return int[]
* @param $end
* @return array
*/ */
public static function locArray($start, $end) public static function locArray(int $start, int $end) : array
{ {
return ['start' => $start, 'end' => $end]; return ['start' => $start, 'end' => $end];
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests; namespace GraphQL\Tests;
use GraphQL\Language\Token; use GraphQL\Language\Token;
@ -13,7 +16,7 @@ class TokenTest extends TestCase
'kind' => 'Kind', 'kind' => 'Kind',
'value' => null, 'value' => null,
'line' => 3, 'line' => 3,
'column' => 5 'column' => 5,
]; ];
$this->assertEquals($expected, $token->toArray()); $this->assertEquals($expected, $token->toArray());

View File

@ -29,15 +29,40 @@ use function iterator_to_array;
class VisitorTest extends ValidatorTestCase class VisitorTest extends ValidatorTestCase
{ {
private function getNodeByPath(DocumentNode $ast, $path) public function testValidatesPathArgument() : void
{ {
$result = $ast; $visited = [];
foreach ($path as $key) {
$resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray(); $ast = Parser::parse('{ a }', ['noLocation' => true]);
$this->assertArrayHasKey($key, $resultArray);
$result = $resultArray[$key]; Visitor::visit(
} $ast,
return $result; [
'enter' => function ($node, $key, $parent, $path) use ($ast, &$visited) {
$this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $path];
},
'leave' => function ($node, $key, $parent, $path) use ($ast, &$visited) {
$this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $path];
},
]
);
$expected = [
['enter', []],
['enter', ['definitions', 0]],
['enter', ['definitions', 0, 'selectionSet']],
['enter', ['definitions', 0, 'selectionSet', 'selections', 0]],
['enter', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']],
['leave', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']],
['leave', ['definitions', 0, 'selectionSet', 'selections', 0]],
['leave', ['definitions', 0, 'selectionSet']],
['leave', ['definitions', 0]],
['leave', []],
];
$this->assertEquals($expected, $visited);
} }
private function checkVisitorFnArgs($ast, $args, $isEdited = false) private function checkVisitorFnArgs($ast, $args, $isEdited = false)
@ -58,6 +83,7 @@ class VisitorTest extends ValidatorTestCase
$this->assertEquals(null, $parent); $this->assertEquals(null, $parent);
$this->assertEquals([], $path); $this->assertEquals([], $path);
$this->assertEquals([], $ancestors); $this->assertEquals([], $ancestors);
return; return;
} }
@ -84,37 +110,16 @@ class VisitorTest extends ValidatorTestCase
} }
} }
public function testValidatesPathArgument() : void private function getNodeByPath(DocumentNode $ast, $path)
{ {
$visited = []; $result = $ast;
foreach ($path as $key) {
$resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray();
$this->assertArrayHasKey($key, $resultArray);
$result = $resultArray[$key];
}
$ast = Parser::parse('{ a }', ['noLocation' => true]); return $result;
Visitor::visit($ast, [
'enter' => function ($node, $key, $parent, $path) use ($ast, &$visited) {
$this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $path];
},
'leave' => function ($node, $key, $parent, $path) use ($ast, &$visited) {
$this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $path];
},
]);
$expected = [
['enter', []],
['enter', ['definitions', 0]],
['enter', ['definitions', 0, 'selectionSet']],
['enter', ['definitions', 0, 'selectionSet', 'selections', 0]],
['enter', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']],
['leave', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']],
['leave', ['definitions', 0, 'selectionSet', 'selections', 0]],
['leave', ['definitions', 0, 'selectionSet']],
['leave', ['definitions', 0]],
['leave', []],
];
$this->assertEquals($expected, $visited);
} }
public function testAllowsEditingNodeOnEnterAndOnLeave() : void public function testAllowsEditingNodeOnEnterAndOnLeave() : void
@ -122,7 +127,9 @@ class VisitorTest extends ValidatorTestCase
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$selectionSet = null; $selectionSet = null;
$editedAst = Visitor::visit($ast, [ $editedAst = Visitor::visit(
$ast,
[
NodeKind::OPERATION_DEFINITION => [ NodeKind::OPERATION_DEFINITION => [
'enter' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { 'enter' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -133,6 +140,7 @@ class VisitorTest extends ValidatorTestCase
'selections' => [], 'selections' => [],
]); ]);
$newNode->didEnter = true; $newNode->didEnter = true;
return $newNode; return $newNode;
}, },
'leave' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { 'leave' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) {
@ -140,10 +148,12 @@ class VisitorTest extends ValidatorTestCase
$newNode = clone $node; $newNode = clone $node;
$newNode->selectionSet = $selectionSet; $newNode->selectionSet = $selectionSet;
$newNode->didLeave = true; $newNode->didLeave = true;
return $newNode; return $newNode;
}, },
], ],
]); ]
);
$this->assertNotEquals($ast, $editedAst); $this->assertNotEquals($ast, $editedAst);
@ -159,13 +169,16 @@ class VisitorTest extends ValidatorTestCase
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$definitions = $ast->definitions; $definitions = $ast->definitions;
$editedAst = Visitor::visit($ast, [ $editedAst = Visitor::visit(
$ast,
[
NodeKind::DOCUMENT => [ NodeKind::DOCUMENT => [
'enter' => function (DocumentNode $node) use ($ast) { 'enter' => function (DocumentNode $node) use ($ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$tmp = clone $node; $tmp = clone $node;
$tmp->definitions = []; $tmp->definitions = [];
$tmp->didEnter = true; $tmp->didEnter = true;
return $tmp; return $tmp;
}, },
'leave' => function (DocumentNode $node) use ($definitions, $ast) { 'leave' => function (DocumentNode $node) use ($definitions, $ast) {
@ -174,7 +187,8 @@ class VisitorTest extends ValidatorTestCase
$node->didLeave = true; $node->didLeave = true;
}, },
], ],
]); ]
);
$this->assertNotEquals($ast, $editedAst); $this->assertNotEquals($ast, $editedAst);
@ -188,14 +202,17 @@ class VisitorTest extends ValidatorTestCase
public function testAllowsForEditingOnEnter() : void public function testAllowsForEditingOnEnter() : void
{ {
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$editedAst = Visitor::visit($ast, [ $editedAst = Visitor::visit(
$ast,
[
'enter' => function ($node) use ($ast) { 'enter' => function ($node) use ($ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
if ($node instanceof FieldNode && $node->name->value === 'b') { if ($node instanceof FieldNode && $node->name->value === 'b') {
return Visitor::removeNode(); return Visitor::removeNode();
} }
}, },
]); ]
);
$this->assertEquals( $this->assertEquals(
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
@ -210,14 +227,17 @@ class VisitorTest extends ValidatorTestCase
public function testAllowsForEditingOnLeave() : void public function testAllowsForEditingOnLeave() : void
{ {
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$editedAst = Visitor::visit($ast, [ $editedAst = Visitor::visit(
$ast,
[
'leave' => function ($node) use ($ast) { 'leave' => function ($node) use ($ast) {
$this->checkVisitorFnArgs($ast, func_get_args(), true); $this->checkVisitorFnArgs($ast, func_get_args(), true);
if ($node instanceof FieldNode && $node->name->value === 'b') { if ($node instanceof FieldNode && $node->name->value === 'b') {
return Visitor::removeNode(); return Visitor::removeNode();
} }
}, },
]); ]
);
$this->assertEquals( $this->assertEquals(
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
@ -240,7 +260,9 @@ class VisitorTest extends ValidatorTestCase
$ast = Parser::parse('{ a { x } }', ['noLocation' => true]); $ast = Parser::parse('{ a { x } }', ['noLocation' => true]);
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function ($node) use ($addedField, &$didVisitAddedField, $ast) { 'enter' => function ($node) use ($addedField, &$didVisitAddedField, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args(), true); $this->checkVisitorFnArgs($ast, func_get_args(), true);
if ($node instanceof FieldNode && $node->name->value === 'a') { if ($node instanceof FieldNode && $node->name->value === 'a') {
@ -256,7 +278,8 @@ class VisitorTest extends ValidatorTestCase
$didVisitAddedField = true; $didVisitAddedField = true;
}, },
]); ]
);
$this->assertTrue($didVisitAddedField); $this->assertTrue($didVisitAddedField);
} }
@ -266,7 +289,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]);
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function (Node $node) use (&$visited, $ast) { 'enter' => function (Node $node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value ?? null]; $visited[] = ['enter', $node->kind, $node->value ?? null];
@ -278,7 +303,8 @@ class VisitorTest extends ValidatorTestCase
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
]); ]
);
$expected = [ $expected = [
['enter', 'Document', null], ['enter', 'Document', null],
@ -306,7 +332,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]);
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function (Node $node) use (&$visited, $ast) { 'enter' => function (Node $node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value ?? null]; $visited[] = ['enter', $node->kind, $node->value ?? null];
@ -318,7 +346,8 @@ class VisitorTest extends ValidatorTestCase
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
]); ]
);
$expected = [ $expected = [
['enter', 'Document', null], ['enter', 'Document', null],
@ -344,7 +373,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]);
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value ?? null]; $visited[] = ['enter', $node->kind, $node->value ?? null];
@ -357,9 +388,12 @@ class VisitorTest extends ValidatorTestCase
return Visitor::stop(); return Visitor::stop();
} }
}, },
]); ]
);
$this->assertEquals($visited, [ $this->assertEquals(
$visited,
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -374,7 +408,8 @@ class VisitorTest extends ValidatorTestCase
['enter', 'Field', null], ['enter', 'Field', null],
['enter', 'Name', 'x'], ['enter', 'Name', 'x'],
['leave', 'Name', 'x'], ['leave', 'Name', 'x'],
]); ]
);
} }
public function testAllowsANamedFunctionsVisitorAPI() : void public function testAllowsANamedFunctionsVisitorAPI() : void
@ -382,7 +417,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]);
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
NodeKind::NAME => function (NameNode $node) use (&$visited, $ast) { NodeKind::NAME => function (NameNode $node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value]; $visited[] = ['enter', $node->kind, $node->value];
@ -397,7 +434,8 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['leave', $node->kind, null]; $visited[] = ['leave', $node->kind, null];
}, },
], ],
]); ]
);
$expected = [ $expected = [
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -424,7 +462,9 @@ class VisitorTest extends ValidatorTestCase
); );
$visited = []; $visited = [];
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value ?? null]; $visited[] = ['enter', $node->kind, $node->value ?? null];
@ -433,7 +473,8 @@ class VisitorTest extends ValidatorTestCase
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
]); ]
);
$expected = [ $expected = [
['enter', 'Document', null], ['enter', 'Document', null],
@ -475,7 +516,9 @@ class VisitorTest extends ValidatorTestCase
$ast = Parser::parse($kitchenSink); $ast = Parser::parse($kitchenSink);
$visited = []; $visited = [];
Visitor::visit($ast, [ Visitor::visit(
$ast,
[
'enter' => function (Node $node, $key, $parent) use (&$visited, $ast) { 'enter' => function (Node $node, $key, $parent) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$r = ['enter', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; $r = ['enter', $node->kind, $key, $parent instanceof Node ? $parent->kind : null];
@ -486,7 +529,8 @@ class VisitorTest extends ValidatorTestCase
$r = ['leave', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; $r = ['leave', $node->kind, $key, $parent instanceof Node ? $parent->kind : null];
$visited[] = $r; $visited[] = $r;
}, },
]); ]
);
$expected = [ $expected = [
['enter', 'Document', null, null], ['enter', 'Document', null, null],
@ -813,7 +857,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }'); $ast = Parser::parse('{ a, b { x }, c }');
Visitor::visit($ast, Visitor::visitInParallel([ Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -829,9 +875,11 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -847,7 +895,9 @@ class VisitorTest extends ValidatorTestCase
['leave', 'SelectionSet', null], ['leave', 'SelectionSet', null],
['leave', 'OperationDefinition', null], ['leave', 'OperationDefinition', null],
['leave', 'Document', null], ['leave', 'Document', null],
], $visited); ],
$visited
);
} }
public function testAllowsSkippingDifferentSubTrees() : void public function testAllowsSkippingDifferentSubTrees() : void
@ -855,7 +905,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a { x }, b { y} }'); $ast = Parser::parse('{ a { x }, b { y} }');
Visitor::visit($ast, Visitor::visitInParallel([ Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -882,9 +934,11 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['no-b', 'leave', $node->kind, $node->value ?? null]; $visited[] = ['no-b', 'leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['no-a', 'enter', 'Document', null], ['no-a', 'enter', 'Document', null],
['no-b', 'enter', 'Document', null], ['no-b', 'enter', 'Document', null],
['no-a', 'enter', 'OperationDefinition', null], ['no-a', 'enter', 'OperationDefinition', null],
@ -919,7 +973,9 @@ class VisitorTest extends ValidatorTestCase
['no-b', 'leave', 'OperationDefinition', null], ['no-b', 'leave', 'OperationDefinition', null],
['no-a', 'leave', 'Document', null], ['no-a', 'leave', 'Document', null],
['no-b', 'leave', 'Document', null], ['no-b', 'leave', 'Document', null],
], $visited); ],
$visited
);
} }
public function testAllowsEarlyExitWhileVisiting2() : void public function testAllowsEarlyExitWhileVisiting2() : void
@ -927,7 +983,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }'); $ast = Parser::parse('{ a, b { x }, c }');
Visitor::visit($ast, Visitor::visitInParallel([ [ Visitor::visit(
$ast,
Visitor::visitInParallel([[
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$value = $node->value ?? null; $value = $node->value ?? null;
@ -941,9 +999,11 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -957,7 +1017,9 @@ class VisitorTest extends ValidatorTestCase
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
['enter', 'Field', null], ['enter', 'Field', null],
['enter', 'Name', 'x'], ['enter', 'Name', 'x'],
], $visited); ],
$visited
);
} }
public function testAllowsEarlyExitFromDifferentPoints() : void public function testAllowsEarlyExitFromDifferentPoints() : void
@ -965,7 +1027,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a { y }, b { x } }'); $ast = Parser::parse('{ a { y }, b { x } }');
Visitor::visit($ast, Visitor::visitInParallel([ Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -994,9 +1058,11 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['break-b', 'leave', $node->kind, $node->value ?? null]; $visited[] = ['break-b', 'leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['break-a', 'enter', 'Document', null], ['break-a', 'enter', 'Document', null],
['break-b', 'enter', 'Document', null], ['break-b', 'enter', 'Document', null],
['break-a', 'enter', 'OperationDefinition', null], ['break-a', 'enter', 'OperationDefinition', null],
@ -1017,7 +1083,9 @@ class VisitorTest extends ValidatorTestCase
['break-b', 'leave', 'Field', null], ['break-b', 'leave', 'Field', null],
['break-b', 'enter', 'Field', null], ['break-b', 'enter', 'Field', null],
['break-b', 'enter', 'Name', 'b'], ['break-b', 'enter', 'Name', 'b'],
], $visited); ],
$visited
);
} }
public function testAllowsEarlyExitWhileLeaving2() : void public function testAllowsEarlyExitWhileLeaving2() : void
@ -1025,7 +1093,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b { x }, c }'); $ast = Parser::parse('{ a, b { x }, c }');
Visitor::visit($ast, Visitor::visitInParallel([ [ Visitor::visit(
$ast,
Visitor::visitInParallel([[
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['enter', $node->kind, $node->value ?? null]; $visited[] = ['enter', $node->kind, $node->value ?? null];
@ -1039,9 +1109,11 @@ class VisitorTest extends ValidatorTestCase
} }
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -1056,7 +1128,9 @@ class VisitorTest extends ValidatorTestCase
['enter', 'Field', null], ['enter', 'Field', null],
['enter', 'Name', 'x'], ['enter', 'Name', 'x'],
['leave', 'Name', 'x'], ['leave', 'Name', 'x'],
], $visited); ],
$visited
);
} }
public function testAllowsEarlyExitFromLeavingDifferentPoints() : void public function testAllowsEarlyExitFromLeavingDifferentPoints() : void
@ -1064,7 +1138,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a { y }, b { x } }'); $ast = Parser::parse('{ a { y }, b { x } }');
Visitor::visit($ast, Visitor::visitInParallel([ Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -1091,9 +1167,11 @@ class VisitorTest extends ValidatorTestCase
} }
}, },
], ],
])); ])
);
$this->assertEquals([ $this->assertEquals(
[
['break-a', 'enter', 'Document', null], ['break-a', 'enter', 'Document', null],
['break-b', 'enter', 'Document', null], ['break-b', 'enter', 'Document', null],
['break-a', 'enter', 'OperationDefinition', null], ['break-a', 'enter', 'OperationDefinition', null],
@ -1130,7 +1208,9 @@ class VisitorTest extends ValidatorTestCase
['break-b', 'leave', 'Field', null], ['break-b', 'leave', 'Field', null],
['break-b', 'leave', 'SelectionSet', null], ['break-b', 'leave', 'SelectionSet', null],
['break-b', 'leave', 'Field', null], ['break-b', 'leave', 'Field', null],
], $visited); ],
$visited
);
} }
public function testAllowsForEditingOnEnter2() : void public function testAllowsForEditingOnEnter2() : void
@ -1138,7 +1218,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$editedAst = Visitor::visit($ast, Visitor::visitInParallel([ $editedAst = Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'enter' => function ($node) use (&$visited, $ast) { 'enter' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
@ -1157,7 +1239,8 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals( $this->assertEquals(
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
@ -1169,7 +1252,8 @@ class VisitorTest extends ValidatorTestCase
$editedAst $editedAst
); );
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -1194,7 +1278,9 @@ class VisitorTest extends ValidatorTestCase
['leave', 'SelectionSet', null], ['leave', 'SelectionSet', null],
['leave', 'OperationDefinition', null], ['leave', 'OperationDefinition', null],
['leave', 'Document', null], ['leave', 'Document', null],
], $visited); ],
$visited
);
} }
public function testAllowsForEditingOnLeave2() : void public function testAllowsForEditingOnLeave2() : void
@ -1202,7 +1288,9 @@ class VisitorTest extends ValidatorTestCase
$visited = []; $visited = [];
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
$editedAst = Visitor::visit($ast, Visitor::visitInParallel([ $editedAst = Visitor::visit(
$ast,
Visitor::visitInParallel([
[ [
'leave' => function ($node) use (&$visited, $ast) { 'leave' => function ($node) use (&$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args(), true); $this->checkVisitorFnArgs($ast, func_get_args(), true);
@ -1221,7 +1309,8 @@ class VisitorTest extends ValidatorTestCase
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
}, },
], ],
])); ])
);
$this->assertEquals( $this->assertEquals(
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
@ -1233,7 +1322,8 @@ class VisitorTest extends ValidatorTestCase
$editedAst $editedAst
); );
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null], ['enter', 'Document', null],
['enter', 'OperationDefinition', null], ['enter', 'OperationDefinition', null],
['enter', 'SelectionSet', null], ['enter', 'SelectionSet', null],
@ -1264,10 +1354,11 @@ class VisitorTest extends ValidatorTestCase
['leave', 'SelectionSet', null], ['leave', 'SelectionSet', null],
['leave', 'OperationDefinition', null], ['leave', 'OperationDefinition', null],
['leave', 'Document', null], ['leave', 'Document', null],
], $visited); ],
$visited
);
} }
/** /**
* Describe: visitWithTypeInfo * Describe: visitWithTypeInfo
*/ */
@ -1278,7 +1369,11 @@ class VisitorTest extends ValidatorTestCase
$typeInfo = new TypeInfo(ValidatorTestCase::getTestSchema()); $typeInfo = new TypeInfo(ValidatorTestCase::getTestSchema());
$ast = Parser::parse('{ human(id: 4) { name, pets { ... { name } }, unknown } }'); $ast = Parser::parse('{ human(id: 4) { name, pets { ... { name } }, unknown } }');
Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [ Visitor::visit(
$ast,
Visitor::visitWithTypeInfo(
$typeInfo,
[
'enter' => function ($node) use ($typeInfo, &$visited, $ast) { 'enter' => function ($node) use ($typeInfo, &$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$parentType = $typeInfo->getParentType(); $parentType = $typeInfo->getParentType();
@ -1307,9 +1402,12 @@ class VisitorTest extends ValidatorTestCase
$inputType ? (string) $inputType : null, $inputType ? (string) $inputType : null,
]; ];
}, },
])); ]
)
);
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null, null, null, null], ['enter', 'Document', null, null, null, null],
['enter', 'OperationDefinition', null, null, 'QueryRoot', null], ['enter', 'OperationDefinition', null, null, 'QueryRoot', null],
['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
@ -1350,7 +1448,9 @@ class VisitorTest extends ValidatorTestCase
['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
['leave', 'OperationDefinition', null, null, 'QueryRoot', null], ['leave', 'OperationDefinition', null, null, 'QueryRoot', null],
['leave', 'Document', null, null, null, null], ['leave', 'Document', null, null, null, null],
], $visited); ],
$visited
);
} }
public function testMaintainsTypeInfoDuringEdit() : void public function testMaintainsTypeInfoDuringEdit() : void
@ -1361,7 +1461,11 @@ class VisitorTest extends ValidatorTestCase
$ast = Parser::parse( $ast = Parser::parse(
'{ human(id: 4) { name, pets }, alien }' '{ human(id: 4) { name, pets }, alien }'
); );
$editedAst = Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [ $editedAst = Visitor::visit(
$ast,
Visitor::visitWithTypeInfo(
$typeInfo,
[
'enter' => function ($node) use ($typeInfo, &$visited, $ast) { 'enter' => function ($node) use ($typeInfo, &$visited, $ast) {
$this->checkVisitorFnArgs($ast, func_get_args(), true); $this->checkVisitorFnArgs($ast, func_get_args(), true);
$parentType = $typeInfo->getParentType(); $parentType = $typeInfo->getParentType();
@ -1410,17 +1514,26 @@ class VisitorTest extends ValidatorTestCase
$inputType ? (string) $inputType : null, $inputType ? (string) $inputType : null,
]; ];
}, },
])); ]
)
);
$this->assertEquals(Printer::doPrint(Parser::parse( $this->assertEquals(
Printer::doPrint(Parser::parse(
'{ human(id: 4) { name, pets }, alien }' '{ human(id: 4) { name, pets }, alien }'
)), Printer::doPrint($ast)); )),
Printer::doPrint($ast)
);
$this->assertEquals(Printer::doPrint(Parser::parse( $this->assertEquals(
Printer::doPrint(Parser::parse(
'{ human(id: 4) { name, pets { __typename } }, alien { __typename } }' '{ human(id: 4) { name, pets { __typename } }, alien { __typename } }'
)), Printer::doPrint($editedAst)); )),
Printer::doPrint($editedAst)
);
$this->assertEquals([ $this->assertEquals(
[
['enter', 'Document', null, null, null, null], ['enter', 'Document', null, null, null, null],
['enter', 'OperationDefinition', null, null, 'QueryRoot', null], ['enter', 'OperationDefinition', null, null, 'QueryRoot', null],
['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
@ -1463,6 +1576,8 @@ class VisitorTest extends ValidatorTestCase
['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
['leave', 'OperationDefinition', null, null, 'QueryRoot', null], ['leave', 'OperationDefinition', null, null, 'QueryRoot', null],
['leave', 'Document', null, null, null, null], ['leave', 'Document', null, null, null, null],
], $visited); ],
$visited
);
} }
} }