mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-06 07:49:24 +03:00
Fixed visitor; more visitor tests
This commit is contained in:
parent
71924f1154
commit
e82f887918
@ -256,7 +256,9 @@ class Visitor
|
|||||||
array_pop($path);
|
array_pop($path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$editValue = null;
|
if ($result->removeNode) {
|
||||||
|
$editValue = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$editValue = $result;
|
$editValue = $result;
|
||||||
}
|
}
|
||||||
@ -312,7 +314,6 @@ class Visitor
|
|||||||
*/
|
*/
|
||||||
static function visitInParallel($visitors)
|
static function visitInParallel($visitors)
|
||||||
{
|
{
|
||||||
// TODO: implement real parallel visiting once PHP supports it
|
|
||||||
$visitorsCount = count($visitors);
|
$visitorsCount = count($visitors);
|
||||||
$skipping = new \SplFixedArray($visitorsCount);
|
$skipping = new \SplFixedArray($visitorsCount);
|
||||||
|
|
||||||
@ -330,6 +331,8 @@ class Visitor
|
|||||||
$skipping[$i] = $node;
|
$skipping[$i] = $node;
|
||||||
} else if ($result->doBreak) {
|
} else if ($result->doBreak) {
|
||||||
$skipping[$i] = $result;
|
$skipping[$i] = $result;
|
||||||
|
} else if ($result->removeNode) {
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
} else if ($result !== null) {
|
} else if ($result !== null) {
|
||||||
return $result;
|
return $result;
|
||||||
@ -348,6 +351,8 @@ class Visitor
|
|||||||
if ($result instanceof VisitorOperation) {
|
if ($result instanceof VisitorOperation) {
|
||||||
if ($result->doBreak) {
|
if ($result->doBreak) {
|
||||||
$skipping[$i] = $result;
|
$skipping[$i] = $result;
|
||||||
|
} else if ($result->removeNode) {
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
} else if ($result !== null) {
|
} else if ($result !== null) {
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -8,7 +8,11 @@ use GraphQL\Language\AST\Node;
|
|||||||
use GraphQL\Language\AST\OperationDefinition;
|
use GraphQL\Language\AST\OperationDefinition;
|
||||||
use GraphQL\Language\AST\SelectionSet;
|
use GraphQL\Language\AST\SelectionSet;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
|
use GraphQL\Language\Printer;
|
||||||
use GraphQL\Language\Visitor;
|
use GraphQL\Language\Visitor;
|
||||||
|
use GraphQL\Tests\Validator\TestCase;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Utils\TypeInfo;
|
||||||
|
|
||||||
class VisitorTest extends \PHPUnit_Framework_TestCase
|
class VisitorTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
@ -643,4 +647,661 @@ class VisitorTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertEquals($expected, $visited);
|
$this->assertEquals($expected, $visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: visitInParallel
|
||||||
|
// Note: nearly identical to the above test of the same test but using visitInParallel.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows skipping a sub-tree
|
||||||
|
*/
|
||||||
|
public function testAllowsSkippingSubTree()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a, b { x }, c }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = [ 'enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') {
|
||||||
|
return Visitor::skipNode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'enter', 'Document', null ],
|
||||||
|
[ 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Field', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'c' ],
|
||||||
|
[ 'leave', 'Name', 'c' ],
|
||||||
|
[ 'leave', 'Field', null ],
|
||||||
|
[ 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'leave', 'OperationDefinition', null ],
|
||||||
|
[ 'leave', 'Document', null ],
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows skipping different sub-trees
|
||||||
|
*/
|
||||||
|
public function testAllowsSkippingDifferentSubTrees()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a { x }, b { y} }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['no-a', 'enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') {
|
||||||
|
return Visitor::skipNode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = [ 'no-a', 'leave', $node->kind, isset($node->value) ? $node->value : null ];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['no-b', 'enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') {
|
||||||
|
return Visitor::skipNode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['no-b', 'leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'no-a', 'enter', 'Document', null ],
|
||||||
|
[ 'no-b', 'enter', 'Document', null ],
|
||||||
|
[ 'no-a', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'no-b', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'no-a', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'no-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'no-a', 'enter', 'Field', null ],
|
||||||
|
[ 'no-b', 'enter', 'Field', null ],
|
||||||
|
[ 'no-b', 'enter', 'Name', 'a' ],
|
||||||
|
[ 'no-b', 'leave', 'Name', 'a' ],
|
||||||
|
[ 'no-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'no-b', 'enter', 'Field', null ],
|
||||||
|
[ 'no-b', 'enter', 'Name', 'x' ],
|
||||||
|
[ 'no-b', 'leave', 'Name', 'x' ],
|
||||||
|
[ 'no-b', 'leave', 'Field', null ],
|
||||||
|
[ 'no-b', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'no-b', 'leave', 'Field', null ],
|
||||||
|
[ 'no-a', 'enter', 'Field', null ],
|
||||||
|
[ 'no-b', 'enter', 'Field', null ],
|
||||||
|
[ 'no-a', 'enter', 'Name', 'b' ],
|
||||||
|
[ 'no-a', 'leave', 'Name', 'b' ],
|
||||||
|
[ 'no-a', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'no-a', 'enter', 'Field', null ],
|
||||||
|
[ 'no-a', 'enter', 'Name', 'y' ],
|
||||||
|
[ 'no-a', 'leave', 'Name', 'y' ],
|
||||||
|
[ 'no-a', 'leave', 'Field', null ],
|
||||||
|
[ 'no-a', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'no-a', 'leave', 'Field', null ],
|
||||||
|
[ 'no-a', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'no-b', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'no-a', 'leave', 'OperationDefinition', null ],
|
||||||
|
[ 'no-b', 'leave', 'OperationDefinition', null ],
|
||||||
|
[ 'no-a', 'leave', 'Document', null ],
|
||||||
|
[ 'no-b', 'leave', 'Document', null ],
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows early exit while visiting
|
||||||
|
*/
|
||||||
|
public function testAllowsEarlyExitWhileVisiting2()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a, b { x }, c }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([ [
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$value = isset($node->value) ? $node->value : null;
|
||||||
|
$visited[] = ['enter', $node->kind, $value];
|
||||||
|
if ($node->kind === 'Name' && $value === 'x') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
] ]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'enter', 'Document', null ],
|
||||||
|
[ 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Field', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'b' ],
|
||||||
|
[ 'leave', 'Name', 'b' ],
|
||||||
|
[ 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'x' ]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows early exit from different points
|
||||||
|
*/
|
||||||
|
public function testAllowsEarlyExitFromDifferentPoints()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a { y }, b { x } }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$value = isset($node->value) ? $node->value : null;
|
||||||
|
$visited[] = ['break-a', 'enter', $node->kind, $value];
|
||||||
|
if ($node->kind === 'Name' && $value === 'a') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = [ 'break-a', 'leave', $node->kind, isset($node->value) ? $node->value : null ];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$value = isset($node->value) ? $node->value : null;
|
||||||
|
$visited[] = ['break-b', 'enter', $node->kind, $value];
|
||||||
|
if ($node->kind === 'Name' && $value === 'b') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['break-b', 'leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'break-a', 'enter', 'Document', null ],
|
||||||
|
[ 'break-b', 'enter', 'Document', null ],
|
||||||
|
[ 'break-a', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'break-b', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'break-a', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-a', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-a', 'enter', 'Name', 'a' ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'a' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'a' ],
|
||||||
|
[ 'break-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'y' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'y' ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'b' ]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows early exit while leaving
|
||||||
|
*/
|
||||||
|
public function testAllowsEarlyExitWhileLeaving2()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a, b { x }, c }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([ [
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$value = isset($node->value) ? $node->value : null;
|
||||||
|
$visited[] = ['leave', $node->kind, $value];
|
||||||
|
if ($node->kind === 'Name' && $value === 'x') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] ]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'enter', 'Document', null ],
|
||||||
|
[ 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Name', 'a' ],
|
||||||
|
[ 'leave', 'Field', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'b' ],
|
||||||
|
[ 'leave', 'Name', 'b' ],
|
||||||
|
[ 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'enter', 'Field', null ],
|
||||||
|
[ 'enter', 'Name', 'x' ],
|
||||||
|
[ 'leave', 'Name', 'x' ]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows early exit from leaving different points
|
||||||
|
*/
|
||||||
|
public function testAllowsEarlyExitFromLeavingDifferentPoints()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a { y }, b { x } }');
|
||||||
|
Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['break-a', 'enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['break-a', 'leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enter' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['break-b', 'enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
},
|
||||||
|
'leave' => function($node) use (&$visited) {
|
||||||
|
$visited[] = ['break-b', 'leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') {
|
||||||
|
return Visitor::stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
[ 'break-a', 'enter', 'Document', null ],
|
||||||
|
[ 'break-b', 'enter', 'Document', null ],
|
||||||
|
[ 'break-a', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'break-b', 'enter', 'OperationDefinition', null ],
|
||||||
|
[ 'break-a', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-a', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-a', 'enter', 'Name', 'a' ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'a' ],
|
||||||
|
[ 'break-a', 'leave', 'Name', 'a' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'a' ],
|
||||||
|
[ 'break-a', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-a', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-a', 'enter', 'Name', 'y' ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'y' ],
|
||||||
|
[ 'break-a', 'leave', 'Name', 'y' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'y' ],
|
||||||
|
[ 'break-a', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ],
|
||||||
|
[ 'break-a', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'break-a', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'b' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'b' ],
|
||||||
|
[ 'break-b', 'enter', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'enter', 'Field', null ],
|
||||||
|
[ 'break-b', 'enter', 'Name', 'x' ],
|
||||||
|
[ 'break-b', 'leave', 'Name', 'x' ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ],
|
||||||
|
[ 'break-b', 'leave', 'SelectionSet', null ],
|
||||||
|
[ 'break-b', 'leave', 'Field', null ]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows for editing on enter
|
||||||
|
*/
|
||||||
|
public function testAllowsForEditingOnEnter2()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
|
||||||
|
$editedAst = Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'enter' => function ($node) use (&$visited) {
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') {
|
||||||
|
return Visitor::removeNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enter' => function ($node) use (&$visited) {
|
||||||
|
$visited[] = ['enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
},
|
||||||
|
'leave' => function ($node) use (&$visited) {
|
||||||
|
$visited[] = ['leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
|
||||||
|
$ast
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
Parser::parse('{ a, c { a, c } }', ['noLocation' => true]),
|
||||||
|
$editedAst
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
['enter', 'Document', null],
|
||||||
|
['enter', 'OperationDefinition', null],
|
||||||
|
['enter', 'SelectionSet', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'a'],
|
||||||
|
['leave', 'Name', 'a'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'c'],
|
||||||
|
['leave', 'Name', 'c'],
|
||||||
|
['enter', 'SelectionSet', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'a'],
|
||||||
|
['leave', 'Name', 'a'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'c'],
|
||||||
|
['leave', 'Name', 'c'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['leave', 'SelectionSet', null],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['leave', 'SelectionSet', null],
|
||||||
|
['leave', 'OperationDefinition', null],
|
||||||
|
['leave', 'Document', null]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows for editing on leave
|
||||||
|
*/
|
||||||
|
public function testAllowsForEditingOnLeave2()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
|
||||||
|
$editedAst = Visitor::visit($ast, Visitor::visitInParallel([
|
||||||
|
[
|
||||||
|
'leave' => function ($node) use (&$visited) {
|
||||||
|
if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') {
|
||||||
|
return Visitor::removeNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enter' => function ($node) use (&$visited) {
|
||||||
|
$visited[] = ['enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
},
|
||||||
|
'leave' => function ($node) use (&$visited) {
|
||||||
|
$visited[] = ['leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]),
|
||||||
|
$ast
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
Parser::parse('{ a, c { a, c } }', ['noLocation' => true]),
|
||||||
|
$editedAst
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
['enter', 'Document', null],
|
||||||
|
['enter', 'OperationDefinition', null],
|
||||||
|
['enter', 'SelectionSet', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'a'],
|
||||||
|
['leave', 'Name', 'a'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'b'],
|
||||||
|
['leave', 'Name', 'b'],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'c'],
|
||||||
|
['leave', 'Name', 'c'],
|
||||||
|
['enter', 'SelectionSet', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'a'],
|
||||||
|
['leave', 'Name', 'a'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'b'],
|
||||||
|
['leave', 'Name', 'b'],
|
||||||
|
['enter', 'Field', null],
|
||||||
|
['enter', 'Name', 'c'],
|
||||||
|
['leave', 'Name', 'c'],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['leave', 'SelectionSet', null],
|
||||||
|
['leave', 'Field', null],
|
||||||
|
['leave', 'SelectionSet', null],
|
||||||
|
['leave', 'OperationDefinition', null],
|
||||||
|
['leave', 'Document', null]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe: visitWithTypeInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it maintains type info during visit
|
||||||
|
*/
|
||||||
|
public function testMaintainsTypeInfoDuringVisit()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
|
||||||
|
$typeInfo = new TypeInfo(TestCase::getDefaultSchema());
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ human(id: 4) { name, pets { name }, unknown } }');
|
||||||
|
Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [
|
||||||
|
'enter' => function ($node) use ($typeInfo, &$visited) {
|
||||||
|
$parentType = $typeInfo->getParentType();
|
||||||
|
$type = $typeInfo->getType();
|
||||||
|
$inputType = $typeInfo->getInputType();
|
||||||
|
$visited[] = [
|
||||||
|
'enter',
|
||||||
|
$node->kind,
|
||||||
|
$node->kind === 'Name' ? $node->value : null,
|
||||||
|
$parentType ? (string)$parentType : null,
|
||||||
|
$type ? (string)$type : null,
|
||||||
|
$inputType ? (string)$inputType : null
|
||||||
|
];
|
||||||
|
},
|
||||||
|
'leave' => function ($node) use ($typeInfo, &$visited) {
|
||||||
|
$parentType = $typeInfo->getParentType();
|
||||||
|
$type = $typeInfo->getType();
|
||||||
|
$inputType = $typeInfo->getInputType();
|
||||||
|
$visited[] = [
|
||||||
|
'leave',
|
||||||
|
$node->kind,
|
||||||
|
$node->kind === 'Name' ? $node->value : null,
|
||||||
|
$parentType ? (string)$parentType : null,
|
||||||
|
$type ? (string)$type : null,
|
||||||
|
$inputType ? (string)$inputType : null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
['enter', 'Document', null, null, null, null],
|
||||||
|
['enter', 'OperationDefinition', null, null, 'QueryRoot', null],
|
||||||
|
['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
|
||||||
|
['enter', 'Field', null, 'QueryRoot', 'Human', null],
|
||||||
|
['enter', 'Name', 'human', 'QueryRoot', 'Human', null],
|
||||||
|
['leave', 'Name', 'human', 'QueryRoot', 'Human', null],
|
||||||
|
['enter', 'Argument', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'IntValue', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'IntValue', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'Argument', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'SelectionSet', null, 'Human', 'Human', null],
|
||||||
|
['enter', 'Field', null, 'Human', 'String', null],
|
||||||
|
['enter', 'Name', 'name', 'Human', 'String', null],
|
||||||
|
['leave', 'Name', 'name', 'Human', 'String', null],
|
||||||
|
['leave', 'Field', null, 'Human', 'String', null],
|
||||||
|
['enter', 'Field', null, 'Human', '[Pet]', null],
|
||||||
|
['enter', 'Name', 'pets', 'Human', '[Pet]', null],
|
||||||
|
['leave', 'Name', 'pets', 'Human', '[Pet]', null],
|
||||||
|
['enter', 'SelectionSet', null, 'Pet', '[Pet]', null],
|
||||||
|
['enter', 'Field', null, 'Pet', 'String', null],
|
||||||
|
['enter', 'Name', 'name', 'Pet', 'String', null],
|
||||||
|
['leave', 'Name', 'name', 'Pet', 'String', null],
|
||||||
|
['leave', 'Field', null, 'Pet', 'String', null],
|
||||||
|
['leave', 'SelectionSet', null, 'Pet', '[Pet]', null],
|
||||||
|
['leave', 'Field', null, 'Human', '[Pet]', null],
|
||||||
|
['enter', 'Field', null, 'Human', null, null],
|
||||||
|
['enter', 'Name', 'unknown', 'Human', null, null],
|
||||||
|
['leave', 'Name', 'unknown', 'Human', null, null],
|
||||||
|
['leave', 'Field', null, 'Human', null, null],
|
||||||
|
['leave', 'SelectionSet', null, 'Human', 'Human', null],
|
||||||
|
['leave', 'Field', null, 'QueryRoot', 'Human', null],
|
||||||
|
['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
|
||||||
|
['leave', 'OperationDefinition', null, null, 'QueryRoot', null],
|
||||||
|
['leave', 'Document', null, null, null, null]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it maintains type info during edit
|
||||||
|
*/
|
||||||
|
public function testMaintainsTypeInfoDuringEdit()
|
||||||
|
{
|
||||||
|
$visited = [];
|
||||||
|
$typeInfo = new TypeInfo(TestCase::getDefaultSchema());
|
||||||
|
|
||||||
|
$ast = Parser::parse(
|
||||||
|
'{ human(id: 4) { name, pets }, alien }'
|
||||||
|
);
|
||||||
|
$editedAst = Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [
|
||||||
|
'enter' => function ($node) use ($typeInfo, &$visited) {
|
||||||
|
$parentType = $typeInfo->getParentType();
|
||||||
|
$type = $typeInfo->getType();
|
||||||
|
$inputType = $typeInfo->getInputType();
|
||||||
|
$visited[] = [
|
||||||
|
'enter',
|
||||||
|
$node->kind,
|
||||||
|
$node->kind === 'Name' ? $node->value : null,
|
||||||
|
$parentType ? (string)$parentType : null,
|
||||||
|
$type ? (string)$type : null,
|
||||||
|
$inputType ? (string)$inputType : null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Make a query valid by adding missing selection sets.
|
||||||
|
if (
|
||||||
|
$node->kind === 'Field' &&
|
||||||
|
!$node->selectionSet &&
|
||||||
|
Type::isCompositeType(Type::getNamedType($type))
|
||||||
|
) {
|
||||||
|
return new Field([
|
||||||
|
'alias' => $node->alias,
|
||||||
|
'name' => $node->name,
|
||||||
|
'arguments' => $node->arguments,
|
||||||
|
'directives' => $node->directives,
|
||||||
|
'selectionSet' => new SelectionSet([
|
||||||
|
'kind' => 'SelectionSet',
|
||||||
|
'selections' => [
|
||||||
|
new Field([
|
||||||
|
'name' => new Name(['value' => '__typename'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'leave' => function ($node) use ($typeInfo, &$visited) {
|
||||||
|
$parentType = $typeInfo->getParentType();
|
||||||
|
$type = $typeInfo->getType();
|
||||||
|
$inputType = $typeInfo->getInputType();
|
||||||
|
$visited[] = [
|
||||||
|
'leave',
|
||||||
|
$node->kind,
|
||||||
|
$node->kind === 'Name' ? $node->value : null,
|
||||||
|
$parentType ? (string)$parentType : null,
|
||||||
|
$type ? (string)$type : null,
|
||||||
|
$inputType ? (string)$inputType : null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(Printer::doPrint(Parser::parse(
|
||||||
|
'{ human(id: 4) { name, pets }, alien }'
|
||||||
|
)), Printer::doPrint($ast));
|
||||||
|
|
||||||
|
$this->assertEquals(Printer::doPrint(Parser::parse(
|
||||||
|
'{ human(id: 4) { name, pets { __typename } }, alien { __typename } }'
|
||||||
|
)), Printer::doPrint($editedAst));
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
['enter', 'Document', null, null, null, null],
|
||||||
|
['enter', 'OperationDefinition', null, null, 'QueryRoot', null],
|
||||||
|
['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
|
||||||
|
['enter', 'Field', null, 'QueryRoot', 'Human', null],
|
||||||
|
['enter', 'Name', 'human', 'QueryRoot', 'Human', null],
|
||||||
|
['leave', 'Name', 'human', 'QueryRoot', 'Human', null],
|
||||||
|
['enter', 'Argument', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'IntValue', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'IntValue', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['leave', 'Argument', null, 'QueryRoot', 'Human', 'ID'],
|
||||||
|
['enter', 'SelectionSet', null, 'Human', 'Human', null],
|
||||||
|
['enter', 'Field', null, 'Human', 'String', null],
|
||||||
|
['enter', 'Name', 'name', 'Human', 'String', null],
|
||||||
|
['leave', 'Name', 'name', 'Human', 'String', null],
|
||||||
|
['leave', 'Field', null, 'Human', 'String', null],
|
||||||
|
['enter', 'Field', null, 'Human', '[Pet]', null],
|
||||||
|
['enter', 'Name', 'pets', 'Human', '[Pet]', null],
|
||||||
|
['leave', 'Name', 'pets', 'Human', '[Pet]', null],
|
||||||
|
['enter', 'SelectionSet', null, 'Pet', '[Pet]', null],
|
||||||
|
['enter', 'Field', null, 'Pet', 'String!', null],
|
||||||
|
['enter', 'Name', '__typename', 'Pet', 'String!', null],
|
||||||
|
['leave', 'Name', '__typename', 'Pet', 'String!', null],
|
||||||
|
['leave', 'Field', null, 'Pet', 'String!', null],
|
||||||
|
['leave', 'SelectionSet', null, 'Pet', '[Pet]', null],
|
||||||
|
['leave', 'Field', null, 'Human', '[Pet]', null],
|
||||||
|
['leave', 'SelectionSet', null, 'Human', 'Human', null],
|
||||||
|
['leave', 'Field', null, 'QueryRoot', 'Human', null],
|
||||||
|
['enter', 'Field', null, 'QueryRoot', 'Alien', null],
|
||||||
|
['enter', 'Name', 'alien', 'QueryRoot', 'Alien', null],
|
||||||
|
['leave', 'Name', 'alien', 'QueryRoot', 'Alien', null],
|
||||||
|
['enter', 'SelectionSet', null, 'Alien', 'Alien', null],
|
||||||
|
['enter', 'Field', null, 'Alien', 'String!', null],
|
||||||
|
['enter', 'Name', '__typename', 'Alien', 'String!', null],
|
||||||
|
['leave', 'Name', '__typename', 'Alien', 'String!', null],
|
||||||
|
['leave', 'Field', null, 'Alien', 'String!', null],
|
||||||
|
['leave', 'SelectionSet', null, 'Alien', 'Alien', null],
|
||||||
|
['leave', 'Field', null, 'QueryRoot', 'Alien', null],
|
||||||
|
['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null],
|
||||||
|
['leave', 'OperationDefinition', null, null, 'QueryRoot', null],
|
||||||
|
['leave', 'Document', null, null, null, null]
|
||||||
|
], $visited);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,10 @@ use GraphQL\Validator\DocumentValidator;
|
|||||||
|
|
||||||
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public $humanType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Schema
|
* @return Schema
|
||||||
*/
|
*/
|
||||||
protected function getDefaultSchema()
|
public static function getDefaultSchema()
|
||||||
{
|
{
|
||||||
$FurColor = null;
|
$FurColor = null;
|
||||||
|
|
||||||
@ -124,7 +122,8 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$Human = $this->humanType = new ObjectType([
|
$Human = null;
|
||||||
|
$Human = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'isTypeOf' => function() {return true;},
|
'isTypeOf' => function() {return true;},
|
||||||
'interfaces' => [$Being, $Intelligent],
|
'interfaces' => [$Being, $Intelligent],
|
||||||
@ -134,7 +133,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
'args' => ['surname' => ['type' => Type::boolean()]]
|
'args' => ['surname' => ['type' => Type::boolean()]]
|
||||||
],
|
],
|
||||||
'pets' => ['type' => Type::listOf($Pet)],
|
'pets' => ['type' => Type::listOf($Pet)],
|
||||||
'relatives' => ['type' => function() {return Type::listOf($this->humanType); }],
|
'relatives' => ['type' => function() use (&$Human) {return Type::listOf($Human); }],
|
||||||
'iq' => ['type' => Type::int()]
|
'iq' => ['type' => Type::int()]
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user