mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-29 08:26:02 +03:00
Merge pull request #339 from simPod/cs-language-test
Fix CS in test/Language
This commit is contained in:
commit
a7af4663b8
@ -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,17 +28,45 @@ 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')
|
||||||
*/
|
*/
|
||||||
public function testAcceptsBomHeader() : void
|
public function testAcceptsBomHeader() : void
|
||||||
{
|
{
|
||||||
$bom = Utils::chr(0xFEFF);
|
$bom = Utils::chr(0xFEFF);
|
||||||
$expected = [
|
$expected = [
|
||||||
'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'));
|
||||||
@ -45,12 +78,12 @@ class LexerTest extends TestCase
|
|||||||
public function testRecordsLineAndColumn() : void
|
public function testRecordsLineAndColumn() : void
|
||||||
{
|
{
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 8,
|
'start' => 8,
|
||||||
'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"));
|
||||||
}
|
}
|
||||||
@ -67,10 +100,10 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'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));
|
||||||
|
|
||||||
@ -80,18 +113,18 @@ class LexerTest extends TestCase
|
|||||||
';
|
';
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'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));
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 3,
|
'start' => 3,
|
||||||
'end' => 6,
|
'end' => 6,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
|
|
||||||
$example3 = ',,,foo,,,';
|
$example3 = ',,,foo,,,';
|
||||||
@ -131,7 +164,7 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testUpdatesLineNumbersInErrorForFileContext() : void
|
public function testUpdatesLineNumbersInErrorForFileContext() : void
|
||||||
{
|
{
|
||||||
$str = '' .
|
$str = '' .
|
||||||
"\n" .
|
"\n" .
|
||||||
"\n" .
|
"\n" .
|
||||||
" ?\n" .
|
" ?\n" .
|
||||||
@ -181,63 +214,86 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testLexesStrings() : void
|
public function testLexesStrings() : void
|
||||||
{
|
{
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 8,
|
'start' => 0,
|
||||||
'value' => 'simple'
|
'end' => 8,
|
||||||
], (array) $this->lexOne('"simple"'));
|
'value' => 'simple',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"simple"')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertArraySubset(
|
||||||
|
[
|
||||||
|
'kind' => Token::STRING,
|
||||||
|
'start' => 0,
|
||||||
|
'end' => 15,
|
||||||
|
'value' => ' white space ',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('" white space "')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 15,
|
'start' => 0,
|
||||||
'value' => ' white space '
|
'end' => 10,
|
||||||
], (array) $this->lexOne('" white space "'));
|
'value' => 'quote "',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"quote \\""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 10,
|
'start' => 0,
|
||||||
'value' => 'quote "'
|
'end' => 25,
|
||||||
], (array) $this->lexOne('"quote \\""'));
|
'value' => 'escaped \n\r\b\t\f',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 25,
|
'start' => 0,
|
||||||
'value' => 'escaped \n\r\b\t\f'
|
'end' => 16,
|
||||||
], (array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"'));
|
'value' => 'slashes \\ \/',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"slashes \\\\ \\\\/"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 16,
|
'start' => 0,
|
||||||
'value' => 'slashes \\ \/'
|
'end' => 13,
|
||||||
], (array) $this->lexOne('"slashes \\\\ \\\\/"'));
|
'value' => 'unicode яуц',
|
||||||
|
],
|
||||||
$this->assertArraySubset([
|
(array) $this->lexOne('"unicode яуц"')
|
||||||
'kind' => Token::STRING,
|
);
|
||||||
'start' => 0,
|
|
||||||
'end' => 13,
|
|
||||||
'value' => '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,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 34,
|
'start' => 0,
|
||||||
'value' => 'unicode ' . $unicode
|
'end' => 34,
|
||||||
], (array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"'));
|
'value' => 'unicode ' . $unicode,
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 26,
|
'start' => 0,
|
||||||
'value' => $unicode
|
'end' => 26,
|
||||||
], (array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"'));
|
'value' => $unicode,
|
||||||
|
],
|
||||||
|
(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,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 12,
|
'start' => 0,
|
||||||
'value' => 'simple'
|
'end' => 12,
|
||||||
], (array) $this->lexOne('"""simple"""'));
|
'value' => 'simple',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""simple"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 19,
|
'start' => 0,
|
||||||
'value' => ' white space '
|
'end' => 19,
|
||||||
], (array) $this->lexOne('""" white space """'));
|
'value' => ' white space ',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('""" white space """')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 22,
|
'start' => 0,
|
||||||
'value' => 'contains " quote'
|
'end' => 22,
|
||||||
], (array) $this->lexOne('"""contains " quote"""'));
|
'value' => 'contains " quote',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""contains " quote"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 31,
|
'start' => 0,
|
||||||
'value' => 'contains """ triplequote'
|
'end' => 31,
|
||||||
], (array) $this->lexOne('"""contains \\""" triplequote"""'));
|
'value' => 'contains """ triplequote',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""contains \\""" triplequote"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 16,
|
'start' => 0,
|
||||||
'value' => "multi\nline"
|
'end' => 16,
|
||||||
], (array) $this->lexOne("\"\"\"multi\nline\"\"\""));
|
'value' => "multi\nline",
|
||||||
|
],
|
||||||
|
(array) $this->lexOne("\"\"\"multi\nline\"\"\"")
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 28,
|
'start' => 0,
|
||||||
'value' => "multi\nline\nnormalized"
|
'end' => 28,
|
||||||
], (array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\""));
|
'value' => "multi\nline\nnormalized",
|
||||||
|
],
|
||||||
|
(array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"")
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 32,
|
'start' => 0,
|
||||||
'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234'
|
'end' => 32,
|
||||||
], (array) $this->lexOne('"""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"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 19,
|
'start' => 0,
|
||||||
'value' => 'slashes \\\\ \\/'
|
'end' => 19,
|
||||||
], (array) $this->lexOne('"""slashes \\\\ \\/"""'));
|
'value' => 'slashes \\\\ \\/',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""slashes \\\\ \\/"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 68,
|
'start' => 0,
|
||||||
'value' => "spans\n multiple\n lines"
|
'end' => 68,
|
||||||
], (array) $this->lexOne("\"\"\"
|
'value' => "spans\n multiple\n lines",
|
||||||
|
],
|
||||||
|
(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)],
|
||||||
@ -336,25 +434,40 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulStringErrors
|
* @dataProvider reportsUsefulStringErrors
|
||||||
* @see it('lex reports useful string errors')
|
* @see it('lex reports useful string errors')
|
||||||
*/
|
*/
|
||||||
public function testLexReportsUsefulStringErrors($str, $expectedMessage, $location) : void
|
public function testLexReportsUsefulStringErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
$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
|
||||||
|
),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulBlockStringErrors
|
* @dataProvider reportsUsefulBlockStringErrors
|
||||||
* @see it('lex reports useful block string errors')
|
* @see it('lex reports useful block string errors')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulBlockStringErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulBlockStringErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -435,21 +548,21 @@ 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)],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulNumberErrors
|
* @dataProvider reportsUsefulNumberErrors
|
||||||
* @see it('lex reports useful number errors')
|
* @see it('lex reports useful number errors')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulNumberErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulNumberErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -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)],
|
||||||
];
|
];
|
||||||
@ -530,7 +643,7 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulUnknownCharErrors
|
* @dataProvider reportsUsefulUnknownCharErrors
|
||||||
* @see it('lex reports useful unknown character error')
|
* @see it('lex reports useful unknown character error')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulUnknownCharErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulUnknownCharErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -542,17 +655,20 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testReportsUsefulDashesInfo() : void
|
public function testReportsUsefulDashesInfo() : void
|
||||||
{
|
{
|
||||||
$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',
|
'{',
|
||||||
'Name',
|
'Comment',
|
||||||
'}',
|
'Name',
|
||||||
'<EOF>'
|
'}',
|
||||||
], Utils::map($tokens, function ($tok) {
|
'<EOF>',
|
||||||
return $tok->kind;
|
],
|
||||||
}));
|
Utils::map(
|
||||||
}
|
$tokens,
|
||||||
|
function ($tok) {
|
||||||
/**
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,22 +48,40 @@ 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"],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +185,7 @@ fragment MissingOn Type
|
|||||||
{
|
{
|
||||||
// Note: \u0A0A could be naively interpretted as two line-feed chars.
|
// Note: \u0A0A could be naively interpretted as two line-feed chars.
|
||||||
|
|
||||||
$char = Utils::chr(0x0A0A);
|
$char = Utils::chr(0x0A0A);
|
||||||
$query = <<<HEREDOC
|
$query = <<<HEREDOC
|
||||||
# This comment has a $char multi-byte character.
|
# This comment has a $char multi-byte character.
|
||||||
{ field(arg: "Has a $char multi-byte character.") }
|
{ field(arg: "Has a $char multi-byte character.") }
|
||||||
@ -156,18 +196,18 @@ HEREDOC;
|
|||||||
$expected = new SelectionSetNode([
|
$expected = new SelectionSetNode([
|
||||||
'selections' => new NodeList([
|
'selections' => new NodeList([
|
||||||
new FieldNode([
|
new FieldNode([
|
||||||
'name' => new NameNode(['value' => 'field']),
|
'name' => new NameNode(['value' => 'field']),
|
||||||
'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);
|
||||||
@ -180,7 +220,7 @@ HEREDOC;
|
|||||||
{
|
{
|
||||||
// Following should not throw:
|
// Following should not throw:
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$result = Parser::parse($kitchenSink);
|
$result = Parser::parse($kitchenSink);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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,94 +331,102 @@ 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,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => NodeKind::DOCUMENT,
|
'kind' => NodeKind::DOCUMENT,
|
||||||
'loc' => $loc(0, 41),
|
'loc' => $loc(0, 41),
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::OPERATION_DEFINITION,
|
'kind' => NodeKind::OPERATION_DEFINITION,
|
||||||
'loc' => $loc(0, 40),
|
'loc' => $loc(0, 40),
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'variableDefinitions' => [],
|
'variableDefinitions' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(0, 40),
|
'loc' => $loc(0, 40),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(4, 38),
|
'loc' => $loc(4, 38),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(4, 8),
|
'loc' => $loc(4, 8),
|
||||||
'value' => 'node'
|
'value' => 'node',
|
||||||
],
|
],
|
||||||
'arguments' => [
|
'arguments' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::ARGUMENT,
|
'kind' => NodeKind::ARGUMENT,
|
||||||
'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' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(16, 38),
|
'loc' => $loc(16, 38),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(22, 24),
|
'loc' => $loc(22, 24),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'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,
|
||||||
'loc' => $loc(30, 34),
|
'loc' => $loc(30, 34),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'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,63 +442,63 @@ 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,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => NodeKind::DOCUMENT,
|
'kind' => NodeKind::DOCUMENT,
|
||||||
'loc' => $loc(0, 30),
|
'loc' => $loc(0, 30),
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::OPERATION_DEFINITION,
|
'kind' => NodeKind::OPERATION_DEFINITION,
|
||||||
'loc' => $loc(0, 29),
|
'loc' => $loc(0, 29),
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'variableDefinitions' => [],
|
'variableDefinitions' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(6, 29),
|
'loc' => $loc(6, 29),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(10, 27),
|
'loc' => $loc(10, 27),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(10, 14),
|
'loc' => $loc(10, 14),
|
||||||
'value' => 'node'
|
'value' => 'node',
|
||||||
],
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(15, 27),
|
'loc' => $loc(15, 27),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(21, 23),
|
'loc' => $loc(21, 23),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'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,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 4]
|
'kind' => NodeKind::NULL,
|
||||||
], $this->nodeToArray(Parser::parseValue('null')));
|
'loc' => ['start' => 0, 'end' => 4],
|
||||||
|
],
|
||||||
|
$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,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 11],
|
'kind' => NodeKind::LST,
|
||||||
'values' => [
|
'loc' => ['start' => 0, 'end' => 11],
|
||||||
[
|
'values' => [
|
||||||
'kind' => NodeKind::INT,
|
[
|
||||||
'loc' => ['start' => 1, 'end' => 4],
|
'kind' => NodeKind::INT,
|
||||||
'value' => '123'
|
'loc' => ['start' => 1, 'end' => 4],
|
||||||
|
'value' => '123',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'kind' => NodeKind::STRING,
|
||||||
|
'loc' => ['start' => 5, 'end' => 10],
|
||||||
|
'value' => 'abc',
|
||||||
|
'block' => false,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
[
|
],
|
||||||
'kind' => NodeKind::STRING,
|
$this->nodeToArray(Parser::parseValue('[123 "abc"]'))
|
||||||
'loc' => ['start' => 5, 'end' => 10],
|
);
|
||||||
'value' => 'abc',
|
|
||||||
'block' => false
|
|
||||||
]
|
|
||||||
]
|
|
||||||
], $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,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'String'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'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,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'value' => 'MyType',
|
||||||
], $this->nodeToArray(Parser::parseType('MyType')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('MyType'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -582,19 +647,22 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesListTypes() : void
|
public function testParsesListTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::LIST_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 8],
|
'kind' => NodeKind::LIST_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 8],
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'type' => [
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
]
|
'value' => 'MyType',
|
||||||
]
|
],
|
||||||
], $this->nodeToArray(Parser::parseType('[MyType]')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('[MyType]'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -602,19 +670,22 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesNonNullTypes() : void
|
public function testParsesNonNullTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::NON_NULL_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 7],
|
'kind' => NodeKind::NON_NULL_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 7],
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'type' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'value' => 'MyType',
|
||||||
]
|
],
|
||||||
], $this->nodeToArray(Parser::parseType('MyType!')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('MyType!'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -622,48 +693,25 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesNestedTypes() : void
|
public function testParsesNestedTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::LIST_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 9],
|
'kind' => NodeKind::LIST_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 9],
|
||||||
'kind' => NodeKind::NON_NULL_TYPE,
|
|
||||||
'loc' => ['start' => 1, 'end' => 8],
|
|
||||||
'type' => [
|
'type' => [
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'kind' => NodeKind::NON_NULL_TYPE,
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'loc' => ['start' => 1, 'end' => 8],
|
||||||
'name' => [
|
'type' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
'value' => 'MyType'
|
'name' => [
|
||||||
]
|
'kind' => NodeKind::NAME,
|
||||||
]
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
]
|
'value' => 'MyType',
|
||||||
], $this->nodeToArray(Parser::parseType('[MyType!]')));
|
],
|
||||||
}
|
],
|
||||||
|
],
|
||||||
/**
|
],
|
||||||
* @param Node $node
|
$this->nodeToArray(Parser::parseType('[MyType!]'))
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
@ -22,7 +19,7 @@ class PrinterTest extends TestCase
|
|||||||
public function testDoesntAlterAST() : void
|
public function testDoesntAlterAST() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
|
|
||||||
$astCopy = $ast->cloneDeep();
|
$astCopy = $ast->cloneDeep();
|
||||||
$this->assertEquals($astCopy, $ast);
|
$this->assertEquals($astCopy, $ast);
|
||||||
@ -66,7 +63,7 @@ class PrinterTest extends TestCase
|
|||||||
$this->assertEquals($expected, Printer::doPrint($queryAstShorthanded));
|
$this->assertEquals($expected, Printer::doPrint($queryAstShorthanded));
|
||||||
|
|
||||||
$mutationAst = Parser::parse('mutation { id, name }');
|
$mutationAst = Parser::parse('mutation { id, name }');
|
||||||
$expected = 'mutation {
|
$expected = 'mutation {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -76,7 +73,7 @@ class PrinterTest extends TestCase
|
|||||||
$queryAstWithArtifacts = Parser::parse(
|
$queryAstWithArtifacts = Parser::parse(
|
||||||
'query ($foo: TestType) @testDirective { id, name }'
|
'query ($foo: TestType) @testDirective { id, name }'
|
||||||
);
|
);
|
||||||
$expected = 'query ($foo: TestType) @testDirective {
|
$expected = 'query ($foo: TestType) @testDirective {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -86,7 +83,7 @@ class PrinterTest extends TestCase
|
|||||||
$mutationAstWithArtifacts = Parser::parse(
|
$mutationAstWithArtifacts = Parser::parse(
|
||||||
'mutation ($foo: TestType) @testDirective { id, name }'
|
'mutation ($foo: TestType) @testDirective { id, name }'
|
||||||
);
|
);
|
||||||
$expected = 'mutation ($foo: TestType) @testDirective {
|
$expected = 'mutation ($foo: TestType) @testDirective {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -100,13 +97,13 @@ class PrinterTest extends TestCase
|
|||||||
public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace() : void
|
public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace() : void
|
||||||
{
|
{
|
||||||
$mutationAstWithArtifacts = Parser::parse(
|
$mutationAstWithArtifacts = Parser::parse(
|
||||||
'{ field(arg: """ space-led value""") }'
|
'{ field(arg: """ space-led value""") }'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """ space-led value""")
|
field(arg: """ space-led value""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,8 +119,8 @@ class PrinterTest extends TestCase
|
|||||||
indentation
|
indentation
|
||||||
""")
|
""")
|
||||||
}'
|
}'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """
|
field(arg: """
|
||||||
first
|
first
|
||||||
line
|
line
|
||||||
@ -131,7 +128,7 @@ class PrinterTest extends TestCase
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,7 +142,7 @@ class PrinterTest extends TestCase
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = <<<END
|
$expected = <<<END
|
||||||
{
|
{
|
||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -187,13 +185,13 @@ END;
|
|||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
}'
|
}'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +200,7 @@ END;
|
|||||||
public function testPrintsKitchenSink() : void
|
public function testPrintsKitchenSink() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
|
|
||||||
$printed = Printer::doPrint($ast);
|
$printed = Printer::doPrint($ast);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||||
}
|
}
|
||||||
@ -41,7 +45,7 @@ class SchemaPrinterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
||||||
|
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$astCopy = $ast->cloneDeep();
|
$astCopy = $ast->cloneDeep();
|
||||||
Printer::doPrint($ast);
|
Printer::doPrint($ast);
|
||||||
|
|
||||||
@ -52,7 +56,7 @@ class SchemaPrinterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
||||||
|
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$printed = Printer::doPrint($ast);
|
$printed = Printer::doPrint($ast);
|
||||||
|
|
||||||
$expected = 'schema {
|
$expected = 'schema {
|
||||||
|
@ -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,80 +10,68 @@ 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
|
||||||
{
|
{
|
||||||
public function testSerializesAst() : void
|
public function testSerializesAst() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
||||||
$this->assertEquals($expectedAst, $ast->toArray(true));
|
$this->assertEquals($expectedAst, $ast->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnserializesAst() : void
|
public function testUnserializesAst() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
||||||
$actualAst = AST::fromArray($serializedAst);
|
$actualAst = AST::fromArray($serializedAst);
|
||||||
$parsedAst = Parser::parse($kitchenSink);
|
$parsedAst = Parser::parse($kitchenSink);
|
||||||
$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);
|
$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);
|
||||||
|
|
||||||
$expectedVars = get_object_vars($expected);
|
$expectedVars = get_object_vars($expected);
|
||||||
$actualVars = get_object_vars($actual);
|
$actualVars = get_object_vars($actual);
|
||||||
$this->assertSame(count($expectedVars), count($actualVars), $err);
|
$this->assertSame(count($expectedVars), count($actualVars), $err);
|
||||||
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
|
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
|
||||||
|
|
||||||
foreach ($expectedVars as $name => $expectedValue) {
|
foreach ($expectedVars as $name => $expectedValue) {
|
||||||
$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);
|
||||||
} else if ($expectedValue instanceof NodeList) {
|
} elseif ($expectedValue instanceof NodeList) {
|
||||||
$this->assertEquals(count($expectedValue), count($actualValue), $err);
|
$this->assertEquals(count($expectedValue), count($actualValue), $err);
|
||||||
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
|
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
|
||||||
|
|
||||||
foreach ($expectedValue as $index => $listNode) {
|
foreach ($expectedValue as $index => $listNode) {
|
||||||
$tmpPath2 = $tmpPath;
|
$tmpPath2 = $tmpPath;
|
||||||
$tmpPath2 [] = $index;
|
$tmpPath2[] = $index;
|
||||||
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
|
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
|
||||||
}
|
}
|
||||||
} else if ($expectedValue instanceof Location) {
|
} elseif ($expectedValue instanceof Location) {
|
||||||
$this->assertInstanceOf(Location::class, $actualValue, $err);
|
$this->assertInstanceOf(Location::class, $actualValue, $err);
|
||||||
$this->assertSame($expectedValue->start, $actualValue->start, $err);
|
$this->assertSame($expectedValue->start, $actualValue->start, $err);
|
||||||
$this->assertSame($expectedValue->end, $actualValue->end, $err);
|
$this->assertSame($expectedValue->end, $actualValue->end, $err);
|
||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests;
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
use GraphQL\Language\Token;
|
use GraphQL\Language\Token;
|
||||||
@ -8,12 +11,12 @@ class TokenTest extends TestCase
|
|||||||
{
|
{
|
||||||
public function testReturnTokenOnArray() : void
|
public function testReturnTokenOnArray() : void
|
||||||
{
|
{
|
||||||
$token = new Token('Kind', 1, 10, 3, 5);
|
$token = new Token('Kind', 1, 10, 3, 5);
|
||||||
$expected = [
|
$expected = [
|
||||||
'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
Loading…
Reference in New Issue
Block a user