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,15 +657,18 @@ 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"');
try { try {
$lexer->advance(); $lexer->advance();
$this->fail('Expected exception not thrown'); $this->fail('Expected exception not thrown');
} catch(SyntaxError $error) { } catch (SyntaxError $error) {
$this->assertEquals([$this->loc(1,3)], $error->getLocations()); $this->assertEquals([$this->loc(1, 3)], $error->getLocations());
throw $error; throw $error;
} }
} }
@ -580,49 +696,28 @@ class LexerTest extends TestCase
$tokens = []; $tokens = [];
for ($tok = $startToken; $tok; $tok = $tok->next) { for ($tok = $startToken; $tok; $tok = $tok->next) {
if (!empty($tokens)) { if (! empty($tokens)) {
// Tokens are double-linked, prev should point to last seen token. // Tokens are double-linked, prev should point to last seen token.
$this->assertSame($tokens[count($tokens) - 1], $tok->prev); $this->assertSame($tokens[count($tokens) - 1], $tok->prev);
} }
$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');
@ -110,10 +133,27 @@ fragment MissingOn Type
$this->expectSyntaxError( $this->expectSyntaxError(
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }', 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
'Unexpected $', 'Unexpected $',
$this->loc(1,37) $this->loc(1, 37)
); );
} }
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"')
*/ */
@ -122,7 +162,7 @@ fragment MissingOn Type
$this->expectSyntaxError( $this->expectSyntaxError(
'fragment on on on { on }', 'fragment on on on { on }',
'Unexpected Name "on"', 'Unexpected Name "on"',
$this->loc(1,10) $this->loc(1, 10)
); );
} }
@ -134,7 +174,7 @@ fragment MissingOn Type
$this->expectSyntaxError( $this->expectSyntaxError(
'{ ...on }', '{ ...on }',
'Expected Name, found }', 'Expected Name, found }',
$this->loc(1,9) $this->loc(1, 9)
); );
} }
@ -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
... $fragmentName query $keyword {
... on $keyword { field } ... $fragmentName
... on $keyword { field }
}
fragment $fragmentName on Type {
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
} }
fragment $fragmentName on Type { fragment $fragmentName on Type {
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword) $keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
} }
"); GRAPHQL
);
$this->assertNotEmpty($result); $this->assertNotEmpty($result);
} }
} }
@ -286,10 +331,10 @@ fragment $fragmentName on Type {
'); ');
$result = Parser::parse($source); $result = Parser::parse($source);
$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);
} }
/** /**
@ -389,10 +442,10 @@ fragment $fragmentName on Type {
'); ');
$result = Parser::parse($source); $result = Parser::parse($source);
$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));
} }
@ -145,9 +189,10 @@ extend type Hello {
} }
'; ';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$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));
} }
@ -178,9 +223,10 @@ extend type Hello {
{ {
$body = 'extend type Hello implements Greeting'; $body = 'extend type Hello implements Greeting';
$doc = Parser::parse($body); $doc = Parser::parse($body);
$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,17 +354,18 @@ extend type Hello {
type Hello { type Hello {
world: String! world: String!
}'; }';
$loc = function($start, $end) { $doc = Parser::parse($body);
$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,
'definitions' => [ 'definitions' => [
[ [
'kind' => NodeKind::OBJECT_TYPE_DEFINITION, 'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
'name' => $this->nameNode('Hello', $loc(6,11)), 'name' => $this->nameNode('Hello', $loc(6, 11)),
'interfaces' => [], 'interfaces' => [],
'directives' => [], 'directives' => [],
'fields' => [ 'fields' => [
@ -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,
@ -640,20 +747,26 @@ type Hello {
$this->typeNode('String', $loc(41, 47)), $this->typeNode('String', $loc(41, 47)),
[ [
$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,6 +37,50 @@ class SerializationTest extends TestCase
$this->assertNodesAreEqual($parsedAst, $actualAst); $this->assertNodesAreEqual($parsedAst, $actualAst);
} }
/**
* Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc
*
* @param string[] $path
*/
private function assertNodesAreEqual(Node $expected, Node $actual, array $path = []) : void
{
$err = 'Mismatch at AST path: ' . implode(', ', $path);
$this->assertInstanceOf(Node::class, $actual, $err);
$this->assertEquals(get_class($expected), get_class($actual), $err);
$expectedVars = get_object_vars($expected);
$actualVars = get_object_vars($actual);
$this->assertSame(count($expectedVars), count($actualVars), $err);
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
foreach ($expectedVars as $name => $expectedValue) {
$actualValue = $actualVars[$name];
$tmpPath = $path;
$tmpPath[] = $name;
$err = 'Mismatch at AST path: ' . implode(', ', $tmpPath);
if ($expectedValue instanceof Node) {
$this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath);
} elseif ($expectedValue instanceof NodeList) {
$this->assertEquals(count($expectedValue), count($actualValue), $err);
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
foreach ($expectedValue as $index => $listNode) {
$tmpPath2 = $tmpPath;
$tmpPath2[] = $index;
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
}
} elseif ($expectedValue instanceof Location) {
$this->assertInstanceOf(Location::class, $actualValue, $err);
$this->assertSame($expectedValue->start, $actualValue->start, $err);
$this->assertSame($expectedValue->end, $actualValue->end, $err);
} else {
$this->assertEquals($expectedValue, $actualValue, $err);
}
}
}
public function testSerializeSupportsNoLocationOption() : void public function testSerializeSupportsNoLocationOption() : void
{ {
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql'); $kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
@ -43,50 +97,4 @@ class SerializationTest extends TestCase
$parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]); $parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]);
$this->assertNodesAreEqual($parsedAst, $actualAst); $this->assertNodesAreEqual($parsedAst, $actualAst);
} }
/**
* Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc
*
* @param $expected
* @param $actual
* @param array $path
*/
private function assertNodesAreEqual($expected, $actual, $path = [])
{
$err = "Mismatch at AST path: " . implode(', ', $path);
$this->assertInstanceOf(Node::class, $actual, $err);
$this->assertEquals(get_class($expected), get_class($actual), $err);
$expectedVars = get_object_vars($expected);
$actualVars = get_object_vars($actual);
$this->assertSame(count($expectedVars), count($actualVars), $err);
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
foreach ($expectedVars as $name => $expectedValue) {
$actualValue = $actualVars[$name];
$tmpPath = $path;
$tmpPath[] = $name;
$err = "Mismatch at AST path: " . implode(', ', $tmpPath);
if ($expectedValue instanceof Node) {
$this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath);
} else if ($expectedValue instanceof NodeList) {
$this->assertEquals(count($expectedValue), count($actualValue), $err);
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
foreach ($expectedValue as $index => $listNode) {
$tmpPath2 = $tmpPath;
$tmpPath2 [] = $index;
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
}
} else if ($expectedValue instanceof Location) {
$this->assertInstanceOf(Location::class, $actualValue, $err);
$this->assertSame($expectedValue->start, $actualValue->start, $err);
$this->assertSame($expectedValue->end, $actualValue->end, $err);
} else {
$this->assertEquals($expectedValue, $actualValue, $err);
}
}
}
} }

View File

@ -1,36 +1,41 @@
<?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 = [];
foreach ($propValue as $tmp1) { foreach ($propValue as $tmp1) {
$tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1; $tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1;
} }
} else if ($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());

File diff suppressed because it is too large Load Diff